UNPKG

@sudowealth/schwab-api

Version:

TypeScript client for Charles Schwab API with OAuth support, market data, trading functionality, and complete type safety

289 lines (288 loc) 9.14 kB
import { type AuthDiagnosticsOptions, type AuthDiagnosticsResult } from './auth-diagnostics.js'; import { type TokenData, type RefreshOptions, type AuthClientOptions, type FullAuthClient } from './types.js'; export declare enum TokenErrorCode { AUTHORIZATION_ERROR = "AUTHORIZATION_ERROR", REFRESH_FAILED = "REFRESH_FAILED" } /** * Enhanced events for token persistence lifecycle */ export declare enum TokenPersistenceEvent { TOKEN_SAVED = "token_saved", TOKEN_SAVE_FAILED = "token_save_failed", TOKEN_LOADED = "token_loaded", TOKEN_LOAD_FAILED = "token_load_failed", TOKEN_VALIDATED = "token_validated", TOKEN_VALIDATION_FAILED = "token_validation_failed" } /** * Type for token persistence event handlers */ export type TokenPersistenceEventHandler = (event: TokenPersistenceEvent, data: TokenData, metadata?: Record<string, any>) => void; /** * Enhanced token manager configuration options */ export interface EnhancedTokenManagerOptions extends AuthClientOptions { /** * Maximum number of retry attempts for token refresh * @default 3 */ maxRetryAttempts?: number; /** * Initial delay in milliseconds before retrying a failed operation * @default 1000 (1 second) */ initialRetryDelayMs?: number; /** * Maximum delay in milliseconds between retry attempts * @default 30000 (30 seconds) */ maxRetryDelayMs?: number; /** * Whether to use exponential backoff for retries * @default true */ useExponentialBackoff?: boolean; /** * Threshold in milliseconds before token expiration to trigger refresh * @default 300000 (5 minutes) */ refreshThresholdMs?: number; /** * Enable detailed debug mode * @default false */ debug?: boolean; /** * Enable token validation before operations * @default true */ validateTokens?: boolean; /** * Enable auto-reconnection handling * @default true */ autoReconnect?: boolean; /** * Handler for token lifecycle events */ onTokenEvent?: TokenPersistenceEventHandler; /** * Enable tracing of all token operations * @default false */ traceOperations?: boolean; /** * Base URL for the issuer * @default API_URLS.PRODUCTION/API_VERSIONS.v1 */ issuerBaseUrl?: string; } /** * Enhanced token manager that provides improved token lifecycle management, * with robust persistence, validation, retry logic, and reconnection handling. */ export declare class EnhancedTokenManager implements FullAuthClient { private config; private tokenSet?; private saveFn?; private loadFn?; private persistenceDebugEnabled; private validateOnLoad; private persistenceEventHandler?; private lastSavedTokens?; private lastLoadedTokens?; private tracer; private refreshCallbacks; private reconnectionHandlers; private isReconnecting; private refreshLock; private logger; constructor(options: EnhancedTokenManagerOptions); /** * Get the authorization URL for the OAuth flow with PKCE support * This is an asynchronous method to properly generate and include the code challenge */ private codeVerifierForCurrentFlow; getAuthorizationUrl(opts?: { scope?: string[]; state?: string; }): Promise<{ authUrl: string; generatedState?: string; }>; /** * Exchange an authorization code for tokens * This method is used after the user completes the authorization flow * @param code The authorization code received from the OAuth server * @param stateParam Optional state parameter received in the callback, may contain code_verifier */ exchangeCode(code: string, stateParam?: string): Promise<TokenData>; /** * Implement the ITokenLifecycleManager interface */ /** * Explicitly initialize the token manager, ensuring tokens are loaded, validated, and refreshed if needed * @returns Promise<boolean> True if a valid access token is available after initialization, false otherwise */ initialize(): Promise<boolean>; /** * Get the current token data * Handles token loading and validation */ getTokenData(): Promise<TokenData | null>; /** * Get the access token only */ getAccessToken(): Promise<string | null>; /** * Check if this manager supports token refresh */ supportsRefresh(): boolean; /** * Refresh the tokens if needed * This is a core method that handles token refresh logic * with concurrency control */ refreshIfNeeded(options?: RefreshOptions): Promise<TokenData>; /** * Register a callback to be notified when tokens are refreshed */ onRefresh(callback: (tokenData: TokenData) => void): void; /** * Refresh tokens using a specific refresh token * This implements the FullAuthClient interface */ refresh(refreshToken: string, options?: { force?: boolean; }): Promise<TokenData>; /** * Register a callback to handle reconnection events * This is useful for handling token expiration scenarios */ addReconnectionHandler(handler: () => Promise<void>): void; /** * Trigger the reconnection process * This calls all registered reconnection handlers */ triggerReconnection(): Promise<void>; /** * Wait for any ongoing reconnection process to complete */ private waitForReconnection; /** * Check if a token needs to be refreshed */ private shouldRefreshToken; /** * Map OIDC token response to our TokenData format */ private mapToTokenData; /** * Load tokens from persistent storage */ private loadTokensFromStorage; /** * Persist tokens to storage * @param tokens The token set to persist * @param metadata Optional metadata about the persistence operation */ private persistTokens; /** * Validate tokens to ensure they meet basic requirements */ private validateTokens; /** * Clear all tokens from memory and storage */ clearTokens(): Promise<void>; /** * Manually save tokens * @param tokens The token set to save * @param metadata Optional metadata about the save operation */ saveTokens(tokens: Partial<TokenData>, metadata?: Record<string, any>): Promise<void>; /** * Helper for logging persistence-related events */ private logPersistenceEvent; /** * Dispatch token lifecycle events */ private dispatchTokenEvent; /** * Perform a direct token refresh using the OIDC client */ private performDirectTokenRefresh; /** * Perform a direct token exchange using fetch * This avoids direct dependency on specific OIDC implementations */ private performDirectTokenExchange; /** * PKCE functionality is now handled by the pkce-challenge package * * The package handles: * 1. Generating a cryptographically random code verifier * 2. Computing the code challenge using SHA-256 hashing * 3. Proper base64url encoding of both values */ /** * Handle token refresh with auto-reconnection */ private handleTokenRefreshWithReconnection; /** * Perform a token refresh with retry logic */ private doRefreshWithRetry; /** * Determine if an error is retryable */ private isRetryableError; /** * Calculate the delay before the next retry attempt */ private calculateRetryDelay; /** * Format token-related errors for consistent handling */ private formatTokenError; /** * Notify all registered refresh listeners */ private notifyRefreshListeners; /** * Generate a comprehensive token status report * This combines information about current tokens, validation status, and refresh history */ generateTokenReport(): Promise<{ currentTokens: TokenData | null; lastSavedTokens: TokenData | undefined; lastLoadedTokens: TokenData | undefined; tokenValidation: { valid: boolean; reason?: string; canRefresh?: boolean; isExpiring?: boolean; expiresInSeconds?: number; format?: { isValid: boolean; issues?: string[]; }; } | null; refreshHistory: any; }>; /** * Get diagnostics information about the current authentication state * This method helps troubleshoot 401 Unauthorized errors by providing token status details * * @param options Options for diagnostics * @returns Detailed diagnostics information */ getDiagnostics(options?: AuthDiagnosticsOptions): Promise<AuthDiagnosticsResult>; /** * Validate tokens and return a detailed report * This is used by generateTokenReport to provide more detailed validation info */ private validateTokenReport; }