@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.13 kB
TypeScript
import { type AuthDiagnosticsOptions, type AuthDiagnosticsResult } from './auth-diagnostics';
import { type TokenData, type RefreshOptions, type AuthClientOptions, type FullAuthClient } from './types';
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;
}