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.

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 argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: 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)