yikart/AiToEarn · warning · BadRequestException
授权参数不完整
Error message
授权参数不完整
What it means
The OAuth callback endpoint GET /youtube/auth/callback requires both 'code' (Google's authorization code) and 'state' (the state payload) query parameters. If either is missing or empty, a BadRequestException (HTTP 400) '授权参数不完整' (incomplete authorization parameters) is thrown.
Source
Thrown at project/aitoearn-electron/server/src/modules/plat/youtube/youtube.controller.ts:79
// // throw new BadRequestException('邮箱参数不能为空');
// // }
return this.youtubeAuthService.getAuthorizationUrl(mail, systemToken.id);
}
@ApiOperation({ summary: '处理YouTube授权回调' })
@Public()
@Get('auth/callback')
async handleAuthCallback(
// @GetToken() systemToken: TokenInfo,
@Query('code') code: string,
@Query('state') state: string,
// @Query('userId') userId: string,
@Res() res: Response
) {
if (!code || !state) {
throw new BadRequestException('授权参数不完整');
}
// 解析state参数以获取token
let stateData;
try {
stateData = JSON.parse(decodeURIComponent(state));
} catch (error) {
throw new BadRequestException('无效的state参数');
}
const { originalState, userId, email } = stateData;
// 现在您可以使用token变量
console.log('Retrieved userId and originalState:', userId, originalState, email);
try {
const results = await this.youtubeAuthService.handleAuthorizationCode(code, originalState, userId);
// 重定向到前端页面,带上token
// return res.redirect(`/auth/success?token=${token}`);View on GitHub (pinned to d3aa8bea5b)
Solutions
- Check whether the callback URL contains error=access_denied — the user denied consent; restart the flow and require approval
- Ensure the auth URL was generated with both state and the correct scope so Google returns code and state
- Verify the redirect URI registered in Google Cloud Console exactly matches the one used to build the auth URL
- Handle the user-denied case gracefully instead of letting it surface as 400
Example fix
// before
const res = await fetch(`/api/plat/youtube/auth/callback`);
// after
const res = await fetch(`/api/plat/youtube/auth/callback?code=${code}&state=${encodeURIComponent(state)}`); Defensive patterns
Strategy: validation
Validate before calling
const url = new URL(callbackRequestUrl, base);
if (!url.searchParams.get('code') || !url.searchParams.get('state')) {
const err = url.searchParams.get('error');
throw new Error(`Callback missing code/state${err ? ` (OAuth error: ${err})` : ''}`);
} Type guard
function hasOAuthCallbackParams(q: Record<string, unknown>): q is { code: string; state: string } {
return typeof q.code === 'string' && q.code.length > 0 && typeof q.state === 'string' && q.state.length > 0;
} Try / catch
try {
await api.get('/plat/youtube/auth/callback', { params: { code, state } });
} catch (e) {
if (e.response?.status === 400 && String(e.response.data?.message).includes('授权参数不完整')) {
return restartOAuthFlow(); // e.g. user denied consent — no code was returned
}
throw e;
} Prevention
- Include state in the authorization URL every time
- Detect error=access_denied on the callback URL and handle the denial path explicitly
- Verify the redirect URI matches Google Cloud Console registration exactly
- Never reuse stale callback URLs from bookmarks/history
When it happens
Trigger: Google redirects to the callback without code (user denied consent) or without state (state not included/lost in the auth URL), or a manual/test request to the callback omits either parameter.
Common situations: User clicks 'cancel' on the Google consent screen so Google redirects with error=access_denied and no code; the authorization URL was built without state; redirect URI misconfigured so query params are dropped; user bookmarks/reloads a stale callback URL.
Related errors
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/65399bd948d41b71.
Report an issue: GitHub.