From 9a4917b164c324fb1538a58646dee80914508cc5 Mon Sep 17 00:00:00 2001 From: "Builder.io" Date: Mon, 17 Nov 2025 02:25:31 +0000 Subject: [PATCH] Foundation OAuth Implementation Guide (Updated) cgen-43e3278525904c9fbd39f2078501e0aa --- docs/FOUNDATION-OAUTH-IMPLEMENTATION.md | 600 ++++++++++++++++++++++++ 1 file changed, 600 insertions(+) create mode 100644 docs/FOUNDATION-OAUTH-IMPLEMENTATION.md diff --git a/docs/FOUNDATION-OAUTH-IMPLEMENTATION.md b/docs/FOUNDATION-OAUTH-IMPLEMENTATION.md new file mode 100644 index 00000000..d07e9967 --- /dev/null +++ b/docs/FOUNDATION-OAUTH-IMPLEMENTATION.md @@ -0,0 +1,600 @@ +# Foundation OAuth Implementation - Complete Guide + +## Overview + +This guide details the **Phase 3 Switchover** implementation where aethex.dev becomes an OAuth **client** of aethex.foundation. aethex.foundation is the authoritative identity provider (the "issuer" of the Passport). + +**Status:** ✅ Implementation complete with PKCE support + +--- + +## Architecture + +``` +User Flow: +┌─────────────────────────────────────────────────────────────┐ +│ │ +│ 1. User visits aethex.dev/login │ +│ 2. Clicks "Login with Foundation" │ +│ 3. Redirected to aethex.foundation /api/oauth/authorize │ +│ 4. User authenticates on Foundation │ +│ 5. Foundation redirects back to aethex.dev/auth/callback │ +│ 6. Backend exchanges code for access token │ +│ 7. User profile synced from Foundation to Corp DB │ +│ 8. Session established on aethex.dev │ +│ 9. Redirected to dashboard │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Foundation OAuth Credentials + +These credentials were obtained during Foundation Phase 1 setup: + +``` +Foundation URL: https://aethex.foundation +Client ID: aethex_corp +Client Secret: bcoEtyQVGr6Z4557658eUXpDF5FDni2TGNahH3HT-FtylNrLCYwydwLO0sbKVHtfYUnZc4flAODa4BXkzxD_qg +Scopes: openid profile email achievements projects +``` + +**Environment Variables (add to .env):** + +```bash +# Foundation identity provider +VITE_FOUNDATION_URL=https://aethex.foundation + +# OAuth client credentials +FOUNDATION_OAUTH_CLIENT_ID=aethex_corp +FOUNDATION_OAUTH_CLIENT_SECRET=bcoEtyQVGr6Z4557658eUXpDF5FDni2TGNahH3HT-FtylNrLCYwydwLO0sbKVHtfYUnZc4flAODa4BXkzxD_qg +``` + +--- + +## Foundation OAuth Endpoints + +### 1. Authorization Endpoint + +Redirect users here to initiate authentication. + +``` +GET https://aethex.foundation/api/oauth/authorize + ?client_id=aethex_corp + &redirect_uri=https://aethex.dev/auth/callback + &response_type=code + &scope=openid profile email achievements projects + &state= + &code_challenge= + &code_challenge_method=S256 +``` + +**Parameters:** +- `client_id` - aethex_corp (identifies this app) +- `redirect_uri` - Where Foundation redirects after auth +- `response_type` - Always "code" (OAuth 2.0 authorization code flow) +- `scope` - Requested permissions +- `state` - CSRF token (generated by client, validated on callback) +- `code_challenge` - PKCE challenge (SHA256 hash of verifier) +- `code_challenge_method` - S256 (SHA256 PKCE method) + +**Implementation:** +See `code/client/lib/foundation-oauth.ts` → `initiateFoundationLogin()` + +### 2. Token Exchange Endpoint + +Backend exchanges authorization code for access token. + +``` +POST https://aethex.foundation/api/oauth/token +Content-Type: application/x-www-form-urlencoded + +grant_type=authorization_code +&code= +&redirect_uri=https://aethex.dev/auth/callback +&client_id=aethex_corp +&client_secret= +``` + +**Response:** +```json +{ + "access_token": "eyJ...", + "token_type": "Bearer", + "expires_in": 3600, + "scope": "openid profile email achievements projects" +} +``` + +**Implementation:** +See `code/api/auth/callback.ts` → `performTokenExchange()` + +### 3. User Info Endpoint + +Fetch authenticated user's profile. + +``` +GET https://aethex.foundation/api/oauth/userinfo +Authorization: Bearer + +Response: +{ + "id": "uuid", + "email": "user@example.com", + "username": "username", + "full_name": "Full Name", + "avatar_url": "https://...", + "profile_complete": true, + "achievements": ["achievement_id"], + "projects": ["project_id"] +} +``` + +**Implementation:** +See `code/api/auth/callback.ts` → `fetchUserInfoFromFoundation()` + +--- + +## PKCE Implementation + +**PKCE** (Proof Key for Code Exchange) adds extra security to OAuth flows. + +### How PKCE Works + +1. **Client generates code verifier:** + ```javascript + verifier = randomString(64 chars, URL-safe) + // Example: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~" + ``` + +2. **Client generates code challenge:** + ```javascript + challenge = base64url(SHA256(verifier)) + ``` + +3. **Client sends challenge with authorization request:** + ``` + GET /api/oauth/authorize?...&code_challenge=...&code_challenge_method=S256 + ``` + +4. **Server stores challenge, validates with verifier on token exchange:** + ``` + POST /api/oauth/token?...&code_verifier=... + ``` + +5. **Server verifies:** + ``` + base64url(SHA256(submitted_verifier)) === stored_challenge + ``` + +**Why PKCE?** +- Prevents authorization code interception attacks +- Secure for mobile apps and single-page applications +- Required by OAuth 2.1 best practices + +**Implementation:** +See `code/client/lib/foundation-oauth.ts` → `generatePKCEParams()` + +--- + +## Implementation Details + +### Frontend Components + +#### 1. Login Page (`code/client/pages/Login.tsx`) + +Updated to show Foundation OAuth button: + +```typescript + +``` + +Features: +- Initiates Foundation OAuth flow +- Generates PKCE parameters +- Stores verifier and state in sessionStorage +- Redirects to Foundation authorization endpoint + +#### 2. Foundation OAuth Library (`code/client/lib/foundation-oauth.ts`) + +Core OAuth functionality: + +```typescript +// Generate PKCE parameters +async function generatePKCEParams(): Promise<{ verifier, challenge }> + +// Build authorization URL +async function getFoundationAuthorizationUrl(options?): Promise + +// Initiate login +async function initiateFoundationLogin(redirectTo?: string): Promise + +// Exchange code for token (called from backend) +async function exchangeCodeForToken(code: string): Promise +``` + +#### 3. useFoundationAuth Hook (`code/client/hooks/use-foundation-auth.ts`) + +Handles OAuth callback: + +```typescript +// Process OAuth callback in URL +useFoundationAuth(): UseFoundationAuthReturn + +// Check authentication status +useFoundationAuthStatus(): { isAuthenticated, userId } +``` + +### Backend Endpoints + +#### 1. Callback Handler (`code/api/auth/callback.ts`) + +**Route:** `GET /auth/callback?code=...&state=...` + +**Flow:** +1. Receive authorization code from Foundation +2. Validate state token (CSRF protection) +3. Exchange code for access token +4. Fetch user info from Foundation +5. Sync user to local database +6. Set session cookies +7. Redirect to dashboard + +**Code:** +```typescript +async function handleCallback(req, res) { + // 1. Get code from URL + const { code, state } = req.query; + + // 2. Validate state + validateState(state); + + // 3. Exchange for token + const token = await performTokenExchange(code); + + // 4. Fetch user info + const user = await fetchUserInfoFromFoundation(token); + + // 5. Sync to database + await syncUserToLocalDatabase(user); + + // 6. Set cookies + res.setHeader("Set-Cookie", [ + `foundation_access_token=${token}; ...`, + `auth_user_id=${user.id}; ...` + ]); + + // 7. Redirect + return res.redirect("/dashboard"); +} +``` + +#### 2. Token Exchange (`POST /auth/callback/exchange`) + +**Called from frontend** to exchange code for token safely: + +```typescript +async function handleTokenExchange(req, res) { + const { code } = req.body; + + // Exchange code with Foundation + // Fetch user info + // Sync to database + // Set cookies + // Return token + user data +} +``` + +--- + +## Session Management + +### Session Cookies + +After successful authentication: + +``` +Cookies set: +├── foundation_access_token: +│ └── HttpOnly, Secure, SameSite=Strict +│ └── Max-Age: from expires_in +│ +└── auth_user_id: + └── Secure, SameSite=Strict + └── Max-Age: 30 days +``` + +### Using Access Token + +For authenticated API requests: + +```typescript +// Get token from cookie +const token = document.cookie + .split(';') + .find(c => c.trim().startsWith('foundation_access_token=')) + ?.split('=')[1]; + +// Use in requests +fetch('/api/user/profile', { + headers: { + 'Authorization': `Bearer ${token}` + }, + credentials: 'include' +}); +``` + +### Clearing Session + +On logout: + +```typescript +// Clear cookies +document.cookie = 'foundation_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC;'; +document.cookie = 'auth_user_id=; expires=Thu, 01 Jan 1970 00:00:00 UTC;'; + +// Optional: Notify Foundation of logout +await fetch(`${FOUNDATION_URL}/api/oauth/logout`, { + method: 'POST', + headers: { 'Authorization': `Bearer ${token}` } +}); +``` + +--- + +## User Profile Sync + +### Sync Flow + +``` +Foundation User Data: +├── id: UUID +├── email: string +├── username: string +├── full_name: string +├── avatar_url: URL +├── profile_complete: boolean +└── achievements, projects: arrays + + ↓ (via /api/oauth/userinfo) + +Corp Local Database (user_profiles table): +├── id: UUID (primary key) +├── email: string +├── username: string +├── full_name: string +├── avatar_url: string +├── profile_completed: boolean +├── updated_at: timestamp +└── (other corp-specific fields) +``` + +### Upsert Logic + +```typescript +await supabase.from("user_profiles").upsert({ + id: foundationUser.id, // Match by ID + email: foundationUser.email, + username: foundationUser.username, + full_name: foundationUser.full_name, + avatar_url: foundationUser.avatar_url, + profile_completed: foundationUser.profile_complete, + updated_at: new Date().toISOString() +}); +``` + +**Note:** Existing user data is preserved, Foundation data is merged/updated. + +--- + +## Testing + +### Local Testing + +1. **Set up environment:** + ```bash + export VITE_FOUNDATION_URL=http://localhost:3001 # Or staging URL + export FOUNDATION_OAUTH_CLIENT_ID=aethex_corp + export FOUNDATION_OAUTH_CLIENT_SECRET= + ``` + +2. **Test flow:** + - Visit `http://localhost:5173/login` + - Click "Login with Foundation" + - Should redirect to Foundation auth page + - After auth, should return to aethex.dev/auth/callback with code + - Should exchange code and redirect to dashboard + - Check cookies: `foundation_access_token` and `auth_user_id` + +3. **Verify database:** + ```sql + SELECT id, email, username FROM user_profiles WHERE id = ''; + ``` + +### Error Scenarios + +**Invalid code:** +``` +GET /auth/callback?code=invalid +→ 400 "Token exchange failed" +→ Redirect to /login?error=auth_failed +``` + +**Invalid state:** +``` +GET /auth/callback?code=...&state=wrong_state +→ Error "Invalid state token - possible CSRF attack" +→ Redirect to /login?error=auth_failed +``` + +**Foundation down:** +``` +POST /api/oauth/token → ECONNREFUSED +→ Error "Failed to exchange code" +→ Redirect to /login?error=auth_failed +``` + +--- + +## Files Modified/Created + +### New Files +``` +code/ +├── client/ +│ ├── lib/foundation-oauth.ts (PKCE + OAuth flow) +│ ├── lib/foundation-auth.ts (Token/profile management) +│ └── hooks/use-foundation-auth.ts (React hooks for callback) +├── api/ +│ └── auth/callback.ts (OAuth callback handler) +└── .env.foundation-oauth.example (Configuration template) +``` + +### Modified Files +``` +code/ +└── client/ + └── pages/Login.tsx (Added Foundation button) +``` + +### Deprecated Files +These can be removed after testing completes: +``` +code/api/discord/oauth/start.ts +code/api/discord/oauth/callback.ts +code/api/discord/link.ts +code/api/discord/create-linking-session.ts +code/api/discord/verify-code.ts +``` + +--- + +## Deployment Checklist + +- [ ] Environment variables set in deployment platform +- [ ] `FOUNDATION_OAUTH_CLIENT_SECRET` stored securely (not in git) +- [ ] `/auth/callback` route properly configured +- [ ] HTTPS enabled (required for secure cookies) +- [ ] SameSite cookie policies enforced +- [ ] PKCE validation working on Foundation side +- [ ] User profile sync tested +- [ ] Session management tested +- [ ] Error handling tested +- [ ] Old Discord OAuth endpoints disabled (after rollout) + +--- + +## Monitoring + +### Key Metrics + +1. **Auth Success Rate** + - Target: >99% + - Alert if: <95% + +2. **Token Exchange Time** + - Target: <500ms + - Alert if: >2s + +3. **Error Categories** + - Track: invalid_code, state_mismatch, timeout, etc. + +4. **Foundation Connectivity** + - Monitor: /api/oauth/authorize, /api/oauth/token availability + - Alert on: persistent errors from Foundation + +### Logging + +Key points to log: + +``` +[Foundation OAuth] Step 1: Received authorization code +[Foundation OAuth] Step 2: State validation passed +[Foundation OAuth] Step 3: Token exchange initiated +[Foundation OAuth] Step 4: User info fetched +[Foundation OAuth] Step 5: User profile synced +[Foundation OAuth] Step 6: Session established +``` + +--- + +## Troubleshooting + +### Problem: "No authorization code received" + +**Cause:** Foundation didn't redirect back properly + +**Solutions:** +1. Check `redirect_uri` matches exactly (case-sensitive) +2. Verify Foundation OAuth settings +3. Check browser console for JavaScript errors + +### Problem: "Invalid state token" + +**Cause:** CSRF validation failed + +**Solutions:** +1. Check that state is generated consistently +2. Verify sessionStorage isn't cleared between redirect +3. Check for multiple browser tabs (different state per tab) + +### Problem: "Token exchange failed" + +**Cause:** Foundation token endpoint unavailable or code invalid + +**Solutions:** +1. Check Foundation is running and `/api/oauth/token` accessible +2. Verify `client_id` and `client_secret` are correct +3. Check code hasn't expired (usually 10 minutes) +4. Review Foundation logs for errors + +### Problem: User not synced to database + +**Cause:** Database error during sync + +**Solutions:** +1. Check `user_profiles` table exists and has proper schema +2. Verify Supabase connection and permissions +3. Check user_id isn't duplicated in database +4. Review application logs for sync errors + +--- + +## FAQ + +**Q: Why PKCE?** +A: OAuth 2.1 best practice. Prevents interception attacks on the authorization code. + +**Q: Can I use Foundation login on multiple apps?** +A: Yes! Any app with valid credentials can use Foundation for auth. + +**Q: What if Foundation is down?** +A: Users cannot login. Have a maintenance page ready. + +**Q: Do I need to handle token refresh?** +A: Foundation tokens are long-lived. Implement refresh if tokens are short-lived. + +**Q: Can users logout from Foundation and still access Corp?** +A: Yes, they have separate sessions. Eventually their Corp token will expire. + +**Q: What happens to existing Discord connections?** +A: They're managed by Foundation now, not Corp. Users reconnect on Foundation. + +--- + +## Summary + +✅ **aethex.dev is now an OAuth client of aethex.foundation** + +- **Foundation** = Single source of truth for identity (Discord, email, etc.) +- **aethex.dev** = OAuth client that redirects to Foundation for authentication +- **User experience** = Seamless login with unified account across ecosystem +- **Security** = PKCE prevents interception, secure cookie handling +- **Architecture** = Axiom Model achieved - Foundation controls all identities + +--- + +**Status:** ✅ Implementation complete, ready for testing and deployment