{"record":{"id":"2856ce0c41ded918","repo":"yikart/AiToEarn","slug":"userid","errorCode":null,"errorMessage":"userId是必需的","messagePattern":"userId是必需的","errorType":"validation","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.auth.service.ts","lineNumber":112,"sourceCode":"    const codeVerifier = crypto.randomBytes(32).toString('base64url');\n\n    // 生成码挑战\n    const codeChallenge = crypto\n      .createHash('sha256')\n      .update(codeVerifier)\n      .digest('base64url');\n\n    return { codeVerifier, codeChallenge };\n  }\n\n  /**\n   * 获取TikTok授权URL\n   * @param mail 用户邮箱\n   * @returns 包含授权URL的对象\n   */\n  async getAuthorizationUrl(userId: string, mail: string): Promise<object> {\n    if (!userId) {\n      throw new BadRequestException('userId是必需的');\n    }\n\n    // 生成状态参数以防止CSRF攻击\n    const state = this.generateState();\n\n    // 生成PKCE的code_verifier和code_challenge\n    const { codeVerifier, codeChallenge } = this.generatePKCE();\n\n    const stateData = {\n      originalState: state, // 保留原始state值\n      userId: userId,      // 用户ID\n      email: mail,         // 邮箱\n      codeVerifier: codeVerifier      // 保存code_verifier用于后续交换token\n    };\n\n    // 将状态与用户数据关联并存储在Redis中 (10分钟有效期)\n    await this.redisService.setKey(`tiktok:state:${state}`, JSON.stringify(stateData), 600);\n","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.auth.service.ts#L94-L130","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Ensure the caller is authenticated and the controller extracts userId from the JWT/auth context, not the raw body","Validate userId presence at the controller/DTO layer with class-validator (@IsNotEmpty()) so the 400 fires with a clear validation message","Fix the client to always pass the logged-in user's id when requesting the authorize URL","If userId can legitimately be resolved from mail, look it up instead of requiring it as a param"],"exampleFix":"// before\nasync getAuthorizationUrl(userId: string, mail: string): Promise<object> {\n  if (!userId) {\n    throw new BadRequestException('userId是必需的');\n  }\n// after\nasync getAuthorizationUrl(@User() userId: string, mail: string): Promise<object> {\n  if (!userId) {\n    throw new UnauthorizedException('User must be logged in to authorize TikTok');\n  }","handlingStrategy":"validation","validationCode":"const userId = req.user?.id;\nif (!userId) {\n  return res.status(401).json({ error: 'Login required before authorizing TikTok' });\n}","typeGuard":"function hasUserId(u: unknown): u is { id: string } {\n  return !!u && typeof u === 'object' && typeof (u as any).id === 'string' && (u as any).id.length > 0;\n}","tryCatchPattern":"try {\n  return await tiktokAuthService.getAuthorizationUrl(userId, mail);\n} catch (err) {\n  if (err instanceof BadRequestException && err.message.includes('userId')) {\n    return res.status(401).json({ error: 'Authenticate first' });\n  }\n  throw err;\n}","preventionTips":["Take userId from the authenticated user (JWT), never from client body","Validate with class-validator DTOs (@IsUUID/@IsNotEmpty) at the controller","Guard client-side: only show the TikTok connect button when logged in","Don't derive userId from mail without verifying it exists"],"tags":["tiktok","oauth","validation","bad-request"],"backgroundTag":"missing-required-parameter","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}