31 KiB
================================================================================ AUTH FOLDER: COMPREHENSIVE ANALYSIS & TOKEN FLOW DOCUMENTATION
PROJECT: NewApproach DATE: 2026-01-10 STATUS: In-depth analysis of auth module token acquisition, storage, and retrieval
================================================================================ FOLDER CONTENTS OVERVIEW
/NewApproach/auth/ ├── auth.ts TypeScript implementation (source) ├── auth-universal.js JavaScript version (browser-compatible) ├── auth-test.html Interactive test interface for manual testing └── README.md Documentation and quick-start guide
================================================================================ ARCHITECTURAL DESIGN
PATTERN-BASED CONFIGURATION:
- Auth system uses a "pattern" approach to enable only needed token types
- Patterns: 'pb', 'graph', 'pb+graph' (composable, can add more)
- This allows flexibility across different projects using different providers
SUPPORTED TOKEN TYPES (4 total):
- 'pb-user' → PocketBase user authentication token
- 'pb-agent' → PocketBase service account token (for server-to-server)
- 'graph-user' → Microsoft Graph delegated token (on behalf of signed-in user)
- 'graph-agent' → Microsoft Graph app-only token (service principal auth)
STORAGE MECHANISM:
- All tokens stored in browser localStorage (client-side storage)
- Storage keys use consistent naming pattern: 'auth:'
- Examples:
- 'auth:pb-user' → stores PocketBase user token
- 'auth:graph-user' → stores Microsoft Graph delegated token
================================================================================ TOKEN ACQUISITION FLOW (WHERE & WHEN)
CRITICAL DISTINCTION: This module does NOT acquire tokens itself. It is a STORAGE & RETRIEVAL system. Actual token acquisition happens elsewhere.
WHO ACQUIRES TOKENS?
- PocketBase tokens: PocketBase SDK (pb.authStore.token after user logs in)
- Graph tokens: Microsoft authentication library (via OAuth flow or app auth)
- These are obtained OUTSIDE this auth module
- This module then STORES and RETRIEVES them
THE FLOW:
-
EXTERNAL CODE ACQUIRES TOKEN └─ Where: In user's application code or backend └─ How: Via PocketBase SDK login or Microsoft authentication libraries └─ When: After successful user authentication or service account setup
-
TOKEN IS STORED VIA Auth.setToken() └─ Code: Auth.setToken('pb-user', ) └─ Storage: localStorage under key 'auth:pb-user' └─ When: Immediately after acquisition (outside this module)
-
TOKEN IS RETRIEVED VIA Auth.getToken() └─ Code: const token = Auth.getToken('pb-user') └─ Returns: String token or null if not found └─ When: Whenever application needs to use the token (API calls, etc.)
================================================================================ STORAGE DETAILS (EXACT LOCATIONS & KEYS)
localStorage MAP:
Token Type │ Storage Key │ Where It Comes From ────────────────────┼────────────────────┼──────────────────────────────── 'pb-user' │ 'auth:pb-user' │ PocketBase SDK after login 'pb-agent' │ 'auth:pb-agent' │ PocketBase SDK service account 'graph-user' │ 'auth:graph-user' │ MSAL or Graph auth library 'graph-agent' │ 'auth:graph-agent' │ Azure app authentication
STORAGE FORMAT:
- Raw string value (plaintext)
- Example: localStorage['auth:pb-user'] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
- No serialization, just direct token string storage
PERSISTENCE:
- localStorage persists until:
- User manually clears browser data
- JavaScript calls localStorage.removeItem(key)
- Session expires (no automatic expiry in this module)
- Survives page refreshes and browser restarts (within same origin)
================================================================================ RETRIEVAL METHODS (HOW TO GET TOKENS BACK)
METHOD 1: Direct Retrieval ───────────────────────── Auth.getToken('pb-user')
Returns: string | null
- Returns the token string if found
- Returns null if token was never set or was cleared
Usage Example:
const pbToken = Auth.getToken('pb-user');
if (pbToken) {
// Use token in API call
fetch('/api/users', {
headers: { 'Authorization': Bearer ${pbToken} }
});
}
Method Signature (TypeScript): getToken(type: TokenType): string | null
Internal Implementation:
- Accepts token type: 'pb-user', 'pb-agent', 'graph-user', or 'graph-agent'
- Looks up storage key from TOKEN_STORAGE_KEYS map
- Calls localStorage.getItem(key)
- Returns result (string or null)
────────────────────────────────────────────────────────────────────────────
METHOD 2: Set Token (Store for Later Retrieval) ─────────────────────────────────────────────── Auth.setToken('pb-user', 'token-string-here')
Usage: When you receive a token from external auth system // After PocketBase login: const pbAuth = await pb.collection('users').authWithPassword(email, pass); Auth.setToken('pb-user', pbAuth.token); // Now stored for later use
Method Signature (TypeScript): setToken(type: TokenType, token: string | null): void
Internal Implementation:
- Accepts token type and token string
- Looks up storage key
- If token is provided: localStorage.setItem(key, token)
- If token is null: localStorage.removeItem(key)
────────────────────────────────────────────────────────────────────────────
METHOD 3: Clear Individual Token ──────────────────────────────── Auth.clearToken('pb-user')
Removes one token from storage.
Method Signature (TypeScript): clearToken(type: TokenType): void
Internal Implementation: Calls setToken(type, null) which removes the item
Usage: On logout or when token expires Auth.clearToken('pb-user'); // PocketBase token removed
────────────────────────────────────────────────────────────────────────────
METHOD 4: Clear All Tokens ────────────────────────── Auth.clearAllTokens()
Removes all stored tokens (all 4 types) in one call.
Method Signature (TypeScript): clearAllTokens(): void
Internal Implementation: Iterates through all TOKEN_STORAGE_KEYS Calls localStorage.removeItem() for each key
Usage: On complete logout Auth.clearAllTokens(); // All tokens removed
────────────────────────────────────────────────────────────────────────────
METHOD 5: Configuration & Setup ──────────────────────────────── await Auth.configure('pb+graph', { pbUrl: 'http://localhost:8090' })
Initializes the auth module with:
- Which patterns to enable ('pb', 'graph', 'pb+graph')
- Optional config (PocketBase URL, Graph API URL)
Method Signature (TypeScript): async configure(patternString: string, config?: AuthConfig): Promise
Internal Implementation:
- Parses pattern string into array of token types
- Stores pattern for validation
- If pattern includes 'pb' tokens: initializes PocketBase SDK
- If PocketBase library not loaded: throws error
CRITICAL: PocketBase must be loaded via <script> BEFORE calling configure()
Config Object Properties: interface AuthConfig { pbUrl?: string; // Default: http://127.0.0.1:8090 graphApiUrl?: string; // URL for Microsoft Graph API }
────────────────────────────────────────────────────────────────────────────
METHOD 6: Query Module State ──────────────────────────── Auth.isConfigured(): boolean Auth.getEnabledTypes(): TokenType[]
Check if module is ready and what token types are enabled.
Usage: if (Auth.isConfigured()) { const types = Auth.getEnabledTypes(); // Returns ['pb-user', 'pb-agent'] const token = Auth.getToken('pb-user'); }
================================================================================ FILE-BY-FILE BREAKDOWN
FILE 1: auth.ts (TypeScript Source) ═════════════════════════════════════
PURPOSE:
- Main implementation of AuthManager class
- Fully typed with TypeScript (strict mode)
- Source file (not browser-compatible directly)
TOKEN ACQUISITION POINT:
- NONE. This file does NOT acquire tokens.
- It only stores and retrieves tokens from localStorage
- External code must provide tokens via Auth.setToken()
TOKEN STORAGE MECHANISM:
- localStorage is the sole storage mechanism
- Keys defined in TOKEN_STORAGE_KEYS constant: 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 STRUCTURE:
- AuthManager class (private instance state)
- Singleton exported as: export default Auth
- Also attached to window for browser use: window.Auth = Auth
KEY STATE VARIABLES:
- private pattern: TokenType[] = [] (which token types are enabled)
- private pbInstance: any = null (PocketBase SDK instance)
- private config: AuthConfig = {} (configuration passed by user)
METHOD IMPLEMENTATIONS:
-
configure(patternString, config?) ├─ Parses pattern string: 'pb+graph' → ['pb-user', 'pb-agent', 'graph-user', 'graph-agent'] ├─ Actually only returns ['pb', 'graph'] because parsePattern treats 'pb' as a prefix filter ├─ Stores config └─ If pattern includes 'pb': calls initPocketBase()
-
parsePattern(pattern: string): TokenType[] ├─ Splits by '+' ├─ Trims whitespace ├─ Filters empty strings ├─ Casts to TokenType[] └─ QUIRK: This is overly simple and may not work correctly for all cases
-
initPocketBase() ├─ Gets PocketBase from window.PocketBase ├─ Creates instance with URL from config ├─ Throws if window.PocketBase not loaded └─ Stores in this.pbInstance (but never used anywhere)
-
getToken(type: TokenType): string | null ├─ Looks up TOKEN_STORAGE_KEYS[type] ├─ Calls localStorage.getItem(key) └─ Returns result (string | null)
-
setToken(type: TokenType, token: string | null): void ├─ Looks up TOKEN_STORAGE_KEYS[type] ├─ If token is truthy: localStorage.setItem(key, token) └─ If token is falsy: localStorage.removeItem(key)
-
clearToken(type: TokenType): void └─ Calls setToken(type, null)
-
clearAllTokens(): void └─ Iterates all TOKEN_STORAGE_KEYS and removes each
DEPENDENCIES:
- localStorage (browser API) - REQUIRED
- PocketBase (window.PocketBase) - Optional, only if using 'pb' pattern
- No npm dependencies
SECURITY NOTES:
- Tokens stored in plaintext localStorage
- Accessible to any JavaScript on the page
- Vulnerable to XSS attacks
- Not suitable for highly sensitive credentials
- Production: consider HTTP-only cookies on backend
────────────────────────────────────────────────────────────────────────────
FILE 2: auth-universal.js (JavaScript Version) ════════════════════════════════════════════════
PURPOSE:
- Browser-compatible JavaScript version of auth.ts
- Simpler implementation, less feature-rich
- Can be used without TypeScript compilation
STRUCTURE:
- IIFE (Immediately Invoked Function Expression)
- Exposes Auth object to window.Auth
- Returns object with public methods
TOKEN ACQUISITION POINT:
- NONE. Same as auth.ts.
- Use Auth.setToken() to store tokens from external sources
TOKEN STORAGE:
- localStorage with keys defined in TOKEN_KEYS: const TOKEN_KEYS = { 'pb-user': 'pbUserToken', 'pb-agent': 'pbAgentToken', 'graph-user': 'graphUserToken', 'graph-agent': 'graphAgentToken' };
IMPORTANT DIFFERENCE FROM auth.ts:
- DIFFERENT STORAGE KEY NAMES!
- auth.ts uses: 'auth:pb-user'
- auth-universal.js uses: 'pbUserToken'
- ⚠️ INCOMPATIBLE: Tokens stored by one won't be readable by the other!
STATE VARIABLES: let pattern = 'pb-user' (default pattern) let enabledTypes = ['pb-user'] (which types are active) let pb = null (PocketBase instance) let popup = null (for OAuth popups, not implemented) let agentCreds = { email: '', password: '' } (credentials storage, not used) let superuserCreds = { email: '', password: '' } (credentials storage, not used)
PUBLIC METHODS: window.Auth.use(type) │ Set active pattern window.Auth.setPB(pbInstance) │ Inject PocketBase instance window.Auth.getToken(type) │ Retrieve token from localStorage window.Auth.setToken(type, token) │ Store token to localStorage
METHOD IMPLEMENTATIONS:
-
use(type) ├─ Sets pattern variable ├─ Parses type string into enabledTypes array ├─ If pattern includes 'pb': calls initPocketBase() └─ Returns 'this' for chaining
-
setPB(pbInstance) ├─ Allows external PocketBase instance injection └─ Stores in pb variable
-
initPocketBase() ├─ Returns early if pb already set ├─ Creates new PocketBase('http://127.0.0.1:8090') └─ Stores in pb variable
-
getToken(type) ├─ Looks up TOKEN_KEYS[type] ├─ Returns localStorage.getItem(key) or null └─ Same behavior as auth.ts version
-
setToken(type, token) ├─ Looks up TOKEN_KEYS[type] ├─ If token: localStorage.setItem(key, token) └─ If !token: localStorage.removeItem(key)
UNUSED/INCOMPLETE:
- popup variable (defined but never used)
- agentCreds, superuserCreds (defined but never used)
- Suggests incomplete refactoring or planned features
DEPENDENCIES:
- localStorage (browser API)
- PocketBase (optional, can be injected via setPB())
────────────────────────────────────────────────────────────────────────────
FILE 3: auth-test.html (Testing & Validation) ═══════════════════════════════════════════════
PURPOSE:
- Interactive HTML test page
- Manual verification of auth module functionality
- No automated testing, all tests are button-triggered
TOKEN ACQUISITION:
- NONE. This file does NOT acquire tokens.
- Tests localStorage directly to verify storage mechanics
HOW IT WORKS:
- Loads auth-universal.js (or auth.ts compiled)
- Provides buttons that trigger JavaScript functions
- Functions test module availability and storage
TEST SECTIONS:
-
Module Availability Test ├─ Button: "Test Module Load" ├─ Tests: typeof window.Auth !== 'undefined' └─ Verifies: Auth module is accessible globally
-
Token Storage Test ├─ Button: "Test Token Storage" ├─ Tests: │ ├─ localStorage.setItem('test:token', 'test-value') │ ├─ localStorage.getItem('test:token') │ └─ localStorage.removeItem('test:token') └─ Verifies: localStorage works correctly
-
Configuration Test ├─ Button: "Test Configuration" ├─ Tests: typeof window.Auth.configure === 'function' └─ Verifies: Auth.configure method exists
-
Clear Tokens Test ├─ Button: "Clear All Tokens" ├─ Steps: │ ├─ Creates test tokens: 'auth:pb-user', 'auth:graph-user' │ ├─ Removes them via localStorage.removeItem() │ └─ Verifies tokens are gone └─ Tests: Auth module clearing capability
LIMITATIONS:
- Tests use direct localStorage, not Auth.clearAllTokens()
- Tests auth-test.html keys, not actual auth module keys
- Not comprehensive
- No error handling for edge cases
- No async test support
────────────────────────────────────────────────────────────────────────────
FILE 4: README.md (Documentation) ══════════════════════════════════
PURPOSE:
- Public-facing documentation
- Quick-start guide for developers
KEY SECTIONS:
- Overview: Describes pattern-based approach and features
- Quick Start: Code examples showing basic usage
- Token Types: Lists all 4 token types
- Configuration: Documents AuthConfig interface
- Security Considerations: Plaintext localStorage warning
- File Structure: Simple directory listing
- Testing: Refers to auth-test.html
- Future Enhancements: Planned features (refresh logic, expiration tracking, etc.)
CRITICAL SECURITY WARNING FROM DOCS:
- "Tokens are stored in plaintext in localStorage"
- "Never store highly sensitive credentials in browser localStorage"
- "For production: consider using secure HTTP-only cookies via backend"
================================================================================ COMPLETE TOKEN FLOW DIAGRAM
┌─────────────────────────────────────────────────────────────────────────┐ │ EXTERNAL AUTH SOURCE │ │ (PocketBase SDK, MSAL, Azure App Auth, etc.) │ └────────────────────────┬────────────────────────────────────────────────┘ │ │ TOKEN ACQUIRED │ (user login, service auth, OAuth) ▼ ┌──────────────────────────────────┐ │ Auth.setToken('type', token) │ ← CODE STORES TOKEN └────────────┬─────────────────────┘ │ │ Calls: localStorage.setItem(key, token) │ Key Example: 'auth:pb-user' ▼ ┌──────────────────────────────────┐ │ Browser localStorage │ ← PERSISTENT STORAGE │ 'auth:pb-user' → 'eyJhbG...' │ │ 'auth:graph-user' → 'ya29.a...' │ └──────────────────────────────────┘ │ │ On page load or API call needed │ ┌────────────▼──────────────────────┐ │ Auth.getToken('pb-user') │ ← CODE RETRIEVES TOKEN └────────────┬─────────────────────┘ │ │ Calls: localStorage.getItem('auth:pb-user') │ Returns: 'eyJhbG...' or null │ ┌────────────▼──────────────────────┐ │ Application Uses Token │ │ (API calls, authenticated │ │ requests, etc.) │ └──────────────────────────────────┘
================================================================================ IMPLEMENTATION GOTCHAS & KNOWN ISSUES
GOTCHA 1: STORAGE KEY MISMATCH Issue: auth.ts and auth-universal.js use DIFFERENT storage keys
- auth.ts: 'auth:pb-user'
- auth-universal.js: 'pbUserToken' Impact: If you use both files, tokens won't be visible across implementations Fix: Standardize on one set of keys
────────────────────────────────────────────────────────────────────────────
GOTCHA 2: POCKETBASE NEVER USED Issue: initPocketBase() creates instance but it's never actually used
- Stored in this.pbInstance but no methods call it
- No token refresh logic
- No authentication flows Impact: PocketBase initialization is pointless Fix: Either remove it or implement actual PocketBase auth flows
────────────────────────────────────────────────────────────────────────────
GOTCHA 3: PLAINTEXT TOKEN STORAGE Issue: Tokens stored in plain text in localStorage
- Accessible to any JavaScript on the page
- Vulnerable to XSS attacks
- Session-persistent (no expiry tracking) Impact: Not suitable for highly sensitive credentials Fix: For production, implement HTTP-only cookies via backend
────────────────────────────────────────────────────────────────────────────
GOTCHA 4: UNUSED STATE VARIABLES (auth-universal.js) Issue: Variables defined but never used:
- popup (for OAuth, not implemented)
- agentCreds, superuserCreds (storage, not implemented) Impact: Code is confusing, suggests incomplete refactoring Fix: Remove unused variables or implement their intended functionality
────────────────────────────────────────────────────────────────────────────
GOTCHA 5: NO PATTERN VALIDATION Issue: parsePattern() doesn't validate that parsed strings are actual TokenTypes
- 'pb' gets parsed as 'pb' but valid types are ['pb-user', 'pb-agent']
- Calling Auth.getToken('pb') will fail (not a valid TokenType) Impact: Configuration can silently set invalid patterns Fix: Validate pattern strings against valid TokenType list
────────────────────────────────────────────────────────────────────────────
GOTCHA 6: NO TOKEN EXPIRY TRACKING Issue: Module stores tokens but doesn't track expiration
- No way to know if token is expired
- No automatic refresh logic Impact: Stale/expired tokens can be used in API calls Fix: Implement expiration tracking and refresh logic (noted as future enhancement)
────────────────────────────────────────────────────────────────────────────
GOTCHA 7: NO ERROR BOUNDARIES Issue: If localStorage is disabled/unavailable, getToken/setToken fail silently
- Try/catch not implemented
- No error logging Impact: Hard to debug issues with storage failures Fix: Add error handling and logging
================================================================================ USAGE PATTERNS: WHEN TOKENS ARE ACQUIRED & USED
PATTERN 1: USER CREDENTIALS LOGIN (PocketBase) ───────────────────────────────────────────────
Timeline:
- User enters email/password
- Frontend code calls: pb.collection('users').authWithPassword(email, pwd)
- PocketBase returns: { token: 'eyJhbG...', user: {...} }
- Frontend calls: Auth.setToken('pb-user', response.token)
- Token stored in: localStorage['auth:pb-user'] = 'eyJhbG...'
- Later, to use token: const token = Auth.getToken('pb-user')
- In API headers: { 'Authorization':
Bearer ${token}}
WHEN IS TOKEN ACQUIRED: During user login WHERE IS TOKEN ACQUIRED: From PocketBase SDK response WHERE IS IT STORED: localStorage under key 'auth:pb-user' WHERE IS IT RETRIEVED: Via Auth.getToken('pb-user') when needed
────────────────────────────────────────────────────────────────────────────
PATTERN 2: SERVICE ACCOUNT AUTHENTICATION (PocketBase) ────────────────────────────────────────────────────────
Timeline:
- Backend has service account credentials (email/password)
- Backend authenticates: pb.collection('_superusers').authWithPassword(...)
- Backend receives token and stores in Auth.setToken('pb-agent', token)
- Frontend retrieves: Auth.getToken('pb-agent')
- Used for server-to-server operations
WHEN IS TOKEN ACQUIRED: During backend service startup WHERE IS TOKEN ACQUIRED: From PocketBase SDK (backend context) WHERE IS IT STORED: localStorage['auth:pb-agent'] WHERE IS IT RETRIEVED: Via Auth.getToken('pb-agent')
────────────────────────────────────────────────────────────────────────────
PATTERN 3: MICROSOFT GRAPH DELEGATED TOKEN ─────────────────────────────────────────────
Timeline:
- User initiates OAuth sign-in via Microsoft
- MSAL library handles OAuth flow
- MSAL returns: { accessToken: 'eyJ0eXAi...', expiresIn: 3600, ... }
- Code stores: Auth.setToken('graph-user', response.accessToken)
- Token stored in: localStorage['auth:graph-user']
- Used in Graph API calls: fetch('https://graph.microsoft.com/v1.0/me', ...)
WHEN IS TOKEN ACQUIRED: During OAuth flow completion WHERE IS TOKEN ACQUIRED: From MSAL authentication library WHERE IS IT STORED: localStorage['auth:graph-user'] WHERE IS IT RETRIEVED: Via Auth.getToken('graph-user')
────────────────────────────────────────────────────────────────────────────
PATTERN 4: MICROSOFT GRAPH APP-ONLY TOKEN (Service Principal) ──────────────────────────────────────────────────────────────
Timeline:
- Backend authenticates as service principal (not user)
- Uses client credentials flow (clientId + clientSecret)
- Azure AD returns: { access_token: 'eyJ0eXAi...', expires_in: 3600 }
- Code stores: Auth.setToken('graph-agent', response.access_token)
- Token stored in: localStorage['auth:graph-agent']
- Used for app-to-app operations
WHEN IS TOKEN ACQUIRED: During service principal auth WHERE IS TOKEN ACQUIRED: From Azure AD token endpoint WHERE IS IT STORED: localStorage['auth:graph-agent'] WHERE IS IT RETRIEVED: Via Auth.getToken('graph-agent')
================================================================================ SUMMARY: QUICK REFERENCE
TOKENS ACQUIRED BY: External authentication systems (not this module) TOKENS STORED BY: Auth.setToken() → localStorage TOKENS RETRIEVED BY: Auth.getToken() → returns string or null
STORAGE LOCATION: Browser localStorage STORAGE KEYS:
- 'auth:pb-user' (auth.ts version)
- 'auth:pb-agent' (auth.ts version)
- 'auth:graph-user' (auth.ts version)
- 'auth:graph-agent' (auth.ts version)
- 'pbUserToken' (auth-universal.js version)
- 'pbAgentToken' (auth-universal.js version)
- 'graphUserToken' (auth-universal.js version)
- 'graphAgentToken' (auth-universal.js version)
PUBLIC API: await Auth.configure(pattern, config?) Setup module Auth.getToken(type: TokenType) Retrieve token (returns string|null) Auth.setToken(type: TokenType, token) Store token Auth.clearToken(type: TokenType) Remove one token Auth.clearAllTokens() Remove all tokens Auth.isConfigured() Check if ready Auth.getEnabledTypes() Get active token types
SECURITY CONCERN: Plaintext localStorage
- Not suitable for highly sensitive credentials
- Vulnerable to XSS
- Production: use HTTP-only cookies on backend
MAIN IMPLEMENTATION FILE: auth.ts (TypeScript) BROWSER VERSION: auth-universal.js (JavaScript) TEST PAGE: auth-test.html (manual testing)