yikart/AiToEarn · warning · BadRequestException

userId是必需的

Error message

userId是必需的

What it means

getAuthorizationUrl builds the TikTok OAuth authorization URL and requires a userId to bind the OAuth state to a user. If userId is falsy (undefined, null, empty string) it throws BadRequestException('userId是必需的') ('userId is required') with HTTP 400 before any TikTok call is made.

Source

Thrown at project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.auth.service.ts:112

    const codeVerifier = crypto.randomBytes(32).toString('base64url');

    // 生成码挑战
    const codeChallenge = crypto
      .createHash('sha256')
      .update(codeVerifier)
      .digest('base64url');

    return { codeVerifier, codeChallenge };
  }

  /**
   * 获取TikTok授权URL
   * @param mail 用户邮箱
   * @returns 包含授权URL的对象
   */
  async getAuthorizationUrl(userId: string, mail: string): Promise<object> {
    if (!userId) {
      throw new BadRequestException('userId是必需的');
    }

    // 生成状态参数以防止CSRF攻击
    const state = this.generateState();

    // 生成PKCE的code_verifier和code_challenge
    const { codeVerifier, codeChallenge } = this.generatePKCE();

    const stateData = {
      originalState: state, // 保留原始state值
      userId: userId,      // 用户ID
      email: mail,         // 邮箱
      codeVerifier: codeVerifier      // 保存code_verifier用于后续交换token
    };

    // 将状态与用户数据关联并存储在Redis中 (10分钟有效期)
    await this.redisService.setKey(`tiktok:state:${state}`, JSON.stringify(stateData), 600);

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Ensure the caller is authenticated and the controller extracts userId from the JWT/auth context, not the raw body
  2. Validate userId presence at the controller/DTO layer with class-validator (@IsNotEmpty()) so the 400 fires with a clear validation message
  3. Fix the client to always pass the logged-in user's id when requesting the authorize URL
  4. If userId can legitimately be resolved from mail, look it up instead of requiring it as a param

Example fix

// before
async getAuthorizationUrl(userId: string, mail: string): Promise<object> {
  if (!userId) {
    throw new BadRequestException('userId是必需的');
  }
// after
async getAuthorizationUrl(@User() userId: string, mail: string): Promise<object> {
  if (!userId) {
    throw new UnauthorizedException('User must be logged in to authorize TikTok');
  }
Defensive patterns

Strategy: validation

Validate before calling

const userId = req.user?.id;
if (!userId) {
  return res.status(401).json({ error: 'Login required before authorizing TikTok' });
}

Type guard

function hasUserId(u: unknown): u is { id: string } {
  return !!u && typeof u === 'object' && typeof (u as any).id === 'string' && (u as any).id.length > 0;
}

Try / catch

try {
  return await tiktokAuthService.getAuthorizationUrl(userId, mail);
} catch (err) {
  if (err instanceof BadRequestException && err.message.includes('userId')) {
    return res.status(401).json({ error: 'Authenticate first' });
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling getAuthorizationUrl without a userId argument, or with an empty/undefined userId — typically when the controller reads userId from a request body/query/auth token that was not populated (missing JWT payload field, unauthenticated request, client omitted the field).

Common situations: Client calls the endpoint before login so req.user is undefined; API consumer forgets userId in the POST body; refactor renamed the auth decorator so userId no longer extracted; passing mail but omitting userId.

Understand the failure class

Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.

Related errors


AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31). Data as JSON: /api/errors/2856ce0c41ded918. Report an issue: GitHub.