mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 05:55:02 +00:00
55 lines
1.8 KiB
TypeScript
55 lines
1.8 KiB
TypeScript
import {
|
|
CanActivate,
|
|
ExecutionContext,
|
|
Injectable,
|
|
Logger,
|
|
UnauthorizedException,
|
|
} from "@nestjs/common";
|
|
import { timingSafeEqual } from "node:crypto";
|
|
import { Request } from "express";
|
|
|
|
/**
|
|
* Shared-secret guard for endpoints only the payment microservice may call
|
|
* (e.g. /internal/payments/mark-paid). The secret is the same SERVICE_AUTH_TOKEN the
|
|
* payment service enforces on its own internal surface. A forged mark-paid must not be able
|
|
* to confirm a booking without a real payment.
|
|
* TODO: integrate @tria-plc IAM / mTLS as the long-term mechanism.
|
|
*/
|
|
@Injectable()
|
|
export class ServiceAuthGuard implements CanActivate {
|
|
private readonly logger = new Logger(ServiceAuthGuard.name);
|
|
private readonly token = process.env.SERVICE_AUTH_TOKEN ?? "";
|
|
private warned = false;
|
|
|
|
constructor() {
|
|
if (!this.token && process.env.NODE_ENV === "production") {
|
|
throw new Error("SERVICE_AUTH_TOKEN must be set in production");
|
|
}
|
|
}
|
|
|
|
canActivate(context: ExecutionContext): boolean {
|
|
if (!this.token) {
|
|
if (!this.warned) {
|
|
this.logger.warn(
|
|
"SERVICE_AUTH_TOKEN unset — internal endpoints are UNGUARDED (dev only)",
|
|
);
|
|
this.warned = true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
const request = context.switchToHttp().getRequest<Request>();
|
|
const header = request.headers["x-service-token"];
|
|
const bearer = request.headers.authorization?.replace(/^Bearer\s+/i, "");
|
|
const presented =
|
|
(Array.isArray(header) ? header[0] : header) ?? bearer ?? "";
|
|
|
|
const expected = Buffer.from(this.token);
|
|
const actual = Buffer.from(presented);
|
|
const valid =
|
|
expected.length === actual.length && timingSafeEqual(expected, actual);
|
|
if (!valid) throw new UnauthorizedException("Invalid service token");
|
|
return true;
|
|
}
|
|
}
|