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
text/typescript
/**
* 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 };