Files
Job-Info/NewApproach/auth/AUTH_ANALYSIS.md
T
2026-01-10 19:23:47 +00:00

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):

  1. 'pb-user' → PocketBase user authentication token
  2. 'pb-agent' → PocketBase service account token (for server-to-server)
  3. 'graph-user' → Microsoft Graph delegated token (on behalf of signed-in user)
  4. '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:

  1. 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

  2. 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)

  3. 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:

  1. Accepts token type: 'pb-user', 'pb-agent', 'graph-user', or 'graph-agent'
  2. Looks up storage key from TOKEN_STORAGE_KEYS map
  3. Calls localStorage.getItem(key)
  4. 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:

  1. Accepts token type and token string
  2. Looks up storage key
  3. If token is provided: localStorage.setItem(key, token)
  4. 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:

  1. Parses pattern string into array of token types
  2. Stores pattern for validation
  3. If pattern includes 'pb' tokens: initializes PocketBase SDK
  4. 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:

  1. 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()

  2. 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

  3. 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)

  4. getToken(type: TokenType): string | null ├─ Looks up TOKEN_STORAGE_KEYS[type] ├─ Calls localStorage.getItem(key) └─ Returns result (string | null)

  5. 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)

  6. clearToken(type: TokenType): void └─ Calls setToken(type, null)

  7. 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:

  1. use(type) ├─ Sets pattern variable ├─ Parses type string into enabledTypes array ├─ If pattern includes 'pb': calls initPocketBase() └─ Returns 'this' for chaining

  2. setPB(pbInstance) ├─ Allows external PocketBase instance injection └─ Stores in pb variable

  3. initPocketBase() ├─ Returns early if pb already set ├─ Creates new PocketBase('http://127.0.0.1:8090') └─ Stores in pb variable

  4. getToken(type) ├─ Looks up TOKEN_KEYS[type] ├─ Returns localStorage.getItem(key) or null └─ Same behavior as auth.ts version

  5. 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:

  1. Module Availability Test ├─ Button: "Test Module Load" ├─ Tests: typeof window.Auth !== 'undefined' └─ Verifies: Auth module is accessible globally

  2. 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

  3. Configuration Test ├─ Button: "Test Configuration" ├─ Tests: typeof window.Auth.configure === 'function' └─ Verifies: Auth.configure method exists

  4. 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:

  1. Overview: Describes pattern-based approach and features
  2. Quick Start: Code examples showing basic usage
  3. Token Types: Lists all 4 token types
  4. Configuration: Documents AuthConfig interface
  5. Security Considerations: Plaintext localStorage warning
  6. File Structure: Simple directory listing
  7. Testing: Refers to auth-test.html
  8. 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:

  1. User enters email/password
  2. Frontend code calls: pb.collection('users').authWithPassword(email, pwd)
  3. PocketBase returns: { token: 'eyJhbG...', user: {...} }
  4. Frontend calls: Auth.setToken('pb-user', response.token)
  5. Token stored in: localStorage['auth:pb-user'] = 'eyJhbG...'
  6. Later, to use token: const token = Auth.getToken('pb-user')
  7. 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:

  1. Backend has service account credentials (email/password)
  2. Backend authenticates: pb.collection('_superusers').authWithPassword(...)
  3. Backend receives token and stores in Auth.setToken('pb-agent', token)
  4. Frontend retrieves: Auth.getToken('pb-agent')
  5. 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:

  1. User initiates OAuth sign-in via Microsoft
  2. MSAL library handles OAuth flow
  3. MSAL returns: { accessToken: 'eyJ0eXAi...', expiresIn: 3600, ... }
  4. Code stores: Auth.setToken('graph-user', response.accessToken)
  5. Token stored in: localStorage['auth:graph-user']
  6. 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:

  1. Backend authenticates as service principal (not user)
  2. Uses client credentials flow (clientId + clientSecret)
  3. Azure AD returns: { access_token: 'eyJ0eXAi...', expires_in: 3600 }
  4. Code stores: Auth.setToken('graph-agent', response.access_token)
  5. Token stored in: localStorage['auth:graph-agent']
  6. 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)

================================================================================ END OF AUTH FOLDER ANALYSIS