@noony-serverless/core
Version:
A Middy base framework compatible with Firebase and GCP Cloud Functions with TypeScript
433 lines • 12.7 kB
TypeScript
import { BaseMiddleware } from '../core/handler';
import { Context } from '../core/core';
/**
* Interface for custom token verification implementations.
* Allows integration with various authentication providers (JWT, OAuth, custom tokens).
*
* @template T - The type of user data returned after successful token verification
*
* @example
* JWT token verification:
* ```typescript
* import jwt from 'jsonwebtoken';
* import { CustomTokenVerificationPort } from '@noony-serverless/core';
*
* interface User {
* id: string;
* email: string;
* roles: string[];
* }
*
* class JWTVerificationPort implements CustomTokenVerificationPort<User> {
* constructor(private secret: string) {}
*
* async verifyToken(token: string): Promise<User> {
* try {
* const payload = jwt.verify(token, this.secret) as any;
* return {
* id: payload.sub,
* email: payload.email,
* roles: payload.roles || []
* };
* } catch (error) {
* throw new Error('Invalid token');
* }
* }
* }
* ```
*
* @example
* Custom API token verification:
* ```typescript
* class APIKeyVerificationPort implements CustomTokenVerificationPort<{ apiKey: string; permissions: string[] }> {
* async verifyToken(token: string): Promise<{ apiKey: string; permissions: string[] }> {
* const apiKey = await this.validateAPIKey(token);
* if (!apiKey) {
* throw new Error('Invalid API key');
* }
* return {
* apiKey: token,
* permissions: apiKey.permissions
* };
* }
*
* private async validateAPIKey(key: string) {
* // Validate against database or external service
* return { permissions: ['read', 'write'] };
* }
* }
* ```
*/
export interface CustomTokenVerificationPort<T> {
verifyToken(token: string): Promise<T>;
}
/**
* Standard JWT payload interface with common claims.
* Extends the payload with custom properties as needed.
*
* @example
* Basic JWT payload usage:
* ```typescript
* import { JWTPayload } from '@noony-serverless/core';
*
* interface CustomJWTPayload extends JWTPayload {
* userId: string;
* email: string;
* roles: string[];
* }
*
* const payload: CustomJWTPayload = {
* sub: 'user-123',
* exp: Math.floor(Date.now() / 1000) + 3600, // 1 hour
* iat: Math.floor(Date.now() / 1000),
* iss: 'my-app',
* aud: 'my-app-users',
* userId: 'user-123',
* email: 'user@example.com',
* roles: ['user', 'admin']
* };
* ```
*
* @example
* Token validation with custom claims:
* ```typescript
* function validateCustomClaims(payload: JWTPayload & { roles?: string[] }) {
* if (!payload.roles || payload.roles.length === 0) {
* throw new Error('User must have at least one role');
* }
*
* if (payload.exp && payload.exp < Date.now() / 1000) {
* throw new Error('Token has expired');
* }
* }
* ```
*/
export interface JWTPayload {
exp?: number;
iat?: number;
nbf?: number;
jti?: string;
iss?: string;
aud?: string | string[];
sub?: string;
[key: string]: unknown;
}
/**
* Configuration options for authentication middleware.
* Provides comprehensive security controls and validation settings.
*
* @example
* Basic authentication options:
* ```typescript
* import { AuthenticationOptions } from '@noony-serverless/core';
*
* const basicOptions: AuthenticationOptions = {
* maxTokenAge: 3600, // 1 hour
* clockTolerance: 60, // 1 minute
* requiredClaims: {
* issuer: 'my-app',
* audience: 'my-app-users'
* }
* };
* ```
*
* @example
* Advanced options with rate limiting and blacklisting:
* ```typescript
* const advancedOptions: AuthenticationOptions = {
* maxTokenAge: 7200, // 2 hours
* clockTolerance: 30,
* rateLimiting: {
* maxAttempts: 5,
* windowMs: 15 * 60 * 1000 // 15 minutes
* },
* isTokenBlacklisted: async (tokenId) => {
* // Check Redis or database for blacklisted tokens
* return await redis.sismember('blacklisted_tokens', tokenId);
* },
* requiredClaims: {
* issuer: 'secure-app',
* audience: ['web-app', 'mobile-app']
* }
* };
* ```
*
* @example
* Production security configuration:
* ```typescript
* const productionOptions: AuthenticationOptions = {
* maxTokenAge: 1800, // 30 minutes - short for security
* clockTolerance: 10, // Tight tolerance
* rateLimiting: {
* maxAttempts: 3, // Strict rate limiting
* windowMs: 30 * 60 * 1000 // 30 minutes lockout
* },
* isTokenBlacklisted: async (tokenId) => {
* const result = await database.query(
* 'SELECT 1 FROM revoked_tokens WHERE jti = ?',
* [tokenId]
* );
* return result.length > 0;
* },
* requiredClaims: {
* issuer: 'production-auth-server',
* audience: 'production-api'
* }
* };
* ```
*/
export interface AuthenticationOptions {
/**
* Maximum token age in seconds (overrides exp claim validation)
*/
maxTokenAge?: number;
/**
* Clock tolerance in seconds for time-based validations
* @default 60
*/
clockTolerance?: number;
/**
* Token blacklist checker function
*/
isTokenBlacklisted?: (tokenId?: string) => Promise<boolean> | boolean;
/**
* Rate limiting per user/IP
*/
rateLimiting?: {
maxAttempts: number;
windowMs: number;
};
/**
* Required token claims
*/
requiredClaims?: {
issuer?: string;
audience?: string | string[];
};
}
/**
* Class-based authentication middleware with comprehensive security features.
* Provides JWT validation, rate limiting, token blacklisting, and security logging.
*
* @template TUser - The type of user data returned by the token verification port
* @template TBody - The type of the request body payload (preserves type chain)
*
* @example
* Basic JWT authentication:
* ```typescript
* import { Handler, AuthenticationMiddleware } from '@noony-serverless/core';
* import jwt from 'jsonwebtoken';
*
* interface User {
* id: string;
* email: string;
* roles: string[];
* }
*
* class JWTVerifier implements CustomTokenVerificationPort<User> {
* async verifyToken(token: string): Promise<User> {
* const payload = jwt.verify(token, process.env.JWT_SECRET!) as any;
* return {
* id: payload.sub,
* email: payload.email,
* roles: payload.roles || []
* };
* }
* }
*
* const protectedHandler = new Handler()
* .use(new AuthenticationMiddleware(new JWTVerifier()))
* .handle(async (request, context) => {
* const user = context.user as User;
* return {
* success: true,
* data: { message: `Hello ${user.email}`, userId: user.id }
* };
* });
* ```
*
* @example
* Advanced authentication with security options:
* ```typescript
* const secureAuthMiddleware = new AuthenticationMiddleware(
* new JWTVerifier(),
* {
* maxTokenAge: 1800, // 30 minutes
* rateLimiting: {
* maxAttempts: 5,
* windowMs: 15 * 60 * 1000 // 15 minutes
* },
* isTokenBlacklisted: async (tokenId) => {
* return await redis.sismember('revoked_tokens', tokenId);
* },
* requiredClaims: {
* issuer: 'my-auth-server',
* audience: 'my-api'
* }
* }
* );
*
* const secureHandler = new Handler()
* .use(secureAuthMiddleware)
* .handle(async (request, context) => {
* // Only authenticated users reach here
* return { success: true, data: 'Secure data' };
* });
* ```
*
* @example
* Google Cloud Functions integration:
* ```typescript
* import { http } from '@google-cloud/functions-framework';
*
* const userProfileHandler = new Handler()
* .use(new AuthenticationMiddleware(new JWTVerifier()))
* .handle(async (request, context) => {
* const user = context.user as User;
* const profile = await getUserProfile(user.id);
* return { success: true, data: profile };
* });
*
* export const getUserProfile = http('getUserProfile', (req, res) => {
* return userProfileHandler.execute(req, res);
* });
* ```
*/
export declare class AuthenticationMiddleware<TUser = unknown, TBody = unknown> implements BaseMiddleware<TBody, TUser> {
private tokenVerificationPort;
private options;
constructor(tokenVerificationPort: CustomTokenVerificationPort<TUser>, options?: AuthenticationOptions);
before(context: Context<TBody, TUser>): Promise<void>;
}
/**
* Factory function that creates an authentication middleware with token verification.
* Provides a functional approach for authentication setup.
*
* @template TUser - The type of user data returned by the token verification port
* @template TBody - The type of the request body payload (preserves type chain)
* @param tokenVerificationPort - The token verification implementation
* @param options - Authentication configuration options
* @returns A BaseMiddleware object with authentication logic
*
* @example
* Simple JWT authentication:
* ```typescript
* import { Handler, verifyAuthTokenMiddleware } from '@noony-serverless/core';
*
* class SimpleJWTVerifier implements CustomTokenVerificationPort<{ userId: string }> {
* async verifyToken(token: string): Promise<{ userId: string }> {
* // Simple token verification logic
* if (token === 'valid-token') {
* return { userId: 'user-123' };
* }
* throw new Error('Invalid token');
* }
* }
*
* const handler = new Handler()
* .use(verifyAuthTokenMiddleware(new SimpleJWTVerifier()))
* .handle(async (request, context) => {
* const user = context.user as { userId: string };
* return { success: true, userId: user.userId };
* });
* ```
*
* @example
* API key authentication with rate limiting:
* ```typescript
* interface APIKeyUser {
* keyId: string;
* permissions: string[];
* organization: string;
* }
*
* class APIKeyVerifier implements CustomTokenVerificationPort<APIKeyUser> {
* async verifyToken(token: string): Promise<APIKeyUser> {
* const keyData = await this.validateAPIKey(token);
* if (!keyData) {
* throw new Error('Invalid API key');
* }
* return keyData;
* }
*
* private async validateAPIKey(key: string): Promise<APIKeyUser | null> {
* // Database lookup or external validation
* return {
* keyId: 'key-123',
* permissions: ['read', 'write'],
* organization: 'org-456'
* };
* }
* }
*
* const apiHandler = new Handler()
* .use(verifyAuthTokenMiddleware(
* new APIKeyVerifier(),
* {
* rateLimiting: {
* maxAttempts: 100,
* windowMs: 60 * 1000 // 1 minute
* }
* }
* ))
* .handle(async (request, context) => {
* const apiUser = context.user as APIKeyUser;
* return {
* success: true,
* data: { organization: apiUser.organization }
* };
* });
* ```
*
* @example
* Express-style middleware chain:
* ```typescript
* import { Handler, verifyAuthTokenMiddleware, errorHandler } from '@noony-serverless/core';
*
* const authMiddleware = verifyAuthTokenMiddleware(
* new JWTVerifier(),
* {
* maxTokenAge: 3600,
* requiredClaims: {
* issuer: 'my-app',
* audience: 'api-users'
* }
* }
* );
*
* const protectedEndpoint = new Handler()
* .use(authMiddleware)
* .use(errorHandler())
* .handle(async (request, context) => {
* // Authenticated user available in context.user
* return { success: true, data: 'Protected resource' };
* });
* ```
*
* @example
* Multiple authentication strategies:
* ```typescript
* // Different handlers for different auth types
* const jwtHandler = new Handler()
* .use(verifyAuthTokenMiddleware(new JWTVerifier()))
* .handle(jwtLogic);
*
* const apiKeyHandler = new Handler()
* .use(verifyAuthTokenMiddleware(new APIKeyVerifier()))
* .handle(apiKeyLogic);
*
* // Route based on authentication type
* export const handleRequest = (req: any, res: any) => {
* const authHeader = req.headers.authorization;
* if (authHeader?.startsWith('Bearer jwt.')) {
* return jwtHandler.execute(req, res);
* } else if (authHeader?.startsWith('Bearer ak_')) {
* return apiKeyHandler.execute(req, res);
* } else {
* res.status(401).json({ error: 'Authentication required' });
* }
* };
* ```
*/
export declare const verifyAuthTokenMiddleware: <TUser = unknown, TBody = unknown>(tokenVerificationPort: CustomTokenVerificationPort<TUser>, options?: AuthenticationOptions) => BaseMiddleware<TBody, TUser>;
//# sourceMappingURL=authenticationMiddleware.d.ts.map