Passport Architecture - Foundation as Sole Issuer
cgen-23713b9e9846477491bef5593076883e
This commit is contained in:
parent
1e88fbe75c
commit
c624ea9325
1 changed files with 270 additions and 0 deletions
270
docs/PASSPORT-ARCHITECTURE.md
Normal file
270
docs/PASSPORT-ARCHITECTURE.md
Normal file
|
|
@ -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:**
|
||||
- <20><> 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
|
||||
Loading…
Reference in a new issue