// Auth Module: Universal, pattern-based token management // // How it works: // - Manages tokens for multiple auth patterns (PocketBase, Microsoft Graph, etc.) // - Stores tokens in localStorage under standardized keys // - Supports composition of patterns (e.g., 'pb+graph' enables both) // - All operations are synchronous and immutable // // Usage: // Auth.configure('pb+graph').then(() => { // const token = Auth.getToken('pb-user'); // const graphToken = Auth.getToken('graph-user'); // }) // // Token types available: // - pb-user: PocketBase user token // - pb-agent: PocketBase service account token // - graph-user: Microsoft Graph delegated token // - graph-agent: Microsoft Graph app-only token // // Dependencies: // - PocketBase (optional, for pb tokens): window.PocketBase // - localStorage: browser API for token persistence // // Gotchas: // - Tokens are stored in plaintext in localStorage (security consideration) // - Pattern must be set before accessing tokens // - All token types must be explicitly enabled via configure() type TokenType = 'pb-user' | 'pb-agent' | 'graph-user' | 'graph-agent'; interface TokenConfig { [key in TokenType]: string; } interface AuthConfig { pbUrl?: string; graphApiUrl?: string; } const TOKEN_STORAGE_KEYS: TokenConfig = { 'pb-user': 'auth:pb-user', 'pb-agent': 'auth:pb-agent', 'graph-user': 'auth:graph-user', 'graph-agent': 'auth:graph-agent' }; class AuthManager { private pattern: TokenType[] = []; private pbInstance: any = null; private config: AuthConfig = {}; async configure(patternString: string, config?: AuthConfig): Promise { this.pattern = this.parsePattern(patternString); this.config = config || {}; if (this.pattern.some(t => t.startsWith('pb'))) { await this.initPocketBase(); } } private parsePattern(pattern: string): TokenType[] { return pattern .split('+') .map(s => s.trim()) .filter(Boolean) as TokenType[]; } private async initPocketBase(): Promise { if (this.pbInstance) return; const pbUrl = this.config.pbUrl || 'http://127.0.0.1:8090'; const PocketBase = (window as any).PocketBase; if (!PocketBase) { throw new Error('PocketBase library not loaded. Include PocketBase script before Auth initialization.'); } this.pbInstance = new PocketBase(pbUrl); } getToken(type: TokenType): string | null { const key = TOKEN_STORAGE_KEYS[type]; if (!key) return null; return localStorage.getItem(key); } setToken(type: TokenType, token: string | null): void { const key = TOKEN_STORAGE_KEYS[type]; if (!key) return; if (token) { localStorage.setItem(key, token); } else { localStorage.removeItem(key); } } clearToken(type: TokenType): void { this.setToken(type, null); } clearAllTokens(): void { Object.keys(TOKEN_STORAGE_KEYS).forEach(key => { localStorage.removeItem(TOKEN_STORAGE_KEYS[key as TokenType]); }); } isConfigured(): boolean { return this.pattern.length > 0; } getEnabledTypes(): TokenType[] { return [...this.pattern]; } } // Export singleton instance const Auth = new AuthManager(); // Make available globally for browser environments if (typeof window !== 'undefined') { (window as any).Auth = Auth; } export default Auth; export { TokenType, AuthManager };