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

  1. Check whether the callback URL contains error=access_denied — the user denied consent; restart the flow and require approval
  2. Ensure the auth URL was generated with both state and the correct scope so Google returns code and state
  3. Verify the redirect URI registered in Google Cloud Console exactly matches the one used to build the auth URL
  4. 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

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.