import { Controller, Post, Body, UseGuards, Req, Logger, HttpCode, HttpStatus, } from "@nestjs/common"; import { Throttle } from "@nestjs/throttler"; import type { Request as ExpressRequest } from "express"; import { SkipCsrf } from "../../common/decorators/skip-csrf.decorator"; import { LocalAuthService } from "./local-auth.service"; import { LocalAuthEnabledGuard } from "./local-auth.guard"; import { LocalLoginDto } from "./dto/local-login.dto"; import { LocalSetupDto } from "./dto/local-setup.dto"; @Controller("auth/local") @UseGuards(LocalAuthEnabledGuard) export class LocalAuthController { private readonly logger = new Logger(LocalAuthController.name); constructor(private readonly localAuthService: LocalAuthService) {} /** * First-time break-glass user creation. * Requires BREAKGLASS_SETUP_TOKEN from environment. */ @Post("setup") @SkipCsrf() @Throttle({ strict: { limit: 5, ttl: 60000 } }) async setup(@Body() dto: LocalSetupDto, @Req() req: ExpressRequest) { const ipAddress = this.getClientIp(req); const userAgent = req.headers["user-agent"]; this.logger.log(`Break-glass setup attempt from ${ipAddress}`); const result = await this.localAuthService.setup( dto.email, dto.name, dto.password, dto.setupToken, ipAddress, userAgent ); return { user: result.user, session: result.session, }; } /** * Break-glass login with email + password. */ @Post("login") @SkipCsrf() @HttpCode(HttpStatus.OK) @Throttle({ strict: { limit: 10, ttl: 60000 } }) async login(@Body() dto: LocalLoginDto, @Req() req: ExpressRequest) { const ipAddress = this.getClientIp(req); const userAgent = req.headers["user-agent"]; const result = await this.localAuthService.login(dto.email, dto.password, ipAddress, userAgent); return { user: result.user, session: result.session, }; } private getClientIp(req: ExpressRequest): string { const forwardedFor = req.headers["x-forwarded-for"]; if (forwardedFor) { const ips = Array.isArray(forwardedFor) ? forwardedFor[0] : forwardedFor; return ips?.split(",")[0]?.trim() ?? "unknown"; } return req.ip ?? req.socket.remoteAddress ?? "unknown"; } }