Files
stack/apps/gateway/src/federation/enrollment.controller.ts
jason.woltje 0bfaa56e9e
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/publish Pipeline was successful
feat(federation): enrollment controller + single-use token flow (FED-M2-07) (#497)
2026-04-22 04:23:19 +00:00

55 lines
1.7 KiB
TypeScript

/**
* EnrollmentController — federation enrollment HTTP layer (FED-M2-07).
*
* Routes:
* POST /api/federation/enrollment/tokens — admin creates a single-use token
* POST /api/federation/enrollment/:token — unauthenticated; token IS the auth
*/
import {
Body,
Controller,
HttpCode,
HttpStatus,
Inject,
Param,
Post,
UseGuards,
} from '@nestjs/common';
import { AdminGuard } from '../admin/admin.guard.js';
import { EnrollmentService } from './enrollment.service.js';
import { CreateEnrollmentTokenDto, RedeemEnrollmentTokenDto } from './enrollment.dto.js';
@Controller('api/federation/enrollment')
export class EnrollmentController {
constructor(@Inject(EnrollmentService) private readonly enrollmentService: EnrollmentService) {}
/**
* Admin-only: generate a single-use enrollment token for a pending grant.
* The token should be distributed out-of-band to the remote peer operator.
*
* POST /api/federation/enrollment/tokens
*/
@Post('tokens')
@UseGuards(AdminGuard)
@HttpCode(HttpStatus.CREATED)
async createToken(@Body() dto: CreateEnrollmentTokenDto) {
return this.enrollmentService.createToken(dto);
}
/**
* Unauthenticated: remote peer redeems a token by submitting its CSR.
* The token itself is the credential — no session or bearer token required.
*
* POST /api/federation/enrollment/:token
*
* Returns the signed leaf cert and full chain PEM on success.
* Returns 410 Gone if the token was already used or has expired.
*/
@Post(':token')
@HttpCode(HttpStatus.OK)
async redeem(@Param('token') token: string, @Body() dto: RedeemEnrollmentTokenDto) {
return this.enrollmentService.redeem(token, dto.csrPem);
}
}