import { Body, Controller, Get, HttpCode, HttpStatus, Post, Query, Req, UseGuards, } from '@nestjs/common'; import { ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags, } from '@nestjs/swagger'; import { JwtGuard } from '../../common/jwt.guard'; import { OptionalJwtGuard } from './optional-jwt.guard'; import { CompleteVerificationResultDto, StartVerificationDto, VerifaydaCallbackDto, VerificationStatusDto, } from './verifayda.dto'; import { VerifaydaService } from './verifayda.service'; /** Shape the JWT strategy puts on `request.user` (see common/jwt.strategy.ts). */ interface AuthedUser { userId: string; email?: string; role?: string; passengerId?: string; } /** Minimal slices of the Express req we touch (avoids a hard dependency on * `@types/express`, which isn't resolved in this package). */ interface RequestWithOptionalUser { user?: AuthedUser; } interface RequestWithUser { user: AuthedUser; } @ApiTags('Fayda Verification') @Controller('fayda/verification') export class VerifaydaController { constructor(private readonly service: VerifaydaService) {} @Post('start') @HttpCode(HttpStatus.OK) @UseGuards(OptionalJwtGuard) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: 'Start a VeriFayda 2.0 verification session', description: `Creates a verification session and returns the eSignet authorize URL the frontend should send the user to. - Works for **logged-in users** and **guests**. If a valid bearer token is present, the verification is tied to that user. - **VERIFY** (default): the user proves their identity and \`/complete\` returns the verified attributes (name, email, phone, dob, gender). - **LOGIN**: \`/complete\` resolves/creates the user and returns a JWT. - The returned \`authorizationUrl\` already carries the PKCE \`code_challenge\`, CSRF \`state\`, requested \`claims\`, and \`code_challenge_method=S256\`. The frontend simply navigates to it (full page or popup).`, }) @ApiOkResponse({ description: 'Authorize URL the frontend should redirect the user to.', schema: { example: { authorizationUrl: 'https://esignet.example.com/authorize?client_id=...&state=...&code_challenge=...', }, }, }) async start( @Body() dto: StartVerificationDto, @Req() req: RequestWithOptionalUser, ): Promise<{ authorizationUrl: string }> { const authorizationUrl = await this.service.startVerification({ purpose: dto.purpose ?? 'VERIFY', platform: dto.platform ?? 'WEB', userId: req.user?.userId, }); return { authorizationUrl }; } @Get('complete') @ApiOperation({ summary: 'Complete a verification (Fayda redirect / client callback lands here)', description: `This is the registered Fayda \`redirect_uri\`. Fayda redirects the browser here with \`?code&state\``, }) @ApiOkResponse({ type: CompleteVerificationResultDto }) async complete( @Query() dto: VerifaydaCallbackDto, ): Promise { return this.service.completeVerification(dto); } @Get('status') @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: "Get the current user's Fayda verification status", description: 'Returns whether the authenticated user has linked a verified Fayda identity to their account, when, and the name on file.', }) @ApiOkResponse({ type: VerificationStatusDto }) async status( @Req() req: RequestWithUser, ): Promise { return this.service.getVerificationStatus(req.user.userId); } }