Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
82 lines
2.2 KiB
TypeScript
82 lines
2.2 KiB
TypeScript
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";
|
|
}
|
|
}
|