UNPKG

manage-token-sessions

Version:

A flexible token session manager for handling access/refresh token pairs with automatic refresh and cross-domain support

262 lines (254 loc) 8.41 kB
/** * Token pair returned by the refresh function */ interface TokenPair { accessToken: string; refreshToken: string; } /** * Stored session data with metadata */ interface TokenSession { accessToken: string; refreshToken: string; expiresAt: number; scope?: string; audience?: string; metadata?: Record<string, unknown>; } /** * Function that refreshes tokens */ type RefreshTokenFunction = (refreshToken: string) => Promise<TokenPair>; /** * Storage interface for flexible storage backends */ interface TokenStorage { get(key: string): Promise<string | null> | string | null; set(key: string, value: string, options?: StorageOptions): Promise<void> | void; remove(key: string, options?: StorageOptions): Promise<void> | void; } /** * Storage options for different backends */ interface StorageOptions { domain?: string; path?: string; secure?: boolean; sameSite?: 'strict' | 'lax' | 'none'; expires?: Date | number; ttl?: number; } /** * Configuration for the TokenSessionManager */ interface TokenSessionConfig { refreshTokenFn: RefreshTokenFunction; storage?: TokenStorage; storageKey?: string; customSessionExpirationTime?: number; debug?: boolean; onSessionStarted?: (session: TokenSession) => void; onSessionRefreshed?: (session: TokenSession) => void; onSessionExpired?: () => void; onSessionError?: (error: Error) => void; onRefreshError?: (error: Error) => void; } /** * JWT payload interface (minimal) */ interface JWTPayload { exp?: number; iat?: number; sub?: string; aud?: string | string[]; iss?: string; [key: string]: unknown; } /** * Decoded JWT structure */ interface DecodedJWT { header: Record<string, unknown>; payload: JWTPayload; signature: string; } /** * Error types */ declare class TokenSessionError extends Error { code: string; constructor(message: string, code: string); } declare class RefreshTokenError extends TokenSessionError { originalError?: Error | undefined; constructor(message: string, originalError?: Error | undefined); } declare class InvalidTokenError extends TokenSessionError { constructor(message: string); } declare class StorageError extends TokenSessionError { originalError?: Error | undefined; constructor(message: string, originalError?: Error | undefined); } declare class DuplicateHandlerError extends TokenSessionError { constructor(message: string); } /** * Cookie implementation of TokenStorage with cross-subdomain support */ declare class CookieStorageAdapter implements TokenStorage { private defaultOptions; constructor(defaultOptions?: StorageOptions); get(key: string): string | null; set(key: string, value: string, options?: StorageOptions): void; remove(key: string, options?: StorageOptions): void; } /** * LocalStorage implementation of TokenStorage */ declare class LocalStorageAdapter implements TokenStorage { get(key: string): string | null; set(key: string, value: string, _options?: StorageOptions): void; remove(key: string, _options?: StorageOptions): void; } /** * In-memory implementation of TokenStorage (useful for testing or temporary storage) */ declare class MemoryStorageAdapter implements TokenStorage { private storage; get(key: string): string | null; set(key: string, value: string, options?: StorageOptions): void; remove(key: string, _options?: StorageOptions): void; /** * Clear all stored items (useful for testing) */ clear(): void; /** * Get all keys (useful for testing) */ keys(): string[]; } /** * SessionStorage implementation of TokenStorage */ declare class SessionStorageAdapter implements TokenStorage { get(key: string): string | null; set(key: string, value: string, _options?: StorageOptions): void; remove(key: string, _options?: StorageOptions): void; } /** * TokenSessionManager - Manages access/refresh token pairs with on-demand refresh */ declare class TokenSessionManager { private config; /** * Promise cache for in-flight refresh requests * Prevents race conditions when multiple requests try to refresh simultaneously */ private refreshPromise; constructor(config: TokenSessionConfig); /** * Debug logger - logs detailed information when debug mode is enabled */ private log; /** * Start a new session with the provided token pair */ startSession(tokens: TokenPair, metadata?: Record<string, unknown>): Promise<void>; /** * Get the current access token, automatically refreshing if expired * This is the main method that handles all token refresh logic * Uses promise deduplication to prevent race conditions when multiple calls happen simultaneously */ getCurrentAccessToken(): Promise<string | null>; /** * Performs the actual token refresh operation * Separated into its own method to enable promise caching */ private performRefresh; /** * Get the current session data */ getCurrentSession(): Promise<TokenSession | null>; /** * End the current session and clean up */ endSession(): Promise<void>; /** * Check if there's an active session */ hasActiveSession(): Promise<boolean>; /** * Destroy the session manager and clean up resources */ destroy(): Promise<void>; /** * Register a handler for session started events * @throws {DuplicateHandlerError} if a handler is already registered */ registerOnSessionStarted(handler: (session: TokenSession) => void): void; /** * Register a handler for session refreshed events * @throws {DuplicateHandlerError} if a handler is already registered */ registerOnSessionRefreshed(handler: (session: TokenSession) => void): void; /** * Register a handler for session expired events * @throws {DuplicateHandlerError} if a handler is already registered */ registerOnSessionExpired(handler: () => void): void; /** * Register a handler for session error events * @throws {DuplicateHandlerError} if a handler is already registered */ registerOnSessionError(handler: (error: Error) => void): void; /** * Register a handler for refresh error events * @throws {DuplicateHandlerError} if a handler is already registered */ registerOnRefreshError(handler: (error: Error) => void): void; /** * Extract expiration time from access token */ private extractExpirationTime; /** * Store session data */ private storeSession; /** * Retrieve stored session data */ private getStoredSession; /** * Clear stored session data */ private clearStoredSession; } /** * Decode a JWT token without verification * This is safe for extracting expiration times since we're not validating the signature */ declare function decodeJWT(token: string): DecodedJWT; /** * Extract expiration time from a JWT token * Returns the expiration time as a Unix timestamp in seconds, or null if not present */ declare function getTokenExpiration(token: string): number | null; /** * Check if a JWT token is expired * @param token - The JWT token to check * @param bufferSeconds - Number of seconds before actual expiry to consider expired (default: 0) */ declare function isTokenExpired(token: string, bufferSeconds?: number): boolean; /** * Get the time remaining until token expiration * @param token - The JWT token to check * @returns Number of seconds until expiration, or null if expiration cannot be determined */ declare function getTimeUntilExpiration(token: string): number | null; /** * Validate that a token has the required claims */ declare function validateTokenClaims(token: string, requiredClaims?: string[]): boolean; export { CookieStorageAdapter, type DecodedJWT, DuplicateHandlerError, InvalidTokenError, type JWTPayload, LocalStorageAdapter, MemoryStorageAdapter, RefreshTokenError, type RefreshTokenFunction, SessionStorageAdapter, StorageError, type StorageOptions, type TokenPair, type TokenSession, type TokenSessionConfig, TokenSessionError, TokenSessionManager, type TokenStorage, decodeJWT, getTimeUntilExpiration, getTokenExpiration, isTokenExpired, validateTokenClaims };