learning_ai_common_plat/packages/fastify-auth/src/request-context.ts
saravanakumardb1 f61a1f0b04 feat(fastify-auth): create @bytelyst/fastify-auth package with JWT auth + request context
- createAuthMiddleware(): RS256 JWKS + HS256 fallback (parameterized)
- createRequestContext(): productId validation + getUserId()
- Fastify request type augmentation for jwtPayload
- 15 tests passing
2026-03-20 07:30:53 -07:00

48 lines
1.6 KiB
TypeScript

/**
* Configurable request context helpers for Fastify product backends.
*
* Factory function creates getRequestProductId() and getUserId() bound to
* the provided product ID, eliminating hardcoded product IDs in each repo.
*/
import type { FastifyRequest } from 'fastify';
import { BadRequestError } from '@bytelyst/errors';
import type { RequestContextOptions } from './types.js';
export function createRequestContext(opts: RequestContextOptions) {
const { productId } = opts;
/**
* Extract productId from request. Validates against this backend's product ID.
* Falls back to the configured productId since this is a product-specific backend.
*/
function getRequestProductId(req: FastifyRequest): string {
// 1. From JWT
const jwtPid = req.jwtPayload?.productId;
if (jwtPid && jwtPid !== productId) {
throw new BadRequestError(`Invalid productId: expected ${productId}, got ${jwtPid}`);
}
// 2. From header
const header = req.headers['x-product-id'];
if (typeof header === 'string' && header.length > 0 && header !== productId) {
throw new BadRequestError(`Invalid productId: expected ${productId}, got ${header}`);
}
return productId;
}
/**
* Extract userId from the JWT payload on the request.
* Throws BadRequestError if no authenticated user is found.
*/
function getUserId(req: FastifyRequest): string {
const sub = req.jwtPayload?.sub;
if (!sub) {
throw new BadRequestError('Missing userId — request must be authenticated');
}
return sub;
}
return { getRequestProductId, getUserId };
}