- ModuleManager: Central tracking for installed marketplace modules - DataAnalyzerWidget: Real-time CPU/RAM/Battery/Storage widget (unlocked by Data Analyzer module) - BottomNavBar: Navigation bar for Projects/Chat/Marketplace/Settings - RootShell: Real root command execution utility - TerminalActivity: Full root shell with neofetch, sysinfo, real Linux commands - Terminal Pro module: Adds aliases (ll, la, h), command history - ArcadeActivity + SnakeGame: Pixel Arcade module unlocks retro games - fade_in/fade_out animations for smooth transitions
15 KiB
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):
# 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=<csrf_token>
&code_challenge=<pkce_challenge>
&code_challenge_method=S256
Parameters:
client_id- aethex_corp (identifies this app)redirect_uri- Where Foundation redirects after authresponse_type- Always "code" (OAuth 2.0 authorization code flow)scope- Requested permissionsstate- 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=<auth_code_from_callback>
&redirect_uri=https://aethex.dev/auth/callback
&client_id=aethex_corp
&client_secret=<stored_securely_on_backend>
Response:
{
"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 <access_token>
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
-
Client generates code verifier:
verifier = randomString(64 chars, URL-safe) // Example: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~" -
Client generates code challenge:
challenge = base64url(SHA256(verifier)); -
Client sends challenge with authorization request:
GET /api/oauth/authorize?...&code_challenge=...&code_challenge_method=S256 -
Server stores challenge, validates with verifier on token exchange:
POST /api/oauth/token?...&code_verifier=... -
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:
<Button onClick={() => initiateFoundationLogin()}>
<Shield /> Login with Foundation
</Button>
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:
// Generate PKCE parameters
async function generatePKCEParams(): Promise<{ verifier; challenge }>;
// Build authorization URL
async function getFoundationAuthorizationUrl(options?): Promise<string>;
// Initiate login
async function initiateFoundationLogin(redirectTo?: string): Promise<void>;
// Exchange code for token (called from backend)
async function exchangeCodeForToken(code: string): Promise<TokenResponse>;
3. useFoundationAuth Hook (code/client/hooks/use-foundation-auth.ts)
Handles OAuth callback:
// 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:
- Receive authorization code from Foundation
- Validate state token (CSRF protection)
- Exchange code for access token
- Fetch user info from Foundation
- Sync user to local database
- Set session cookies
- Redirect to dashboard
Code:
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:
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: <jwt_token>
│ └── HttpOnly, Secure, SameSite=Strict
│ └── Max-Age: from expires_in
│
└── auth_user_id: <user_uuid>
└── Secure, SameSite=Strict
└── Max-Age: 30 days
Using Access Token
For authenticated API requests:
// 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:
// 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
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
-
Set up environment:
export VITE_FOUNDATION_URL=http://localhost:3001 # Or staging URL export FOUNDATION_OAUTH_CLIENT_ID=aethex_corp export FOUNDATION_OAUTH_CLIENT_SECRET=<secret> -
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_tokenandauth_user_id
- Visit
-
Verify database:
SELECT id, email, username FROM user_profiles WHERE id = '<user_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_SECRETstored securely (not in git)/auth/callbackroute 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
-
Auth Success Rate
- Target: >99%
- Alert if: <95%
-
Token Exchange Time
- Target: <500ms
- Alert if: >2s
-
Error Categories
- Track: invalid_code, state_mismatch, timeout, etc.
-
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:
- Check
redirect_urimatches exactly (case-sensitive) - Verify Foundation OAuth settings
- Check browser console for JavaScript errors
Problem: "Invalid state token"
Cause: CSRF validation failed
Solutions:
- Check that state is generated consistently
- Verify sessionStorage isn't cleared between redirect
- Check for multiple browser tabs (different state per tab)
Problem: "Token exchange failed"
Cause: Foundation token endpoint unavailable or code invalid
Solutions:
- Check Foundation is running and
/api/oauth/tokenaccessible - Verify
client_idandclient_secretare correct - Check code hasn't expired (usually 10 minutes)
- Review Foundation logs for errors
Problem: User not synced to database
Cause: Database error during sync
Solutions:
- Check
user_profilestable exists and has proper schema - Verify Supabase connection and permissions
- Check user_id isn't duplicated in database
- 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