How AiToEarn Handles User Authentication and Authorization: JWT and OAuth 2.0 Architecture
AiToEarn implements a two-layer security architecture using JWT-based user authentication for API access and OAuth 2.0 for platform authorization, with guards, decorators, and interceptors handling token validation across the NestJS backend.
The yikart/AiToEarn repository provides a comprehensive example of modern authentication patterns in a full-stack TypeScript application. This guide examines how the backend validates user sessions through JSON Web Tokens while managing third-party platform credentials through OAuth 2.0 flows.
JWT-Based User Authentication
The backend API relies on stateless JWT authentication to identify users across requests. After successful credential validation, the system issues signed tokens that subsequent requests must present in the Authorization header.
Token Generation and Validation
The authentication flow begins at the /login endpoint. The AitoearnAuthService class provides the generateToken() method to create signed JWTs following successful credential verification.
// project/aitoearn-backend/apps/aitoearn-server/src/core/user/login.controller.ts
@Post('login')
async login(@Body() dto: LoginDto) {
const userInfo = await this.authService.validateUser(dto);
const token = this.authService.generateToken(userInfo); // → JWT
const tokenInfo = this.authService.decodeToken(token); // → { userId, role, … }
return { token, tokenInfo };
}
Guarding Routes with AitoearnAuthGuard
Protected endpoints use the AitoearnAuthGuard located in project/aitoearn-backend/libs/aitoearn-auth/src/aitoearn-auth.guard.ts. This guard extracts the Bearer token from the Authorization header, verifies its signature, and throws UnauthorizedException for missing, malformed, or expired tokens.
// project/aitoearn-backend/apps/aitoearn-server/src/core/user/user.controller.ts
@Get('profile')
@UseGuards(AitoearnAuthGuard) // ← checks JWT
async getProfile(@GetToken() token: TokenInfo) {
// token.userId is guaranteed to be a valid, authenticated user
return this.userService.getProfile(token.userId);
}
Accessing Token Data with Decorators
The @GetToken() custom decorator, defined in project/aitoearn-backend/libs/aitoearn-auth/src/aitoearn-auth.decorator.ts, injects the decoded token payload (TokenInfo) directly into controller method parameters. This gives handlers immediate access to userId, role, and other claims without manual parsing.
OAuth 2.0 Platform Authorization
Beyond user identity, AiToEarn manages authorization for external content platforms like XHS, Douyin, and LinkedIn through OAuth 2.0 credential exchange.
Credential Storage Schema
Platform tokens persist in MongoDB according to the schema defined in project/aitoearn-backend/libs/mongodb/src/schemas/oauth2-credential.schema.ts. The document structure stores accessToken, refreshToken, expiresAt, and the associated userId, enabling secure automated API calls on behalf of users.
Frontend OAuth Flow Implementation
The Channel Manager frontend component orchestrates the OAuth handshake. The process uses a temporary task ID pattern: the frontend requests an authorization URL, opens a popup, and polls for completion.
// project/aitoearn-web/src/components/ChannelManager/channelManagerStore.ts
async startAuth(platform: string) {
const { authData } = await getAuthUrl(platform); // → { taskId, url }
this.authState = { taskId: authData.taskId, authUrl: authData.url };
const popup = window.open(authData.url);
this.pollAuthStatus();
}
Once the user authenticates with the external platform, the backend stores the credential via OAuth2CredentialRepository. The frontend detects completion through polling:
private async pollAuthStatus() {
const result = await checkAuthStatus(this.authState.platform, this.authState.taskId);
if (result.success) {
this.authState = initialAuthState; // clear state
// credential now stored on the backend; UI can reflect the linked account
}
}
Request Context Propagation
To make authenticated user data available throughout the request lifecycle, AiToEarn uses the RequestContextInterceptor in project/aitoearn-backend/libs/common/src/interceptors/request-context.interceptor.ts. This interceptor attaches the user context to the request object, allowing downstream services to access authentication state without explicit parameter passing.
Summary
- JWT Validation: The
AitoearnAuthGuardvalidates Bearer tokens on every protected request, throwingUnauthorizedExceptionfor invalid sessions. - Token Injection: The
@GetToken()decorator provides controller methods immediate access to decoded JWT payloads containinguserIdand role information. - OAuth Management: Platform credentials store in MongoDB with expiration tracking, while the frontend Channel Manager handles OAuth flows via task ID polling.
- Context Availability:
RequestContextInterceptorensures authenticated user data propagates through the entire request chain.
Frequently Asked Questions
How does AiToEarn validate incoming JWT tokens?
The AitoearnAuthGuard extracts the token from the Authorization header, verifies its cryptographic signature, and validates the payload structure. If the token is missing, malformed, or expired, the guard immediately throws an UnauthorizedException, preventing access to protected endpoints.
Where does AiToEarn store OAuth 2.0 tokens for connected platforms?
Platform credentials persist in MongoDB collections defined by oauth2-credential.schema.ts within the libs/mongodb library. Each document contains the accessToken, refreshToken, expiresAt timestamp, and the userId to associate the credential with the correct account.
How does the frontend know when OAuth authorization completes?
The Channel Manager store utilizes a polling mechanism. After opening the OAuth provider URL in a popup, it repeatedly calls checkAuthStatus(taskId) until the backend confirms successful token exchange. The taskId uniquely identifies the pending authorization session during this asynchronous handshake.
Can controllers access user data outside of the JWT decorator?
Yes. The RequestContextInterceptor attaches the authenticated user to the request object, making user information available to any downstream service or interceptor in the request pipeline. This provides an alternative to method parameter decorators for scenarios requiring implicit context access.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →