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
- 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
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
- 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
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.