AeThex-OS/temp-forge-extract/aethex-forge-main/docs/FOUNDATION-OAUTH-IMPLEMENTATION.md
MrPiglr b3c308b2c8 Add functional marketplace modules, bottom nav bar, root terminal, arcade games
- 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
2026-02-18 22:03:50 -07:00

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

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

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


PKCE Implementation

PKCE (Proof Key for Code Exchange) adds extra security to OAuth flows.

How PKCE Works

  1. Client generates code verifier:

    verifier = randomString(64 chars, URL-safe)
    // Example: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"
    
  2. Client generates code challenge:

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


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:

  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:

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

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

    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_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