api-machine provides a declarative authentication system with cascading support across server, router, and endpoint levels. Authentication schemes automatically integrate with OpenAPI/Swagger documentation.
import { RestServer, BaseApiRouter, BaseApiEndpoint } from 'api-machine';
import { BearerAuthenticationScheme } from 'api-machine';
class SecureEndpoint extends BaseApiEndpoint {
override path = '/data';
async handle(request, response) {
return { message: 'Secure data' };
}
}
class ApiRouter extends BaseApiRouter {
override path = '/api';
override authentication = new BearerAuthenticationScheme({
checkToken: async (token: string) => token === 'secret-token',
schemeName: 'BearerAuth',
});
async routes() {
return [SecureEndpoint];
}
}
const server = new RestServer({
port: 4000,
authentication: new BearerAuthenticationScheme({
checkToken: async (token: string) => token === 'server-token',
}),
});Authentication follows a priority hierarchy:
Endpoint → Router → Server
- Endpoint-level authentication has highest priority
- Router-level authentication applies if endpoint doesn't specify
- Server-level authentication applies as the default fallback
- Set
authentication = nullto make a route explicitly public
class PublicRouter extends BaseApiRouter {
override path = '/public';
override authentication = null; // Bypass server auth
async routes() {
return [PublicEndpoint];
}
}
class AdminEndpoint extends BaseApiEndpoint {
override path = '/admin';
override authentication = new BearerAuthenticationScheme({
checkToken: async (token) => token === 'admin-token',
}); // Override router auth
async handle() {
return { admin: true };
}
}Validates Bearer tokens from the Authorization header.
import { BearerAuthenticationScheme } from 'api-machine';
const auth = new BearerAuthenticationScheme({
checkToken: async (token: string) => {
// Validate token (check database, JWT, etc.)
return token === 'valid-token';
},
schemeName: 'BearerAuth', // Optional: OpenAPI scheme name
bearerFormat: 'JWT', // Optional: Token format
description: 'JWT Bearer Auth', // Optional: OpenAPI description
});Features:
- Automatically validates
Authorization: Bearer <token>header format - Returns 401 Unauthorized if token missing or invalid
- Integrates with OpenAPI security schemes
- Sets
request.authenticated = trueon successful validation
Apply authentication to all endpoints by default:
const server = new RestServer({
port: 4000,
authentication: new BearerAuthenticationScheme({
checkToken: async (token) => await validateToken(token),
}),
});All endpoints will require authentication unless a router or endpoint overrides it with authentication = null.
Apply authentication to all endpoints in a router:
class SecureRouter extends BaseApiRouter {
override path = '/secure';
override authentication = new BearerAuthenticationScheme({
checkToken: async (token) => token === 'router-token',
});
async routes() {
return [Endpoint1, Endpoint2]; // Both require auth
}
}Override parent authentication for specific endpoints:
class AdminEndpoint extends BaseApiEndpoint {
override path = '/admin';
override authentication = new BearerAuthenticationScheme({
checkToken: async (token) => token === 'admin-token',
schemeName: 'AdminAuth',
});
async handle() {
return { admin: true };
}
}Make routes explicitly public by setting authentication = null:
class PublicEndpoint extends BaseApiEndpoint {
override path = '/public';
override authentication = null; // No authentication required
async handle() {
return { public: true };
}
}This bypasses any parent router or server authentication.
Create custom authentication by extending AuthenticationScheme:
import { AuthenticationScheme } from 'api-machine';
import { RequestHandler } from 'express';
class ApiKeyAuthenticationScheme extends AuthenticationScheme {
constructor(private apiKey: string) {
super();
}
getSecurityScheme() {
return {
type: 'apiKey' as const,
in: 'header' as const,
name: 'X-API-Key',
};
}
getMiddleware(): RequestHandler {
return async (req, res, next) => {
const apiKey = req.headers['x-api-key'];
if (apiKey !== this.apiKey) {
throw new UnauthorizedError('Invalid API key');
}
next();
};
}
}Required Methods:
getSecurityScheme(): Returns OpenAPI SecuritySchemeObjectgetMiddleware(): Returns Express middleware functiongetSecurityRequirement(): Optional, returns OpenAPI SecurityRequirementObject
Authentication schemes automatically generate OpenAPI security definitions:
const auth = new BearerAuthenticationScheme({
checkToken: async (token) => await validate(token),
schemeName: 'BearerAuth',
description: 'JWT Bearer Authentication',
});- Use server-level auth as default - Apply authentication at the server level and override only where needed
- Make public routes explicit - Use
authentication = nullto clearly mark public endpoints - Validate tokens properly - Always validate tokens against a secure source (database, JWT verification, etc.)
- Use descriptive scheme names - Help API consumers understand authentication requirements
- Leverage cascading - Set auth at the appropriate level (server/router/endpoint) based on your needs
See examples/authentication-example.ts for a complete working example.