diff --git a/docs/PASSPORT-ARCHITECTURE.md b/docs/PASSPORT-ARCHITECTURE.md new file mode 100644 index 00000000..08b875fb --- /dev/null +++ b/docs/PASSPORT-ARCHITECTURE.md @@ -0,0 +1,270 @@ +# Passport Architecture: Foundation as Sole Issuer + +## Overview + +**aethex.foundation** is the **sole issuer and authority** for all user passports/identities in the AeThex ecosystem. All other platforms (aethex.dev, future platforms) are **OAuth clients** that consume Foundation-issued identities. + +This document defines the passport architecture and data flow. + +## Core Principle + +``` +Foundation = Single Source of Truth (SSOT) for identity +Each Platform = Read-only cache of Foundation passport data +``` + +## Architecture + +### 1. Foundation (aethex.foundation) +- **Owner of passport data** +- Issues and maintains all user identities +- Provides OAuth endpoints for authentication +- Provides passport API endpoints for clients to fetch user data +- Single authority for all identity mutations (updates, deletions) + +### 2. Client Platforms (aethex.dev, etc.) +- **Consumers of Foundation identities** +- Cache Foundation passport data locally for performance +- Use cached data for reads (lookups, profile queries) +- Forward any profile updates to Foundation's API +- Validate passport integrity on each login + +## Data Flow + +### User Login Flow + +``` +User → Login → Redirect to Foundation + ↓ +Foundation (OAuth) → Verify credentials + ↓ +Return access_token + user data to aethex.dev + ↓ +aethex.dev (callback handler): + 1. Exchange code for token with Foundation + 2. Fetch userinfo from Foundation API + 3. Validate passport is complete + 4. SYNC (one-way) to local cache: + - User profile data (from Foundation) + - Achievements (from Foundation) + - Projects (from Foundation) + 5. Create session on aethex.dev + ↓ +User authenticated, using Foundation passport +``` + +### Profile Update Flow + +``` +User edits profile on aethex.dev + ↓ +aethex.dev validates input locally + ↓ +Forward update request to Foundation API + ↓ +Foundation (SSOT) validates and applies changes + ↓ +Foundation returns updated passport + ↓ +aethex.dev syncs updated data to local cache + ↓ +Update confirmed to user +``` + +### Profile Read Flow + +``` +User/App requests profile on aethex.dev + ↓ +Check local cache + ↓ +If cache valid: Return cached data +If cache stale: Refresh from Foundation → update cache → return + ↓ +Never modify local cache outside of sync operations +``` + +## Key Rules + +### ✅ ALLOWED Operations on aethex.dev + +1. **Read from local cache** - Fast lookups of passport data +2. **Sync from Foundation** - One-way data import during login/refresh +3. **Validate against cache** - Use cached data for access control +4. **Forward to Foundation** - Route update requests to Foundation API +5. **Cache invalidation** - Clear stale cache on logout or explicit refresh + +### ❌ FORBIDDEN Operations on aethex.dev + +1. **Direct writes to passport data** - All identity mutations must go through Foundation +2. **Creating new passports** - Only Foundation can issue identities +3. **Modifying cached data** - Cache is read-only except during sync +4. **Trusting unvalidated data** - Always verify data came from Foundation + +## Database Schema (aethex.dev) + +```sql +-- Local cache of Foundation passport data +CREATE TABLE user_profiles ( + id UUID PRIMARY KEY, -- Foundation user ID (immutable) + email TEXT UNIQUE NOT NULL, -- From Foundation + username TEXT UNIQUE, -- From Foundation + full_name TEXT, -- From Foundation + avatar_url TEXT, -- From Foundation + profile_completed BOOLEAN DEFAULT false,-- From Foundation + + -- Sync tracking + foundation_synced_at TIMESTAMP, -- When last synced from Foundation + cache_valid_until TIMESTAMP, -- When cache expires + + -- Local metadata (not from Foundation) + last_login_at TIMESTAMP, -- aethex.dev tracking only + created_at TIMESTAMP, + updated_at TIMESTAMP +); + +-- Data from Foundation that shouldn't be written locally +-- (achievements, projects, etc. are fetched on-demand) +``` + +## Sync Mechanism + +### Initial Sync (On Login) +1. User authenticates with Foundation +2. aethex.dev receives `access_token` and basic user info +3. aethex.dev calls Foundation's `/api/oauth/userinfo` endpoint +4. All user data from Foundation gets UPSERTED to local cache +5. `foundation_synced_at` timestamp is updated + +### Periodic Sync (Background) +``` +Every 24 hours (or on explicit request): + 1. Fetch current user data from Foundation API + 2. Compare with local cache + 3. If Foundation data differs: update cache + 4. If local cache is newer: ERROR (shouldn't happen) + 5. Update `cache_valid_until` timestamp +``` + +### Cache Expiration +``` +If (now > cache_valid_until): + 1. Treat cache as stale + 2. Fetch fresh from Foundation + 3. Update cache and expiration +``` + +## Error Handling + +### What if Foundation is unavailable? + +**During Login:** +- �� Cannot proceed - user must authenticate through Foundation +- Return error: "Identity service unavailable" + +**For existing users (cache valid):** +- ✅ Can use cached data temporarily +- Continue serving from cache +- Log warning about Foundation unavailability +- Retry sync in background + +**For profile updates:** +- ❌ Cannot proceed - updates must go through Foundation +- Queue update locally, retry when Foundation is available +- Or fail gracefully: "Identity service unavailable, please try again" + +## Validation Rules + +### On Every Auth Request +```javascript +if (!user.foundation_synced_at) { + throw new Error("User not synced from Foundation"); +} + +if (now > user.cache_valid_until) { + // Refresh from Foundation + await syncFromFoundation(user.id); +} + +// User is authenticated and passport is valid +``` + +### On Every Profile Update Request +```javascript +// Forward to Foundation API, never write locally +const updated = await foundation.api.updateProfile(userId, changes); + +// On success, sync response back to cache +await syncFromFoundation(userId); + +return updated; +``` + +## Migration from Old Model + +**Old Model:** aethex.dev wrote directly to passport table +**New Model:** aethex.dev only syncs FROM Foundation + +### Migration Steps + +1. ✅ Remove Foundation arm from aethex.dev UI (DONE) +2. ✅ Clarify that aethex.dev is an OAuth client of Foundation (DONE) +3. Make profile endpoints read-only, forward writes to Foundation +4. Add cache validation on every auth request +5. Implement background sync for cache freshness +6. Monitor logs for any direct write attempts (should be 0) +7. Deprecate old direct-write endpoints + +## API Endpoints Reference + +### Foundation APIs (Used by aethex.dev) + +``` +GET /api/oauth/authorize - Start OAuth flow +POST /api/oauth/token - Exchange code for token +GET /api/oauth/userinfo - Fetch authenticated user's passport +GET /api/users/:id - Fetch specific user passport (public data) +PATCH /api/users/:id - Update passport (requires auth) +``` + +### aethex.dev APIs (Now read-only/cache-focused) + +``` +GET /api/profile/:username - Read from cache or fetch from Foundation +POST /api/profile/ensure - Sync passport from Foundation (on login) +PATCH /api/profile/:id - Forward to Foundation (no local writes) +``` + +## Monitoring & Observability + +### Log these events + +``` +[Passport Sync] User synced from Foundation: {userId} +[Passport Error] User not found in Foundation: {userId} +[Passport Stale] Cache expired for user: {userId}, refreshing... +[Passport Write] Attempted direct write to local cache (FORBIDDEN): {userId} +[Passport Conflict] Local cache newer than Foundation (SYNC ERROR): {userId} +``` + +### Metrics to track + +- Sync success rate (should be ~100%) +- Cache hit rate (should be >95% for logged-in users) +- Foundation API latency +- Failed sync attempts +- Stale cache detections + +## Future Enhancements + +1. **Webhook sync** - Foundation notifies clients when passport changes +2. **Event stream** - Subscribe to passport update events +3. **Multi-version support** - Handle different Foundation versions +4. **Distributed cache** - Redis layer for cross-instance consistency +5. **Audit trail** - Track all passport mutations at Foundation level + +## Related Documentation + +- `code/docs/PHASE3-SWITCHOVER-GUIDE.md` - OAuth migration guide +- `code/api/auth/callback.ts` - Login sync implementation +- `code/api/profile/ensure.ts` - Profile sync endpoint