Files
IdP/apps/api-gateway/src/controllers/advanced-auth.controller.ts
2026-06-29 22:51:25 +03:00

66 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Body, Controller, Get, Headers, Param, Post, Req } from '@nestjs/common';
import type { Request } from 'express';
import { enrichAuthClientMeta } from '../client-request.util';
import { JwtService } from '@nestjs/jwt';
import { ApiBearerAuth, ApiBody, ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger';
import { CoreGrpcService } from '../core-grpc.service';
import { QrSessionDto, WebAuthnDto } from '../dto/identity.dto';
import { resolveAuthorizedPayload } from '../session-auth';
@ApiTags('Биометрия и QR-вход')
@Controller('auth/advanced')
export class AdvancedAuthController {
constructor(
private readonly core: CoreGrpcService,
private readonly jwt: JwtService
) {}
@Post('webauthn/register/challenge')
@ApiOperation({ summary: 'Challenge регистрации WebAuthn', description: 'Создает challenge для регистрации лица/отпечатка. Endpoint готов для подключения настоящего WebAuthn attestation.' })
@ApiBody({ type: WebAuthnDto })
@ApiResponse({ status: 201, description: 'Challenge создан' })
webAuthnRegister(@Body() dto: WebAuthnDto) {
return this.core.advancedAuth.CreateWebAuthnRegistrationChallenge(dto);
}
@Post('webauthn/login/challenge')
@ApiOperation({ summary: 'Challenge входа WebAuthn', description: 'Создает challenge для входа по лицу или отпечатку.' })
@ApiBody({ type: WebAuthnDto })
@ApiResponse({ status: 201, description: 'Challenge создан' })
webAuthnLogin(@Body() dto: WebAuthnDto) {
return this.core.advancedAuth.CreateWebAuthnLoginChallenge(dto);
}
@Post('qr/session')
@ApiOperation({ summary: 'Создать QR-сессию', description: 'Создает временную QR-сессию для входа с другого устройства.' })
@ApiBody({ type: QrSessionDto })
@ApiResponse({ status: 201, description: 'QR-сессия создана' })
createQr(@Body() dto: QrSessionDto, @Req() req: Request) {
return this.core.advancedAuth.CreateQrSession(
enrichAuthClientMeta(req, {
deviceName: dto.deviceName,
fingerprint: dto.fingerprint,
deviceType: dto.deviceType ?? 'WEB'
})
);
}
@Get('qr/session/:sessionId')
@ApiOperation({ summary: 'Проверить QR-сессию', description: 'Возвращает текущий статус QR-сессии: PENDING/APPROVED/EXPIRED.' })
@ApiParam({ name: 'sessionId', description: 'ID QR-сессии' })
@ApiResponse({ status: 200, description: 'Статус QR-сессии получен' })
pollQr(@Param('sessionId') sessionId: string) {
return this.core.advancedAuth.PollQrSession({ sessionId });
}
@Post('qr/session/:sessionId/approve')
@ApiBearerAuth()
@ApiOperation({ summary: 'Подтвердить QR-вход', description: 'Подтверждает QR-сессию с мобильного приложения уже авторизованным пользователем.' })
@ApiParam({ name: 'sessionId', description: 'ID QR-сессии' })
@ApiResponse({ status: 201, description: 'QR-сессия подтверждена' })
async approveQr(@Param('sessionId') sessionId: string, @Headers('authorization') authorization?: string) {
const payload = await resolveAuthorizedPayload(this.jwt, this.core, authorization);
return this.core.advancedAuth.ApproveQrSession({ sessionId, userId: payload.sub });
}
}