UNPKG

@smertins27/jwt-auth-manager

Version:

Modernes JWT-Management mit Access & Refresh Token Rotation

533 lines (523 loc) 25.4 kB
import { Request, Response, NextFunction } from 'express'; import { JWTPayload } from 'jose'; import { RedisClientType } from 'redis'; /** * An object representing HTTP status codes with their corresponding standard values. * This constant provides a set of commonly used HTTP response status codes. * Each key represents the status name, and the value is the numeric HTTP status code. * * Properties: * - `OK`: HTTP status code 200, indicating a successful request. * - `CREATED`: HTTP status code 201, indicating that a resource has been successfully created. * - `BAD_REQUEST`: HTTP status code 400, indicating that the server could not understand the request due to invalid syntax. * - `UNAUTHORIZED`: HTTP status code 401, indicating that authentication is required but has failed or has not been provided. * - `FORBIDDEN`: HTTP status code 403, indicating that the request is understood by the server but it refuses to authorize it. * - `NOT_FOUND`: HTTP status code 404, indicating that the requested resource could not be found on the server. * - `INTERNAL_SERVER_ERROR`: HTTP status code 500, indicating an unexpected condition encountered on the server. */ declare const HTTP_STATUS: { readonly OK: 200; readonly CREATED: 201; readonly BAD_REQUEST: 400; readonly UNAUTHORIZED: 401; readonly FORBIDDEN: 403; readonly NOT_FOUND: 404; readonly INTERNAL_SERVER_ERROR: 500; }; /** * Represents the HTTP methods that can be used in an HTTP request. * * These methods define the type of action to be performed on a specific resource. * * The available HTTP methods are: * - 'GET': Used to retrieve data from a resource without causing any state change. * - 'POST': Used to submit data to create or update a resource. * - 'PUT': Used to update or replace a resource with the provided data. * - 'PATCH': Used to apply partial modifications to a resource. * - 'DELETE': Used to delete a resource. * - 'HEAD': Similar to 'GET', but fetches only the headers without the body. * - 'OPTIONS': Used to describe the communication options for the target resource. */ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS'; /** * Represents the payload contained within a token. This interface defines the structure * of the key-value pairs that include both predefined and custom claims. * * It defines a structure where a required user ID (uid) is included, as well as a flexible * set of custom claims represented by an index signature. * * Properties: * - `uid`: A string representing the unique ID of the user. * - `[key: string]`: An index signature allowing additional custom key-value pairs. * * Use this interface when defining or interacting with token payloads containing standardized * and custom claims. */ interface TokenPayload { uid: string; [key: string]: any; } /** * Represents the payload structure for a refresh token. * Extends the base TokenPayload interface. * * This interface includes a unique token identifier (jti) and * specifies the token type as 'refresh'. */ interface RefreshTokenPayload extends TokenPayload { jti: string; type: 'refresh'; } /** * Represents the configuration for JWT (JSON Web Token) generation and verification. * * This configuration is used to define the parameters necessary for creating and validating tokens. * * - `secret`: The secret key or binary data used for signing or verifying the JWT. * - `algorithm`: Optional, specifies the algorithm to be employed for signing or verifying the JWT. Defaults to `HS256` if not provided. * - `accessTokenExpiry`: Optional, defines the expiration time for access tokens, represented as a string (e.g., '1h', '30m'). * - `refreshTokenExpiry`: Optional, defines the expiration time for refresh tokens, represented as a string (e.g., '7d', '1d'). * - `issuer`: Optional, specifies the entity (issuer) generating the token. * - `audience`: Optional, indicates the intended recipient(s) of the token. */ interface JwtConfig { secret: string | Uint8Array; algorithm?: 'HS256' | 'HS384' | 'HS512' | 'RS256' | 'RS384' | 'RS512' | 'ES256' | 'ES384' | 'ES512' | 'EdDSA'; accessTokenExpiry?: string; refreshTokenExpiry?: string; issuer?: string; audience?: string; } /** * Represents a pair of tokens and associated metadata for authentication or authorization purposes. * * @interface TokenPair * @property {string} accessToken The token utilized for accessing secured resources or endpoints. * @property {string} refreshToken The token used to acquire a new access token upon expiration. * @property {number} expiresIn The duration in seconds until the access token expires. */ interface TokenPair { accessToken: string; refreshToken: string; expiresIn: number; } /** * Represents a token that has been successfully verified, containing its payload and protected header information. * * @template T - The shape of the token payload, defaults to `TokenPayload`. * @property {T} payload - The payload of the verified token, which contains the data encoded within the token. * @property {any} protectedHeader - The protected header of the verified token, which includes cryptographic and metadata information. */ interface VerifiedToken<T = TokenPayload> { payload: T; protectedHeader: any; } /** * Represents an endpoint to be excluded based on specified patterns and allowed HTTP methods. */ interface ExcludedEndpoint { endpoint: string | RegExp; methods: HttpMethod[]; } /** * Configuration options for setting up middleware. * * This interface defines the properties needed to configure middleware behavior, * including token handling, algorithm settings, endpoint exclusions, and other customizations. */ interface MiddlewareConfig { secret: string | Uint8Array; algorithm?: JwtConfig['algorithm']; excludedEndpoints?: ExcludedEndpoint[]; tokenExtractor?: (req: Request) => string | undefined; isBlacklisted?: (token: string, payload: TokenPayload) => Promise<boolean>; corsOrigin?: string; } /** * Extends the Request interface to include additional properties for handling authentication. */ interface AuthRequest extends Request { payload?: { uid: string; [key: string]: any; }; token?: string; } /** * Interface for managing a blacklist of tokens, typically used to handle JWT token invalidation. * Allows for adding tokens to the blacklist, checking if a token is blacklisted, and optionally cleaning up expired tokens. */ interface TokenBlacklist { /** * Adds a token with an associated expiry time to the system. * * @param {string} token - The token string to be added. * @param {number} expirySeconds - The time in seconds after which the token will expire. * @return {Promise<void>} A promise that resolves when the token is successfully added. */ add(token: string, expirySeconds: number): Promise<void>; /** * Checks if the provided token is blacklisted. * * @param {string} token - The token to check against the blacklist. * @return {Promise<boolean>} A promise that resolves to a boolean indicating whether the token is blacklisted. */ isBlacklisted(token: string): Promise<boolean>; /** * Performs cleanup operations and releases any resources that were allocated. * Typically used to ensure proper handling and disposal of resources. * * @return {Promise<void>} A promise that resolves when the cleanup process is complete. */ cleanup?(): Promise<void>; } /** * Interface representing the data structure for a refresh token. * * This interface contains attributes related to a refresh token, including * identification, expiration details, token tracking for reuse detection, * and optional references to usage and subsequent tokens. * **/ interface RefreshTokenData { userId: string; expiryDate: Date; tokenFamily: string; usedAt?: Date; nextToken?: string; } /** * Configuration options for Refresh Token Rotation. * * This interface defines properties to configure the behavior of refresh token rotation, * including a grace period to allow token reuse under certain conditions. * * Properties: * - `reuseWindowSeconds`: An optional number representing the grace period in seconds during which a refresh token can be reused without being considered invalid. Defaults to 10 seconds if not provided. */ interface RefreshTokenRotationConfig { reuseWindowSeconds?: number; } /** * An interface representing a store for managing refresh tokens, providing methods * to save, retrieve, validate, invalidate, and manage tokens, including token families. */ interface RefreshTokenStore { save(jti: string, userId: string, expiryDate: Date, tokenFamily: string): Promise<void>; get(jti: string): Promise<RefreshTokenData | null>; exists(jti: string): Promise<boolean>; invalidate(jti: string): Promise<void>; markAsUsed(jti: string, usedAt: Date, nextToken: string): Promise<void>; invalidateAllForUser(userId: string): Promise<void>; invalidateTokenFamily(tokenFamily: string): Promise<void>; } /** * Represents an error related to JWT authentication. * This class extends the standard Error object to include additional properties * specific to authorization errors, such as an HTTP status code and an optional error code. */ declare class JwtAuthError extends Error { statusCode: number; code?: string | undefined; constructor(message: string, statusCode?: number, code?: string | undefined); } /** * Manages JWT token generation, verification, and decoding. This class provides methods * for creating access and refresh tokens, verifying their validity, and decoding tokens for debugging purposes. * It ensures tokens are signed and verified using secure algorithms. */ declare class JwtTokenManager { private readonly secret; private config; constructor(config: JwtConfig); /** * Generates a pair of tokens (access token and refresh token) based on the provided payload. * * @param {TokenPayload} payload - The data to include in the token payload. Must include a `uid` property representing the user ID. * @return {Promise<TokenPair>} A promise resolving to an object containing the access token, refresh token, and the access token's expiration time. * @throws {JwtAuthError} Throws an error if the `uid` property is missing from the payload. */ generateTokenPair(payload: TokenPayload): Promise<TokenPair>; /** * Generates a signed access token based on the given payload. * * @param {TokenPayload} payload - The payload object containing user-specific claims and details required for token generation. * @return {Promise<string>} A promise that resolves to the generated access token as a string. * @throws {JwtAuthError} If the payload does not include a valid user ID (uid). */ generateAccessToken(payload: TokenPayload): Promise<string>; /** * Verifies the provided access token and returns the token payload and protected header if valid. * * @param {string} token - The access token to be verified. * @return {Promise<VerifiedToken<TokenPayload>>} A promise that resolves with the verified payload and protected header if the token is valid. * @throws {JwtAuthError} Throws an error if the token is invalid, expired, or has a signature verification failure. */ verifyAccessToken(token: string): Promise<VerifiedToken<TokenPayload>>; /** * Verifies the provided refresh token and extracts its payload and protected header. * * @param {string} token - The refresh token to be verified. * @return {Promise<VerifiedToken<RefreshTokenPayload>>} A promise that resolves to a VerifiedToken object containing the * payload and protected header if the token is valid and meets all requirements. * @throws {JwtAuthError} Throws an error if the token is invalid, expired, has an invalid payload, is of the wrong type, * or fails signature verification. */ verifyRefreshToken(token: string): Promise<VerifiedToken<RefreshTokenPayload>>; /** * Decodes a given JSON Web Token (JWT) and extracts its payload. * * @param {string} token - The JSON Web Token to decode. * @return {JWTPayload} The payload object extracted from the token. * @throws {JwtAuthError} If the token format is invalid or cannot be decoded. */ decodeToken(token: string): JWTPayload; } /** * The `RefreshTokenManager` class is responsible for managing the lifecycle of refresh tokens, * including token rotation, validation, and revocation. It also implements detection for reuse attempts * and provides a grace period mechanism for managing concurrent requests. */ declare class RefreshTokenManager { private tokenManager; private tokenStore; private config; constructor(tokenManager: JwtTokenManager, tokenStore: RefreshTokenStore, config?: RefreshTokenRotationConfig); /** * Rotates the provided refresh token by validating its authenticity, checking for reuse, and generating a new token pair. * If the token has been reused within a specific grace period, it returns the existing new token pair. * If the token is reused outside the grace period, it invalidates the entire token family. * If the token is unused, it generates a new token pair with the same token family. * * @param {string} refreshToken The refresh token to be validated and rotated. * @return {Promise<TokenPair>} A Promise that resolves to a new token pair containing a new refresh token and an access token. * @throws {JwtAuthError} If the refresh token is invalid, expired, or reused suspiciously. */ rotateRefreshToken(refreshToken: string): Promise<TokenPair>; /** * Erstellt initiales Token Pair mit neuer Token-Familie */ createTokenPair(payload: { uid: string; [key: string]: any; }, tokenFamily?: string): Promise<TokenPair>; /** * Revoke alle Tokens eines Users */ revokeAllTokens(userId: string): Promise<void>; /** * Generiert eine eindeutige Token-Familie-ID */ private generateTokenFamily; /** * Entfernt JWT-Standard-Claims aus Payload */ private stripJwtClaims; } /** * Creates an authentication middleware function for verifying and handling access tokens. * * @param {MiddlewareConfig} config Configuration object for the middleware setup. Includes options such as excluded endpoints, token extractor, blacklist checker, and CORS origin. * @param {JwtTokenManager} tokenManager An instance of a JWT token manager, used for verifying access tokens. * @return {Function} An Express.js middleware function that validates access tokens, checks for blacklisted tokens, and manages CORS headers. */ declare function createAuthMiddleware(config: MiddlewareConfig, tokenManager: JwtTokenManager): (req: AuthRequest, res: Response, next: NextFunction) => Promise<void | Response<any, Record<string, any>>>; /** * A memory-based implementation of a token blacklist. * This class allows adding tokens to a blacklist with an expiration time, * checking whether a token is currently blacklisted, * and cleaning up expired tokens from the blacklist. * * Implements the `TokenBlacklist` interface. */ declare class MemoryTokenBlacklist implements TokenBlacklist { private blacklisted; add(token: string, expirySeconds: number): Promise<void>; isBlacklisted(token: string): Promise<boolean>; cleanup(): Promise<void>; } /** * Parses an expiry value provided as a string or number and converts it into seconds. * Supports string formats with time units: seconds (s), minutes (m), hours (h), and days (d). * Defaults to 15 minutes (900 seconds) if input is invalid or not provided in a proper format. * * @param {string | number} expiry - The expiry value as a number (in seconds) or string * (e.g., "10m" for 10 minutes). * @return {number} - The expiry time in seconds. */ declare function parseExpiry(expiry: string | number): number; /** * Generates a unique token ID as a hexadecimal string. * * @return {string} A randomly generated 32-character hexadecimal token ID. */ declare function generateTokenId(): string; /** * A memory-based implementation of the `RefreshTokenStore` interface. * This class manages refresh tokens using an in-memory `Map` to store the tokens and related metadata. * It provides methods to save, retrieve, validate, mark, and invalidate refresh tokens. * * Note that this implementation is not suitable for production use, as all data is stored in memory * and will be lost when the application stops. It is intended for testing or simple use cases only. */ declare class MemoryRefreshTokenStore implements RefreshTokenStore { private store; /** * Saves a token's metadata into the store. * * @param {string} jti - The unique token identifier. * @param {string} userId - The user ID associated with the token. * @param {Date} expiryDate - The expiration date of the token. * @param {string} tokenFamily - The family or category of the token. * @return {Promise<void>} A promise that resolves when the token is saved. */ save(jti: string, userId: string, expiryDate: Date, tokenFamily: string): Promise<void>; /** * Retrieves refresh token data identified by the given jti (JSON Token Identifier). * Ensures the token has not expired; deletes expired tokens and returns null. * * @param {string} jti - The unique identifier of the refresh token to retrieve. * @return {Promise<RefreshTokenData | null>} A promise that resolves to the refresh token data if found and valid, or null if not found or expired. */ get(jti: string): Promise<RefreshTokenData | null>; /** * Checks if a given identifier (jti) exists in the storage. * * @param {string} jti - The unique identifier to check for existence. * @return {Promise<boolean>} A promise that resolves to true if the identifier exists, false otherwise. */ exists(jti: string): Promise<boolean>; /** * Marks a token as used by updating its associated data with the provided usage timestamp and next token. * * @param {string} jti - The unique identifier of the token to be marked as used. * @param {Date} usedAt - The timestamp indicating when the token was used. * @param {string} nextToken - The next token to associate with the current token. * @return {Promise<void>} A promise that resolves once the operation is complete. */ markAsUsed(jti: string, usedAt: Date, nextToken: string): Promise<void>; /** * Invalidates a token by its unique identifier (jti). * * @param {string} jti - The unique identifier of the token to invalidate. * @return {Promise<void>} A promise that resolves when the token has been successfully invalidated. */ invalidate(jti: string): Promise<void>; /** * Invalidates all tokens belonging to a specific token family by removing their entries from the store. * * @param {string} tokenFamily - The identifier of the token family to be invalidated. * @return {Promise<void>} Resolves when all tokens in the specified family have been invalidated. */ invalidateTokenFamily(tokenFamily: string): Promise<void>; /** * Invalidates all entries in the store associated with the specified user. * * @param {string} userId - The unique identifier of the user for whom all entries should be invalidated. * @return {Promise<void>} A promise that resolves when all relevant entries have been invalidated. */ invalidateAllForUser(userId: string): Promise<void>; } /** * Configuration options for the Redis refresh token store. * This interface defines the properties used to customize the behavior * of storing and managing refresh tokens in a Redis database. * * Properties: * - tokenPrefix: A string to be used as a prefix for storing tokens in Redis. * - userIndexPrefix: A string to be used as a prefix for user-related indices in Redis. * - familyIndexPrefix: A string to be used as a prefix for token family-related indices in Redis. */ interface RedisRefreshTokenStoreOptions { tokenPrefix?: string; userIndexPrefix?: string; familyIndexPrefix?: string; } /** * A class managing refresh tokens using a Redis backend. * Implements the `RefreshTokenStore` interface to handle token storage, retrieval, and invalidation. */ declare class RedisRefreshTokenStore implements RefreshTokenStore { private redis; private readonly tokenPrefix; private readonly userIndexPrefix; private readonly familyIndexPrefix; constructor(redis: RedisClientType, options?: RedisRefreshTokenStoreOptions); /** * Generates a token key by appending the provided token identifier (jti) to the token prefix. * * @param {string} jti - The unique token identifier. * @return {string} The concatenated token key. */ private tokenKey; /** * Generates a unique key for a user by appending the specified user ID to a predefined prefix. * * @param {string} userId - The unique identifier of the user. * @return {string} A string representing the generated user key. */ private userKey; /** * Generates a unique key by combining a predefined prefix with the provided tokenFamily string. * * @param {string} tokenFamily - The identifier for the token family to be used in the key generation. * @return {string} The generated family key as a string. */ private familyKey; /** * Saves a refresh token and its associated data into Redis with the specified time-to-live (TTL). * * @param {string} jti - The unique identifier of the token. * @param {string} userId - The ID of the user associated with the token. * @param {Date} expiryDate - The date and time when the token expires. * @param {string} tokenFamily - The family identifier of the token used for grouping related tokens. * @return {Promise<void>} A promise that resolves once the token and associated data have been saved. */ save(jti: string, userId: string, expiryDate: Date, tokenFamily: string): Promise<void>; /** * Retrieves refresh token data by its unique token identifier (jti). * * @param {string} jti - The unique token identifier. * @return {Promise<RefreshTokenData | null>} A promise that resolves to the refresh token data if found, or null if not found. */ get(jti: string): Promise<RefreshTokenData | null>; /** * Checks if a token identifier (JTI) exists by verifying its presence. * * @param {string} jti - The token identifier to check for existence. * @return {Promise<boolean>} A promise that resolves to true if the token identifier exists, otherwise false. */ exists(jti: string): Promise<boolean>; /** * Marks a token as used by updating its metadata and resetting its expiration time. * * @param {string} jti - The unique identifier of the token to mark as used. * @param {Date} usedAt - The date and time when the token was used. * @param {string} nextToken - The next token value to associate with the current token. * @return {Promise<void>} A promise that resolves when the token metadata is successfully updated. */ markAsUsed(jti: string, usedAt: Date, nextToken: string): Promise<void>; /** * Invalidates a token by its unique JSON Token Identifier (JTI). * * @param {string} jti - The unique identifier of the token to be invalidated. * @return {Promise<void>} A promise that resolves once the token is invalidated. */ invalidate(jti: string): Promise<void>; /** * Invalidates all tokens associated with a given token family. * * @param {string} tokenFamily - The identifier for the token family to invalidate. * @return {Promise<void>} A promise that resolves once the token family and related tokens are invalidated. */ invalidateTokenFamily(tokenFamily: string): Promise<void>; /** * Invalidates all tokens associated with a specific user by their user ID. * This includes removing all token data and metadata stored in the Redis database. * * @param {string} userId - The unique identifier of the user for whom all tokens should be invalidated. * @return {Promise<void>} A promise that resolves when all tokens have been successfully invalidated. */ invalidateAllForUser(userId: string): Promise<void>; } export { type AuthRequest, type ExcludedEndpoint, HTTP_STATUS, type HttpMethod, JwtAuthError, type JwtConfig, JwtTokenManager, MemoryRefreshTokenStore, MemoryTokenBlacklist, type MiddlewareConfig, RedisRefreshTokenStore, type RefreshTokenData, RefreshTokenManager, type RefreshTokenPayload, type RefreshTokenRotationConfig, type RefreshTokenStore, type TokenBlacklist, type TokenPair, type TokenPayload, type VerifiedToken, createAuthMiddleware, generateTokenId, parseExpiry };