Prettier format pending files

This commit is contained in:
Builder.io 2025-11-17 02:27:28 +00:00
parent ab154def0e
commit e141e68ef4
16 changed files with 512 additions and 251 deletions

View file

@ -11,7 +11,8 @@
import { VercelRequest, VercelResponse } from "@vercel/node";
import { getAdminClient } from "../_supabase";
const FOUNDATION_URL = process.env.VITE_FOUNDATION_URL || "https://aethex.foundation";
const FOUNDATION_URL =
process.env.VITE_FOUNDATION_URL || "https://aethex.foundation";
const CLIENT_ID = process.env.FOUNDATION_OAUTH_CLIENT_ID || "aethex_corp";
const CLIENT_SECRET = process.env.FOUNDATION_OAUTH_CLIENT_SECRET;
const API_BASE = process.env.VITE_API_BASE || "https://aethex.dev";
@ -47,14 +48,18 @@ export async function handleCallback(req: VercelRequest, res: VercelResponse) {
// Handle Foundation errors
if (error) {
const message = error_description ? decodeURIComponent(String(error_description)) : String(error);
const message = error_description
? decodeURIComponent(String(error_description))
: String(error);
return res.redirect(
`/login?error=${error}&message=${encodeURIComponent(message)}`,
);
}
if (!code) {
return res.redirect(`/login?error=no_code&message=${encodeURIComponent("No authorization code received")}`);
return res.redirect(
`/login?error=no_code&message=${encodeURIComponent("No authorization code received")}`,
);
}
try {
@ -64,7 +69,9 @@ export async function handleCallback(req: VercelRequest, res: VercelResponse) {
console.warn("[Foundation OAuth] Missing state parameter");
}
console.log("[Foundation OAuth] Received authorization code, initiating token exchange");
console.log(
"[Foundation OAuth] Received authorization code, initiating token exchange",
);
// Store code in a temporary location for the exchange endpoint
// In a real implementation, you'd use a temporary token or session
@ -75,7 +82,9 @@ export async function handleCallback(req: VercelRequest, res: VercelResponse) {
}
// Fetch user information from Foundation
const userInfo = await fetchUserInfoFromFoundation(exchangeResult.accessToken);
const userInfo = await fetchUserInfoFromFoundation(
exchangeResult.accessToken,
);
// Sync user to local database
await syncUserToLocalDatabase(userInfo);
@ -89,11 +98,12 @@ export async function handleCallback(req: VercelRequest, res: VercelResponse) {
console.log("[Foundation OAuth] User authenticated:", userInfo.id);
// Redirect to dashboard (or stored destination)
const redirectTo = req.query.redirect_to as string || "/dashboard";
const redirectTo = (req.query.redirect_to as string) || "/dashboard";
return res.redirect(redirectTo);
} catch (error) {
console.error("[Foundation OAuth] Callback error:", error);
const message = error instanceof Error ? error.message : "Authentication failed";
const message =
error instanceof Error ? error.message : "Authentication failed";
return res.redirect(
`/login?error=auth_failed&message=${encodeURIComponent(message)}`,
);
@ -105,7 +115,10 @@ export async function handleCallback(req: VercelRequest, res: VercelResponse) {
* Exchange authorization code for access token
* Called from frontend
*/
export async function handleTokenExchange(req: VercelRequest, res: VercelResponse) {
export async function handleTokenExchange(
req: VercelRequest,
res: VercelResponse,
) {
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
}
@ -120,7 +133,9 @@ export async function handleTokenExchange(req: VercelRequest, res: VercelRespons
const exchangeResult = await performTokenExchange(code);
// Fetch user information from Foundation
const userInfo = await fetchUserInfoFromFoundation(exchangeResult.accessToken);
const userInfo = await fetchUserInfoFromFoundation(
exchangeResult.accessToken,
);
// Sync user to local database
await syncUserToLocalDatabase(userInfo);
@ -131,7 +146,10 @@ export async function handleTokenExchange(req: VercelRequest, res: VercelRespons
`auth_user_id=${userInfo.id}; Path=/; Secure; SameSite=Strict; Max-Age=2592000`,
]);
console.log("[Foundation OAuth] Token exchange successful for user:", userInfo.id);
console.log(
"[Foundation OAuth] Token exchange successful for user:",
userInfo.id,
);
return res.status(200).json({
accessToken: exchangeResult.accessToken,
@ -139,7 +157,8 @@ export async function handleTokenExchange(req: VercelRequest, res: VercelRespons
});
} catch (error) {
console.error("[Foundation OAuth] Token exchange error:", error);
const message = error instanceof Error ? error.message : "Token exchange failed";
const message =
error instanceof Error ? error.message : "Token exchange failed";
return res.status(400).json({ error: message });
}
}
@ -147,9 +166,7 @@ export async function handleTokenExchange(req: VercelRequest, res: VercelRespons
/**
* Exchange authorization code for access token with Foundation
*/
async function performTokenExchange(
code: string,
): Promise<{
async function performTokenExchange(code: string): Promise<{
accessToken: string;
tokenType: string;
expiresIn: number;
@ -205,7 +222,9 @@ async function performTokenExchange(
/**
* Fetch user information from Foundation using access token
*/
async function fetchUserInfoFromFoundation(accessToken: string): Promise<FoundationUserInfo> {
async function fetchUserInfoFromFoundation(
accessToken: string,
): Promise<FoundationUserInfo> {
const userInfoEndpoint = `${FOUNDATION_URL}/api/oauth/userinfo`;
console.log("[Foundation OAuth] Fetching user info from:", userInfoEndpoint);
@ -234,15 +253,18 @@ async function fetchUserInfoFromFoundation(accessToken: string): Promise<Foundat
/**
* Sync Foundation user to local database
*/
async function syncUserToLocalDatabase(foundationUser: FoundationUserInfo): Promise<void> {
async function syncUserToLocalDatabase(
foundationUser: FoundationUserInfo,
): Promise<void> {
const supabase = getAdminClient();
console.log("[Foundation OAuth] Syncing user to local database:", foundationUser.id);
console.log(
"[Foundation OAuth] Syncing user to local database:",
foundationUser.id,
);
// Upsert user profile
const { error } = await supabase
.from("user_profiles")
.upsert({
const { error } = await supabase.from("user_profiles").upsert({
id: foundationUser.id,
email: foundationUser.email,
username: foundationUser.username || null,
@ -257,7 +279,10 @@ async function syncUserToLocalDatabase(foundationUser: FoundationUserInfo): Prom
throw new Error("Failed to create local user profile");
}
console.log("[Foundation OAuth] User synced successfully:", foundationUser.id);
console.log(
"[Foundation OAuth] User synced successfully:",
foundationUser.id,
);
}
/**

View file

@ -7,13 +7,11 @@
import { VercelRequest, VercelResponse } from "@vercel/node";
const FOUNDATION_URL = process.env.VITE_FOUNDATION_URL || "https://aethex.foundation";
const FOUNDATION_URL =
process.env.VITE_FOUNDATION_URL || "https://aethex.foundation";
const API_BASE = process.env.VITE_API_BASE || "https://aethex.dev";
export default async function handler(
req: VercelRequest,
res: VercelResponse,
) {
export default async function handler(req: VercelRequest, res: VercelResponse) {
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
}

View file

@ -8,7 +8,8 @@
import { getAdminClient } from "../_supabase";
import { VercelRequest, VercelResponse } from "@vercel/node";
const FOUNDATION_URL = process.env.VITE_FOUNDATION_URL || "https://aethex.foundation";
const FOUNDATION_URL =
process.env.VITE_FOUNDATION_URL || "https://aethex.foundation";
const API_BASE = process.env.VITE_API_BASE || "https://aethex.dev";
interface FoundationTokenResponse {
@ -23,10 +24,7 @@ interface FoundationTokenResponse {
};
}
export default async function handler(
req: VercelRequest,
res: VercelResponse,
) {
export default async function handler(req: VercelRequest, res: VercelResponse) {
if (req.method !== "GET") {
return res.status(405).json({ error: "Method not allowed" });
}
@ -36,11 +34,15 @@ export default async function handler(
// Handle Foundation errors
if (error) {
const errorDesc = req.query.error_description || error;
return res.redirect(`/login?error=${error}&message=${encodeURIComponent(String(errorDesc))}`);
return res.redirect(
`/login?error=${error}&message=${encodeURIComponent(String(errorDesc))}`,
);
}
if (!code) {
return res.redirect("/login?error=no_code&message=Authorization code not received");
return res.redirect(
"/login?error=no_code&message=Authorization code not received",
);
}
try {
@ -74,7 +76,7 @@ export default async function handler(
const errorData = await tokenResponse.json().catch(() => ({}));
console.error("[Foundation OAuth] Token exchange failed:", errorData);
return res.redirect(
`/login?error=token_exchange&message=${encodeURIComponent("Failed to exchange authorization code")}`
`/login?error=token_exchange&message=${encodeURIComponent("Failed to exchange authorization code")}`,
);
}
@ -82,7 +84,9 @@ export default async function handler(
if (!tokenData.access_token || !tokenData.user) {
console.error("[Foundation OAuth] Invalid token response");
return res.redirect("/login?error=invalid_token&message=Invalid token response");
return res.redirect(
"/login?error=invalid_token&message=Invalid token response",
);
}
// Extract user information from Foundation response
@ -101,12 +105,17 @@ export default async function handler(
if (fetchError && fetchError.code !== "PGRST116") {
// PGRST116 = no rows found (expected for new users)
console.error("[Foundation OAuth] Error fetching user profile:", fetchError);
console.error(
"[Foundation OAuth] Error fetching user profile:",
fetchError,
);
}
if (!existingProfile) {
// Create user profile from Foundation data
const { error: createError } = await supabase.from("user_profiles").insert({
const { error: createError } = await supabase
.from("user_profiles")
.insert({
id: user.id,
email: user.email,
username: user.username || null,
@ -117,9 +126,12 @@ export default async function handler(
});
if (createError) {
console.error("[Foundation OAuth] Failed to create user profile:", createError);
console.error(
"[Foundation OAuth] Failed to create user profile:",
createError,
);
return res.redirect(
`/login?error=profile_create&message=${encodeURIComponent("Failed to create local user profile")}`
`/login?error=profile_create&message=${encodeURIComponent("Failed to create local user profile")}`,
);
}
}
@ -138,7 +150,7 @@ export default async function handler(
} catch (error) {
console.error("[Foundation OAuth] Callback error:", error);
return res.redirect(
`/login?error=unknown&message=${encodeURIComponent("An unexpected error occurred")}`
`/login?error=unknown&message=${encodeURIComponent("An unexpected error occurred")}`,
);
}
}

View file

@ -72,7 +72,10 @@ export function useFoundationAuth(): UseFoundationAuthReturn {
throw new Error("Invalid response from token exchange");
}
console.log("[Foundation Auth] Token exchange successful for user:", tokenData.user.id);
console.log(
"[Foundation Auth] Token exchange successful for user:",
tokenData.user.id,
);
// Clear auth parameters from URL and storage
const url = new URL(window.location.href);
@ -95,16 +98,21 @@ export function useFoundationAuth(): UseFoundationAuthReturn {
// Redirect to dashboard or stored destination
navigate(redirectTo, { replace: true });
} catch (exchangeError) {
const message = exchangeError instanceof Error
const message =
exchangeError instanceof Error
? exchangeError.message
: "Failed to exchange authorization code";
console.error("[Foundation Auth] Token exchange failed:", exchangeError);
console.error(
"[Foundation Auth] Token exchange failed:",
exchangeError,
);
throw new Error(message);
}
} catch (err) {
const message = err instanceof Error ? err.message : "Authentication failed";
const message =
err instanceof Error ? err.message : "Authentication failed";
setError(message);
console.error("[Foundation Auth] Callback processing error:", err);
@ -150,7 +158,9 @@ export function useFoundationAuthStatus(): {
useEffect(() => {
// Check for foundation_access_token cookie
const cookies = document.cookie.split(";").map((c) => c.trim());
const tokenCookie = cookies.find((c) => c.startsWith("foundation_access_token="));
const tokenCookie = cookies.find((c) =>
c.startsWith("foundation_access_token="),
);
const userCookie = cookies.find((c) => c.startsWith("auth_user_id="));
if (tokenCookie && userCookie) {

View file

@ -50,8 +50,10 @@ export function clearFoundationAuth(): void {
if (typeof window === "undefined") return;
// Clear cookies by setting expiration to past
document.cookie = "foundation_access_token=; Path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;";
document.cookie = "auth_user_id=; Path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;";
document.cookie =
"foundation_access_token=; Path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;";
document.cookie =
"auth_user_id=; Path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;";
// Clear session storage
sessionStorage.removeItem("oauth_code_verifier");
@ -89,7 +91,8 @@ export async function makeAuthenticatedRequest(
* Clears local auth state and optionally notifies Foundation
*/
export async function logoutFromFoundation(): Promise<void> {
const FOUNDATION_URL = import.meta.env.VITE_FOUNDATION_URL || "https://aethex.foundation";
const FOUNDATION_URL =
import.meta.env.VITE_FOUNDATION_URL || "https://aethex.foundation";
const token = getFoundationAccessToken();
// Clear local auth

View file

@ -10,8 +10,10 @@
* - GET /api/oauth/userinfo - User info endpoint
*/
const FOUNDATION_URL = import.meta.env.VITE_FOUNDATION_URL || "https://aethex.foundation";
const CLIENT_ID = import.meta.env.VITE_FOUNDATION_OAUTH_CLIENT_ID || "aethex_corp";
const FOUNDATION_URL =
import.meta.env.VITE_FOUNDATION_URL || "https://aethex.foundation";
const CLIENT_ID =
import.meta.env.VITE_FOUNDATION_OAUTH_CLIENT_ID || "aethex_corp";
const API_BASE = import.meta.env.VITE_API_BASE || "https://aethex.dev";
/**
@ -19,7 +21,8 @@ const API_BASE = import.meta.env.VITE_API_BASE || "https://aethex.dev";
* Must be 43-128 characters, URL-safe (A-Z, a-z, 0-9, -, ., _, ~)
*/
function generateCodeVerifier(): string {
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
const charset =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
const length = 64;
let verifier = "";
const randomValues = new Uint8Array(length);
@ -104,7 +107,9 @@ export async function getFoundationAuthorizationUrl(options?: {
/**
* Initiate the Foundation OAuth login flow
*/
export async function initiateFoundationLogin(redirectTo?: string): Promise<void> {
export async function initiateFoundationLogin(
redirectTo?: string,
): Promise<void> {
try {
const authUrl = await getFoundationAuthorizationUrl({ redirectTo });
window.location.href = authUrl;

View file

@ -1,4 +1,5 @@
# Axiom Model: Phase 1 Code Migration Scope
## Moving Identity from aethex.dev (Vercel) to aethex.foundation (Replit)
**Status:** CRITICAL P0 (Blocks NEXUS & FOUNDATION work)
@ -10,31 +11,34 @@
## 1. PAGES TO COPY
### Authentication & Onboarding Pages
| File | Purpose | Notes |
|------|---------|-------|
| ------------------------------------- | ----------------------------------- | ------------------------------------------- |
| `code/client/pages/Login.tsx` | Login UI + Discord OAuth button | Copy as-is; validate Discord button routing |
| `code/client/pages/Signup.tsx` | (if exists) User registration | Copy if present |
| `code/client/pages/Onboarding.tsx` | Realm/arm selection, profile setup | Copy all onboarding flow |
| `code/client/pages/DiscordVerify.tsx` | Verification code entry for linking | Copy verification flow |
### Profile & Settings Pages
| File | Purpose | Notes |
|------|---------|-------|
| --------------------------------- | -------------------------------------- | --------------------------- |
| `code/client/pages/Profile.tsx` | (or Dashboard) User profile view | Copy public profile viewing |
| `code/client/pages/Dashboard.tsx` | User dashboard + OAuthConnections | Copy OAuth linking UI |
| `code/client/pages/settings/*` | Profile settings, password reset, etc. | Copy all settings pages |
### Passport Pages
| File | Purpose | Notes |
|------|---------|-------|
| `code/client/pages/SubdomainPassport.tsx` | Creator passport for *.aethex.me | Copy; will fetch from Foundation API |
| ----------------------------------------- | --------------------------------- | ------------------------------------ |
| `code/client/pages/SubdomainPassport.tsx` | Creator passport for \*.aethex.me | Copy; will fetch from Foundation API |
---
## 2. CONTEXTS & STATE MANAGEMENT
| File | Purpose | Dependencies |
|------|---------|--------------|
| ------------------------------------------------- | ----------------------------------------------- | ------------------------------------ |
| `code/client/contexts/AuthContext.tsx` | Central auth state, loginProvider, linkProvider | Depends on Supabase client |
| `code/client/contexts/DiscordActivityContext.tsx` | Discord Activity SDK state | Optional; copy if Activity is needed |
| `code/client/contexts/ThemeContext.tsx` | Theme switching | Dependency; copy |
@ -44,22 +48,25 @@
## 3. COMPONENTS TO COPY
### Auth & OAuth Components
| File | Purpose |
|------|---------|
| --------------------------------------------------------- | ------------------------------------------------- |
| `code/client/components/settings/OAuthConnections.tsx` | OAuth provider cards (Discord, etc.) |
| `code/client/components/admin/AdminDiscordManagement.tsx` | Admin UI for role mappings (optional for Phase 1) |
### Profile & Passport Components
| File | Purpose |
|------|---------|
| ----------------------------------------------------- | ------------------------ |
| `code/client/components/passport/PassportSummary.tsx` | Renders creator passport |
| `code/client/components/ErrorBoundary.tsx` | Error handling |
| `code/client/components/LoadingScreen.tsx` | Loading UI |
| `code/client/components/Layout.tsx` | App layout & header |
### Shared UI Components
| Directory | Purpose |
|-----------|---------|
| ----------------------------- | --------------------------------------- |
| `code/client/components/ui/*` | All Radix UI & design system components |
---
@ -67,8 +74,9 @@
## 4. API ENDPOINTS & SERVERLESS FUNCTIONS TO COPY
### Discord OAuth Endpoints
| File | Endpoint | Purpose |
|------|----------|---------|
| ------------------------------------ | --------------------------------- | ---------------------------------- |
| `code/api/discord/oauth/start.ts` | `GET /api/discord/oauth/start` | Redirect to Discord authorization |
| `code/api/discord/oauth/callback.ts` | `GET /api/discord/oauth/callback` | Handle Discord callback, link user |
| `code/api/discord/verify-code.ts` | `POST /api/discord/verify-code` | Verify 6-digit code for linking |
@ -76,14 +84,16 @@
| `code/api/discord/sync-roles.ts` | `POST /api/discord/sync-roles` | Assign Discord roles after linking |
### Profile & Auth Endpoints
| File | Endpoint | Purpose |
|------|----------|---------|
| ---------------------------- | -------------------------- | ------------------------------------------ |
| `code/api/profile/ensure.ts` | `POST /api/profile/ensure` | Create or ensure user profile exists |
| `code/api/user/*` | Various | User data endpoints (review for auth deps) |
### Passport Endpoints
| File | Endpoint | Purpose |
|------|----------|---------|
| ------------------------------------------- | --------------------------------------- | ------------------------- |
| `code/api/passport/subdomain/[username].ts` | `GET /api/passport/subdomain/:username` | Creator passport JSON API |
| `code/api/passport/project/[slug].ts` | `GET /api/passport/project/:slug` | Project passport JSON API |
@ -92,11 +102,12 @@
## 5. DATABASE MIGRATIONS TO COPY
| File | Purpose |
|------|---------|
| --------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `code/supabase/migrations/20250107_add_discord_integration.sql` | Discord tables (discord_links, discord_verifications, discord_role_mappings) |
| All other user/auth-related migrations | Copy all identity-related schema |
**Supabase Tables Required:**
- `user_profiles`
- `user_auth_identities`
- `discord_links`
@ -108,6 +119,7 @@
## 6. LIBRARIES & DEPENDENCIES
### Required npm packages (verify in aethex.dev package.json)
```json
{
"@supabase/supabase-js": "^2.x",
@ -124,6 +136,7 @@
```
### Environment Variables Needed
```
VITE_SUPABASE_URL=https://kmdeisowhtsalsekkzqd.supabase.co
VITE_SUPABASE_ANON_KEY=sb_publishable_...
@ -139,13 +152,14 @@ VITE_API_BASE=https://aethex.foundation (after switchover)
## 7. CRITICAL ADAPTATIONS FOR REPLIT TARGET
| Current (aethex.dev) | Needed for aethex.foundation |
|----------------------|------------------------------|
| ------------------------------------------ | ------------------------------------------- |
| Vercel serverless functions (`code/api/*`) | Express or Remix server endpoints on Replit |
| `VITE_API_BASE=https://aethex.dev` | `VITE_API_BASE=https://aethex.foundation` |
| Vite + React on Vercel | Vite + React on Replit (same) |
| Uses Vercel environment variables | Use Replit Secrets or .env |
### Key Refactoring Points
1. **API Endpoints:** Vercel's `/api/*` files may need conversion to Express routes in `code/server/index.ts` or equivalent Replit server.
2. **Base URLs:** Update all `VITE_API_BASE` references to point to `aethex.foundation` instead of `aethex.dev`.
3. **OAuth Redirect URIs:** Update Discord OAuth app to use `aethex.foundation` callback URL.
@ -158,6 +172,7 @@ VITE_API_BASE=https://aethex.foundation (after switchover)
After copying existing code, build 3 new OAuth 2.0 endpoints on aethex.foundation:
### 1. `/authorize` (Foundation SSO Authorization)
**Purpose:** Initiate login flow for external apps (aethex.dev)
```
@ -167,6 +182,7 @@ GET /authorize?client_id=AETHEX_DEV&redirect_uri=https://aethex.dev/auth/callbac
**Response:** Redirect user to `/login` with state preserved
### 2. `/token` (Foundation SSO Token Exchange)
**Purpose:** Exchange auth code for JWT token
```
@ -188,6 +204,7 @@ Returns:
```
### 3. `/userinfo` (Foundation SSO User Info)
**Purpose:** Fetch current logged-in user info (used by aethex.dev after login)
```
@ -211,6 +228,7 @@ Returns:
## 9. MIGRATION CHECKLIST
### Before Starting Phase 1
- [ ] Verify all auth code is in `code/client/pages/` and `code/api/discord/*`
- [ ] List all custom hooks used in auth flow (use-toast, etc.)
- [ ] Document all Supabase queries used for auth
@ -218,10 +236,11 @@ Returns:
- [ ] Create a "mirror" directory structure on aethex.foundation (Replit)
### During Phase 1
- [ ] Copy all page files (Login, Signup, Onboarding, Dashboard, etc.)
- [ ] Copy all context files (AuthContext, DiscordActivityContext, ThemeContext)
- [ ] Copy all component files (OAuthConnections, PassportSummary, etc.)
- [ ] Copy all API endpoint files (discord/oauth/*, profile/ensure.ts, passport/*)
- [ ] Copy all API endpoint files (discord/oauth/_, profile/ensure.ts, passport/_)
- [ ] Copy all Supabase migrations
- [ ] Copy tailwind.config.js and global.css for styling
- [ ] Adapt all import paths for new directory structure
@ -230,6 +249,7 @@ Returns:
- [ ] Set up environment variables on Replit
### Testing Phase 1
- [ ] Can users log in via Discord on aethex.foundation?
- [ ] Can users view their profile?
- [ ] Can users link additional OAuth providers?
@ -254,7 +274,7 @@ Returns:
## 11. ESTIMATED EFFORT
| Task | Estimate |
|------|----------|
| -------------------------- | --------------- |
| Audit & document auth code | 2-3 hours |
| Copy & adapt page files | 4-6 hours |
| Copy & adapt API endpoints | 3-4 hours |

View file

@ -20,6 +20,7 @@ FOUNDATION_OAUTH_CLIENT_SECRET=bcoEtyQVGr6Z4557658eUXpDF5FDni2TGNahH3HT-FtylNrLC
```
**Important:**
- ✅ Keep `FOUNDATION_OAUTH_CLIENT_SECRET` **secure** (never commit to git)
- ✅ Use deployment platform's secret management (Vercel > Settings > Environment Variables)
- ✅ Mark secret variables as "Encrypted"
@ -35,6 +36,7 @@ Before deploying, confirm:
- [ ] OAuth credentials valid (client_id, client_secret)
**Quick Test:**
```bash
# Test token endpoint
curl -X POST https://aethex.foundation/api/oauth/token \
@ -62,6 +64,7 @@ Ask Foundation admin to verify these are registered.
### Step 1: Set Environment Variables
**Vercel:**
1. Go to Project Settings > Environment Variables
2. Add three variables:
- `VITE_FOUNDATION_URL` = `https://aethex.foundation`
@ -71,6 +74,7 @@ Ask Foundation admin to verify these are registered.
4. Save
**Railway/Other:**
- Add to `.env` file in deployment
- Or configure in platform's settings
- Restart deployment for changes to take effect
@ -98,6 +102,7 @@ code/
```
**Deploy command:**
```bash
# For Vercel
vercel deploy --prod
@ -114,22 +119,26 @@ docker push <registry>/aethex-dev
### Step 3: Verify Deployment
1. **Check environment variables:**
```bash
# On deployed app, check logs for env var loading
# Should see Foundation URL in console (not secret though!)
```
2. **Visit login page:**
- Go to https://aethex.dev/login
- Should see "Login with Foundation" button
- No console errors
3. **Test OAuth flow:**
- Click "Login with Foundation"
- Should redirect to https://aethex.foundation/api/oauth/authorize
- Page should show Foundation login (or auth screen)
4. **Check callback endpoint:**
- Network tab should show POST to `/auth/callback/exchange`
- Should return 200 with access_token
@ -162,6 +171,7 @@ ECONNREFUSED ⚠️ Foundation unreachabl
### Test 1: Happy Path (Successful Login)
**Steps:**
1. Visit https://aethex.dev/login
2. Click "Login with Foundation"
3. Enter test credentials on Foundation
@ -172,6 +182,7 @@ ECONNREFUSED ⚠️ Foundation unreachabl
**Expected Result:** ✅ Logged in, cookies set, profile synced
**Check:**
```bash
# In browser console:
document.cookie # Should show foundation_access_token
@ -184,6 +195,7 @@ SELECT * FROM user_profiles WHERE email = '<test-user-email>';
### Test 2: Error: Invalid Code
**Steps:**
1. Manually modify callback URL: `?code=invalid_code_123`
2. Press Enter
@ -192,6 +204,7 @@ SELECT * FROM user_profiles WHERE email = '<test-user-email>';
### Test 3: Network Error
**Steps:**
1. Stop/pause Foundation service
2. Attempt login
3. Foundation redirects back with code
@ -202,6 +215,7 @@ SELECT * FROM user_profiles WHERE email = '<test-user-email>';
### Test 4: Logout and Re-login
**Steps:**
1. Logout from dashboard (if logout button exists)
2. Check cookies are cleared
3. Login again with Foundation
@ -212,6 +226,7 @@ SELECT * FROM user_profiles WHERE email = '<test-user-email>';
### Test 5: Multiple Browsers
Test on:
- [ ] Chrome/Chromium
- [ ] Firefox
- [ ] Safari
@ -255,6 +270,7 @@ After 1-2 weeks of successful deployment:
1. Remove old Discord OAuth code (optional)
2. Delete deprecated files:
- `code/api/discord/oauth/start.ts`
- `code/api/discord/oauth/callback.ts`
- `code/api/discord/link.ts`
@ -273,6 +289,7 @@ If critical issues occur:
### Immediate Rollback (< 1 hour)
1. **Revert deployment:**
```bash
# Vercel
vercel rollback
@ -282,6 +299,7 @@ If critical issues occur:
```
2. **Remove environment variables:**
- Remove VITE_FOUNDATION_URL
- Remove FOUNDATION_OAUTH_CLIENT_ID
- Remove FOUNDATION_OAUTH_CLIENT_SECRET
@ -293,6 +311,7 @@ If critical issues occur:
### If Rollback Fails
Contact Foundation admin for assistance with:
- OAuth endpoint status
- User session validation
- Database consistency
@ -304,16 +323,19 @@ Contact Foundation admin for assistance with:
### Key Metrics to Monitor
1. **Auth Success Rate**
- Target: >99%
- Alert threshold: <95%
- What to check: Logs for "Token exchange" errors
2. **Token Exchange Time**
- Target: <500ms
- Alert threshold: >2000ms
- What to check: Network latency to Foundation
3. **Foundation Connectivity**
- Monitor: Foundation endpoint availability
- Alert on: Connection failures to /api/oauth/token
- Fallback: Maintenance page if Foundation down
@ -334,7 +356,7 @@ const metrics = {
failed_token_exchange: 5,
failed_user_sync: 2,
failed_state_validation: 3,
avg_token_exchange_time_ms: 234
avg_token_exchange_time_ms: 234,
};
```
@ -345,7 +367,7 @@ const metrics = {
### Common Issues During Deployment
| Issue | Cause | Solution |
|-------|-------|----------|
| --------------------------- | -------------------------------- | ---------------------------------- |
| "Client secret not found" | Missing env var | Add FOUNDATION_OAUTH_CLIENT_SECRET |
| "Redirect URI mismatch" | URI not registered on Foundation | Ask Foundation admin to register |
| "Token exchange failed 401" | Invalid credentials | Verify client_id and client_secret |
@ -371,12 +393,14 @@ psql -c "SELECT COUNT(*) FROM user_profiles;"
### Getting Help
1. **Check logs:**
- Deployment platform logs (Vercel Dashboard, Railway Dashboard)
- Application logs (if available)
- Browser console (F12)
- Network tab (check requests/responses)
2. **Verify configuration:**
- Environment variables set correctly
- Foundation endpoints accessible
- Redirect URI registered

View file

@ -71,6 +71,7 @@ GET https://aethex.foundation/api/oauth/authorize
```
**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)
@ -98,6 +99,7 @@ grant_type=authorization_code
```
**Response:**
```json
{
"access_token": "eyJ...",
@ -143,22 +145,26 @@ See `code/api/auth/callback.ts` → `fetchUserInfoFromFoundation()`
### 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))
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=...
```
@ -169,6 +175,7 @@ See `code/api/auth/callback.ts` → `fetchUserInfoFromFoundation()`
```
**Why PKCE?**
- Prevents authorization code interception attacks
- Secure for mobile apps and single-page applications
- Required by OAuth 2.1 best practices
@ -193,6 +200,7 @@ Updated to show Foundation OAuth button:
```
Features:
- Initiates Foundation OAuth flow
- Generates PKCE parameters
- Stores verifier and state in sessionStorage
@ -204,16 +212,16 @@ Core OAuth functionality:
```typescript
// Generate PKCE parameters
async function generatePKCEParams(): Promise<{ verifier, challenge }>
async function generatePKCEParams(): Promise<{ verifier; challenge }>;
// Build authorization URL
async function getFoundationAuthorizationUrl(options?): Promise<string>
async function getFoundationAuthorizationUrl(options?): Promise<string>;
// Initiate login
async function initiateFoundationLogin(redirectTo?: string): Promise<void>
async function initiateFoundationLogin(redirectTo?: string): Promise<void>;
// Exchange code for token (called from backend)
async function exchangeCodeForToken(code: string): Promise<TokenResponse>
async function exchangeCodeForToken(code: string): Promise<TokenResponse>;
```
#### 3. useFoundationAuth Hook (`code/client/hooks/use-foundation-auth.ts`)
@ -235,6 +243,7 @@ useFoundationAuthStatus(): { isAuthenticated, userId }
**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
@ -244,6 +253,7 @@ useFoundationAuthStatus(): { isAuthenticated, userId }
7. Redirect to dashboard
**Code:**
```typescript
async function handleCallback(req, res) {
// 1. Get code from URL
@ -264,7 +274,7 @@ async function handleCallback(req, res) {
// 6. Set cookies
res.setHeader("Set-Cookie", [
`foundation_access_token=${token}; ...`,
`auth_user_id=${user.id}; ...`
`auth_user_id=${user.id}; ...`,
]);
// 7. Redirect
@ -314,16 +324,16 @@ For authenticated API requests:
```typescript
// Get token from cookie
const token = document.cookie
.split(';')
.find(c => c.trim().startsWith('foundation_access_token='))
?.split('=')[1];
.split(";")
.find((c) => c.trim().startsWith("foundation_access_token="))
?.split("=")[1];
// Use in requests
fetch('/api/user/profile', {
fetch("/api/user/profile", {
headers: {
'Authorization': `Bearer ${token}`
Authorization: `Bearer ${token}`,
},
credentials: 'include'
credentials: "include",
});
```
@ -333,13 +343,14 @@ 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;';
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}` }
method: "POST",
headers: { Authorization: `Bearer ${token}` },
});
```
@ -382,7 +393,7 @@ await supabase.from("user_profiles").upsert({
full_name: foundationUser.full_name,
avatar_url: foundationUser.avatar_url,
profile_completed: foundationUser.profile_complete,
updated_at: new Date().toISOString()
updated_at: new Date().toISOString(),
});
```
@ -395,6 +406,7 @@ await supabase.from("user_profiles").upsert({
### Local Testing
1. **Set up environment:**
```bash
export VITE_FOUNDATION_URL=http://localhost:3001 # Or staging URL
export FOUNDATION_OAUTH_CLIENT_ID=aethex_corp
@ -402,6 +414,7 @@ await supabase.from("user_profiles").upsert({
```
2. **Test flow:**
- Visit `http://localhost:5173/login`
- Click "Login with Foundation"
- Should redirect to Foundation auth page
@ -417,6 +430,7 @@ await supabase.from("user_profiles").upsert({
### Error Scenarios
**Invalid code:**
```
GET /auth/callback?code=invalid
→ 400 "Token exchange failed"
@ -424,6 +438,7 @@ GET /auth/callback?code=invalid
```
**Invalid state:**
```
GET /auth/callback?code=...&state=wrong_state
→ Error "Invalid state token - possible CSRF attack"
@ -431,6 +446,7 @@ GET /auth/callback?code=...&state=wrong_state
```
**Foundation down:**
```
POST /api/oauth/token → ECONNREFUSED
→ Error "Failed to exchange code"
@ -442,6 +458,7 @@ POST /api/oauth/token → ECONNREFUSED
## Files Modified/Created
### New Files
```
code/
├── client/
@ -454,6 +471,7 @@ code/
```
### Modified Files
```
code/
└── client/
@ -461,7 +479,9 @@ code/
```
### Deprecated Files
These can be removed after testing completes:
```
code/api/discord/oauth/start.ts
code/api/discord/oauth/callback.ts
@ -492,14 +512,17 @@ code/api/discord/verify-code.ts
### 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**
@ -528,6 +551,7 @@ Key points to log:
**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
@ -537,6 +561,7 @@ Key points to log:
**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)
@ -546,6 +571,7 @@ Key points to log:
**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)
@ -556,6 +582,7 @@ Key points to log:
**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

View file

@ -157,7 +157,11 @@
"source": "code/supabase/migrations/20250107_add_discord_integration.sql",
"type": "migration",
"priority": "high",
"tables": ["discord_links", "discord_verifications", "discord_role_mappings"]
"tables": [
"discord_links",
"discord_verifications",
"discord_role_mappings"
]
}
],
"config": [

View file

@ -1,22 +1,29 @@
# Axiom Model - Phase 1 Migration
## How to Use This Package
You have 3 files to help with the Phase 1 code migration:
### 1. **PHASE1-CHECKLIST.txt** ← START HERE
Simple, easy-to-read checklist of all files to copy.
- ✅ Just check off items as you copy them
- 📋 Best for quick reference while working
- 👥 Share this with your team
### 2. **PHASE1-FILES.json**
Machine-readable list of all files with metadata.
- 🤖 Use this if you want to script/automate the copy
- 📊 Includes priority levels, categories, and notes
- 💻 Parse this in your favorite tool
### 3. **AXIOM-MODEL-PHASE1-SCOPE.md**
Complete detailed documentation.
- 📖 Full explanation of every file and why it matters
- 🎯 Includes adaptation notes and potential blockers
- 🔍 Reference this if you get stuck on something
@ -43,12 +50,15 @@ Complete detailed documentation.
---
## Estimated Time
**17-25 hours** for complete Phase 1 migration
---
## Questions?
Refer to AXIOM-MODEL-PHASE1-SCOPE.md sections:
- **Import issues?** → Section 7: Libraries & Dependencies
- **What file goes where?** → Sections 1-5: Complete file listing
- **How do I adapt?** → Section 7: Critical Adaptations
@ -57,11 +67,14 @@ Refer to AXIOM-MODEL-PHASE1-SCOPE.md sections:
---
## Next: Phase 2
Once Phase 1 is complete, Phase 2 involves:
- Supabase permission migration (Foundation gets full access)
- `aethex.dev` loses direct write access to user_profiles
Then Phase 3:
- Reroute `aethex.dev` login → `aethex.foundation` (SSO)
**Done!** The Axiom Model is live.

View file

@ -58,6 +58,7 @@ Foundation Endpoints:
### New Implementation Files
#### Frontend OAuth Client (`code/client/lib/foundation-oauth.ts`)
✅ **Implements PKCE (Proof Key for Code Exchange)**
- Generates code verifier (64-char random, URL-safe)
@ -68,14 +69,16 @@ Foundation Endpoints:
- Stores verifier/state in sessionStorage
**Key Functions:**
```typescript
getFoundationAuthorizationUrl() // Build auth URL
initiateFoundationLogin() // Redirect to Foundation
exchangeCodeForToken() // Exchange code (called from backend)
validateState() // CSRF validation
getFoundationAuthorizationUrl(); // Build auth URL
initiateFoundationLogin(); // Redirect to Foundation
exchangeCodeForToken(); // Exchange code (called from backend)
validateState(); // CSRF validation
```
#### Token & Cookie Management (`code/client/lib/foundation-auth.ts`)
✅ **Handles session cookies and authentication state**
- Get/check Foundation access token from cookies
@ -85,16 +88,18 @@ validateState() // CSRF validation
- Logout notification to Foundation
**Key Functions:**
```typescript
getFoundationAccessToken() // Get JWT from cookie
getAuthUserId() // Get user UUID from cookie
isFoundationAuthenticated() // Check auth status
clearFoundationAuth() // Logout
makeAuthenticatedRequest() // API call with token
logoutFromFoundation() // Full logout flow
getFoundationAccessToken(); // Get JWT from cookie
getAuthUserId(); // Get user UUID from cookie
isFoundationAuthenticated(); // Check auth status
clearFoundationAuth(); // Logout
makeAuthenticatedRequest(); // API call with token
logoutFromFoundation(); // Full logout flow
```
#### OAuth Callback Hook (`code/client/hooks/use-foundation-auth.ts`)
✅ **Detects OAuth callback and handles token exchange**
- Detects authorization code in URL
@ -105,16 +110,20 @@ logoutFromFoundation() // Full logout flow
- Error handling with user feedback
**Key Functions:**
```typescript
useFoundationAuth() // Process OAuth callback
useFoundationAuthStatus() // Check auth status
useFoundationAuth(); // Process OAuth callback
useFoundationAuthStatus(); // Check auth status
```
#### OAuth Callback Handler (`code/api/auth/callback.ts`)
✅ **Backend endpoint for OAuth flow completion**
**Two routes:**
1. `GET /auth/callback?code=...&state=...`
- Receives authorization code from Foundation
- Validates state (CSRF)
- Exchanges code for token
@ -130,15 +139,17 @@ useFoundationAuthStatus() // Check auth status
- Sets secure cookies
**Key Functions:**
```typescript
handleCallback() // GET /auth/callback
handleTokenExchange() // POST /auth/callback/exchange
performTokenExchange() // Code → token exchange
fetchUserInfoFromFoundation() // Fetch user profile
syncUserToLocalDatabase() // Upsert to local DB
handleCallback(); // GET /auth/callback
handleTokenExchange(); // POST /auth/callback/exchange
performTokenExchange(); // Code → token exchange
fetchUserInfoFromFoundation(); // Fetch user profile
syncUserToLocalDatabase(); // Upsert to local DB
```
#### Updated Login Page (`code/client/pages/Login.tsx`)
✅ **New Foundation OAuth button**
- Added "Login with Foundation" button (primary option)
@ -147,6 +158,7 @@ syncUserToLocalDatabase() // Upsert to local DB
- Discord now managed by Foundation instead
**Changes:**
```typescript
// NEW
<Button onClick={() => initiateFoundationLogin()}>
@ -160,6 +172,7 @@ syncUserToLocalDatabase() // Upsert to local DB
### Configuration Files
#### Example Environment Variables (`.env.foundation-oauth.example`)
```bash
VITE_FOUNDATION_URL=https://aethex.foundation
FOUNDATION_OAUTH_CLIENT_ID=aethex_corp
@ -171,6 +184,7 @@ FOUNDATION_OAUTH_CLIENT_SECRET=bcoEtyQVGr6Z4557658eUXpDF5FDni2TGNahH3HT-FtylNrLC
✅ **Complete Documentation Provided:**
1. **FOUNDATION-OAUTH-IMPLEMENTATION.md** (601 lines)
- Complete technical guide
- PKCE explanation
- All endpoints documented
@ -335,20 +349,20 @@ document.cookie
const token = getFoundationAccessToken();
// Make authenticated request
fetch('/api/user/profile', {
headers: { 'Authorization': `Bearer ${token}` },
credentials: 'include' // Include cookies
fetch("/api/user/profile", {
headers: { Authorization: `Bearer ${token}` },
credentials: "include", // Include cookies
});
// Or use helper
import { makeAuthenticatedRequest } from '@/lib/foundation-auth';
const response = await makeAuthenticatedRequest('/api/user/profile');
import { makeAuthenticatedRequest } from "@/lib/foundation-auth";
const response = await makeAuthenticatedRequest("/api/user/profile");
```
### Logout
```typescript
import { logoutFromFoundation } from '@/lib/foundation-auth';
import { logoutFromFoundation } from "@/lib/foundation-auth";
// Logout button click handler
await logoutFromFoundation();
@ -404,7 +418,7 @@ await supabase.from("user_profiles").upsert({
full_name: foundationUser.full_name,
avatar_url: foundationUser.avatar_url,
profile_completed: foundationUser.profile_complete,
updated_at: new Date().toISOString()
updated_at: new Date().toISOString(),
});
// Result:
@ -495,7 +509,7 @@ Why? Foundation now handles all Discord OAuth
## Key Differences from Before
| Aspect | Before Phase 3 | After Phase 3 |
|--------|---|---|
| ---------------------- | --------------------- | ------------------------------------------ |
| **Identity Provider** | aethex.dev (local) | aethex.foundation (remote) |
| **Discord OAuth** | Handled on aethex.dev | Handled on Foundation |
| **Session Token** | Supabase JWT | Foundation JWT |
@ -529,7 +543,9 @@ Phase 3 is **successfully deployed when:**
## Documentation Provided
### Implementation Guide
📖 **`FOUNDATION-OAUTH-IMPLEMENTATION.md`** (601 lines)
- Technical deep-dive
- PKCE explanation
- All endpoints documented
@ -538,7 +554,9 @@ Phase 3 is **successfully deployed when:**
- Troubleshooting guide
### Deployment Guide
📖 **`DEPLOYMENT-CHECKLIST.md`** (470 lines)
- Step-by-step deployment
- Environment setup
- Testing plan
@ -547,7 +565,9 @@ Phase 3 is **successfully deployed when:**
- Success criteria
### Code Documentation
✅ **Inline code comments**
- `foundation-oauth.ts` - PKCE + auth flow
- `foundation-auth.ts` - Token management
- `use-foundation-auth.ts` - React hooks
@ -558,12 +578,14 @@ Phase 3 is **successfully deployed when:**
## Next Steps
### Immediate (Today)
1. Review implementation
2. Verify credentials are correct
3. Set environment variables in deployment platform
4. Deploy to staging
### Short-term (This Week)
1. Test complete OAuth flow
2. Verify user syncing
3. Monitor logs for errors
@ -571,6 +593,7 @@ Phase 3 is **successfully deployed when:**
5. Deploy to production
### Long-term (Next Week+)
1. Monitor metrics (auth success rate, response times)
2. Remove old Discord OAuth code
3. Update user documentation

View file

@ -11,6 +11,7 @@ This Phase 3 implementation transforms `aethex.dev` from an identity provider in
### Client-side OAuth Utilities
1. **`code/client/lib/foundation-oauth.ts`**
- OAuth flow initialization
- URL generation for Foundation redirect
- `initiateFoundationLogin(redirectTo?)` - Main entry point for login
@ -19,6 +20,7 @@ This Phase 3 implementation transforms `aethex.dev` from an identity provider in
- Storage helpers for redirect destinations
2. **`code/client/lib/foundation-auth.ts`**
- Token and session management
- Cookie handling (`foundation_access_token`, `auth_user_id`)
- User profile fetching from Foundation
@ -37,6 +39,7 @@ This Phase 3 implementation transforms `aethex.dev` from an identity provider in
### Backend OAuth Endpoints
4. **`code/api/auth/foundation-callback.ts`**
- Callback endpoint: `GET /api/auth/foundation-callback?code=...&state=...`
- Validates authorization code from Foundation
- Exchanges code for access token
@ -54,6 +57,7 @@ This Phase 3 implementation transforms `aethex.dev` from an identity provider in
### Configuration & Documentation
6. **`code/.env.foundation-oauth.example`**
- Example environment variables
- `VITE_FOUNDATION_URL` - Foundation identity provider URL
- `FOUNDATION_OAUTH_CLIENT_SECRET` - OAuth credentials
@ -73,6 +77,7 @@ This Phase 3 implementation transforms `aethex.dev` from an identity provider in
### **`code/client/pages/Login.tsx`**
**Changes:**
- Added Foundation URL import
- Added `initiateFoundationLogin` import
- Replaced Discord OAuth button with "Login with Foundation" button
@ -82,14 +87,16 @@ This Phase 3 implementation transforms `aethex.dev` from an identity provider in
- **Other Options** (Roblox, Ethereum)
**Old button removed:**
```javascript
// REMOVED - Discord OAuth now handled by Foundation
<Button onClick={() => window.location.href = "/api/discord/oauth/start"}>
<Button onClick={() => (window.location.href = "/api/discord/oauth/start")}>
Discord
</Button>
```
**New button added:**
```javascript
<Button onClick={() => initiateFoundationLogin(redirectTo)}>
<Shield /> Login with Foundation
@ -145,6 +152,7 @@ FOUNDATION_OAUTH_CLIENT_SECRET=<secret-from-foundation-setup>
```
**Obtain these from Foundation admin:**
1. After Foundation's Phase 1 setup is complete
2. Request OAuth client secret for `aethex-corp`
3. Verify Foundation endpoints are operational
@ -155,7 +163,7 @@ FOUNDATION_OAUTH_CLIENT_SECRET=<secret-from-foundation-setup>
## Key Differences from Phase 2
| Aspect | Phase 2 | Phase 3 |
|--------|--------|--------|
| ------------------- | ------------------- | ------------------------------- |
| **Auth Provider** | Supabase (local) | Foundation (remote) |
| **Identity Issuer** | aethex.dev | aethex.foundation |
| **Discord OAuth** | Handled locally | Handled by Foundation |
@ -169,11 +177,13 @@ FOUNDATION_OAUTH_CLIENT_SECRET=<secret-from-foundation-setup>
## What Happens to Discord OAuth?
**Old flow (Phase 2):**
- User clicks Discord button on aethex.dev
- aethex.dev handles OAuth with Discord
- Discord connects to aethex.dev's Supabase
**New flow (Phase 3):**
- User clicks "Login with Foundation" on aethex.dev
- Redirected to aethex.foundation for authentication
- User connects Discord on Foundation (if needed)
@ -232,15 +242,15 @@ function ProtectedRoute() {
Send Foundation token with requests:
```typescript
import { getFoundationAccessToken } from '@/lib/foundation-auth';
import { getFoundationAccessToken } from "@/lib/foundation-auth";
const token = getFoundationAccessToken();
fetch('/api/user/profile', {
fetch("/api/user/profile", {
headers: {
'Authorization': `Bearer ${token}`
Authorization: `Bearer ${token}`,
},
credentials: 'include'
credentials: "include",
});
```
@ -266,6 +276,7 @@ fetch('/api/user/profile', {
## Next Steps
### Immediate (Week 1)
1. ✅ Implement Phase 3 code (this document)
2. ⏳ Set Foundation OAuth credentials
3. ⏳ Deploy to staging
@ -273,6 +284,7 @@ fetch('/api/user/profile', {
5. ⏳ Monitor for errors and issues
### Short-term (Week 2-3)
1. ⏳ Verify all existing users can re-authenticate
2. ⏳ Confirm user profile syncing works
3. ⏳ Test role/permission inheritance from Foundation
@ -280,6 +292,7 @@ fetch('/api/user/profile', {
5. ⏳ Update documentation
### Future (Phase 4+)
1. ⏳ Remove email/password auth from aethex.dev
2. ⏳ Remove Roblox/Ethereum OAuth (centralize at Foundation)
3. ⏳ Implement cross-domain SSO

View file

@ -9,11 +9,13 @@ The Phase 3 implementation is **complete and ready for deployment**. aethex.dev
## What You Need To Know
### Before Foundation Migrate
- **aethex.dev** handled all authentication (Discord OAuth, email/password)
- User identity was distributed across multiple systems
- Each application had its own auth logic
### After Phase 3 Deployed
- **aethex.foundation** is the authoritative identity provider
- **aethex.dev** redirects users to Foundation for authentication
- All Discord connections handled by Foundation
@ -42,12 +44,14 @@ FOUNDATION_OAUTH_CLIENT_SECRET=<secret-provided-by-foundation>
The following files are new and handle Foundation OAuth:
**Client-side:**
- `code/client/lib/foundation-oauth.ts` - OAuth flow
- `code/client/lib/foundation-auth.ts` - Token management
- `code/client/hooks/use-foundation-auth.ts` - React hooks
- `code/client/pages/Login.tsx` - UPDATED with Foundation button
**Server-side:**
- `code/api/auth/foundation-callback.ts` - OAuth callback handler
- `code/api/auth/exchange-token.ts` - Token exchange endpoint
@ -64,18 +68,22 @@ The following files are new and handle Foundation OAuth:
## Key Changes in This Phase
### Login Page
- **Old:** Discord button redirected to local `/api/discord/oauth/start`
- **New:** "Login with Foundation" button redirects to `aethex.foundation`
### Authentication Flow
- **Old:** Local Supabase auth → Discord OAuth locally → Session on aethex.dev
- **New:** Redirect to Foundation → User auth on Foundation → Session on aethex.dev with Foundation token
### User Profile
- **Old:** Stored directly in aethex.dev's Supabase
- **New:** Synced from Foundation's Supabase to aethex.dev's local copy
### Discord Management
- **Old:** aethex.dev handled all Discord connections
- **New:** Foundation handles all Discord connections; aethex.dev consumes the result
@ -84,6 +92,7 @@ The following files are new and handle Foundation OAuth:
## Important Files
### New Components (Phase 3 Specific)
```
code/
├── client/
@ -106,6 +115,7 @@ code/
```
### Configuration Files
```
code/
└── .env.foundation-oauth.example ← Example env vars
@ -154,6 +164,7 @@ Discord OAuth is now **managed entirely by aethex.foundation**.
## User Experience After Phase 3
### For New Users
1. Visit aethex.dev/login
2. See "Login with Foundation" button (primary option)
3. Click it
@ -162,6 +173,7 @@ Discord OAuth is now **managed entirely by aethex.foundation**.
6. Complete onboarding with pre-filled Foundation data
### For Existing Users
1. Existing sessions will be cleared (they had aethex.dev Supabase tokens)
2. They'll be redirected to login page
3. They click "Login with Foundation"
@ -208,15 +220,18 @@ Discord OAuth is now **managed entirely by aethex.foundation**.
If critical issues arise:
1. **Revert code:**
```bash
git revert <Phase3-commit-hash>
```
2. **Restore environment:**
- Remove VITE_FOUNDATION_URL
- Remove FOUNDATION_OAUTH_CLIENT_SECRET
3. **Tell users:**
- "We've temporarily disabled Foundation integration"
- "Please use local login or Discord OAuth"
@ -229,6 +244,7 @@ If critical issues arise:
## Deployment Recommendations
### Staging Deployment (First)
1. Deploy Phase 3 code to staging
2. Set Foundation OAuth credentials on staging
3. Test according to `PHASE3-TESTING-PLAN.md`
@ -236,6 +252,7 @@ If critical issues arise:
5. Monitor staging for 24 hours
### Production Deployment
1. Create backup of current auth system
2. Deploy Phase 3 code gradually (canary deployment if possible)
3. Set Foundation OAuth credentials in production
@ -244,6 +261,7 @@ If critical issues arise:
6. Communicate with users
### Monitoring
- Auth success rate (target >99%)
- Token exchange time (target <2s)
- Error messages in logs
@ -280,12 +298,14 @@ A: Users link Discord on Foundation instead. No linking needed on aethex.dev.
## Next Steps
### Week 1: Setup
1. ✅ Code implemented (DONE)
2. ⏳ Get Foundation OAuth credentials
3. ⏳ Set environment variables
4. ⏳ Deploy to staging
### Week 2: Testing
5. ⏳ Test complete auth flow
6. ⏳ Test error scenarios
7. ⏳ Test on multiple browsers
@ -293,12 +313,14 @@ A: Users link Discord on Foundation instead. No linking needed on aethex.dev.
9. ⏳ Get team approval
### Week 3: Deployment
10. ⏳ Deploy to production
11. ⏳ Monitor closely for issues
12. ⏳ Document any bugs found
13. ⏳ Communicate with users
### Week 4+: Optimization
14. ⏳ Remove old Discord OAuth endpoints
15. ⏳ Optimize token handling
16. ⏳ Update documentation
@ -322,16 +344,19 @@ Detailed documentation available:
If you encounter issues:
1. **Check logs:**
- Foundation callback logs (Vercel deployment)
- Token exchange errors
- Profile sync failures
2. **Verify environment:**
- VITE_FOUNDATION_URL is correct
- FOUNDATION_OAUTH_CLIENT_SECRET is correct
- Foundation service is running
3. **Test manually:**
- Use curl to test token endpoint
- Check database for user profiles
- Inspect cookies in browser

View file

@ -59,6 +59,7 @@ FOUNDATION_OAUTH_CLIENT_SECRET=<secret-from-foundation-setup>
```
**Files affected:**
- `.env.foundation-oauth.example` - Example configuration
---
@ -68,11 +69,13 @@ FOUNDATION_OAUTH_CLIENT_SECRET=<secret-from-foundation-setup>
New utility modules for Foundation OAuth:
**Files created:**
- `code/client/lib/foundation-oauth.ts` - OAuth flow helpers
- `code/client/lib/foundation-auth.ts` - Token/profile management
- `code/client/hooks/use-foundation-auth.ts` - React hooks for auth handling
**Key functions:**
- `initiateFoundationLogin()` - Redirects to Foundation
- `exchangeCodeForToken()` - Backend token exchange
- `fetchUserProfileFromFoundation()` - Get user data from Foundation
@ -84,10 +87,12 @@ New utility modules for Foundation OAuth:
### Step 3: Backend OAuth Endpoints ✅
**Files created:**
- `code/api/auth/foundation-callback.ts` - Handles redirect from Foundation
- `code/api/auth/exchange-token.ts` - Token exchange endpoint
**Flow:**
1. User clicks "Login with Foundation" on aethex.dev/login
2. Browser redirected to `aethex.foundation/api/oauth/authorize`
3. User authenticates on Foundation
@ -101,14 +106,17 @@ New utility modules for Foundation OAuth:
### Step 4: Frontend Login Page Refactoring ✅
**File modified:**
- `code/client/pages/Login.tsx`
**Changes:**
- Replaced local Discord OAuth button with "Login with Foundation" button
- Uses `initiateFoundationLogin()` to start OAuth flow
- Removed Discord Activity check for Discord login (now handled by Foundation)
**New UI:**
```
┌─────────────────────────────────────┐
│ Sign In to AeThex │
@ -133,6 +141,7 @@ New utility modules for Foundation OAuth:
### Step 5: Remove Old Authentication Endpoints
**Files to remove or deprecate:**
- `code/api/discord/oauth/start.ts` - Local Discord OAuth start
- `code/api/discord/oauth/callback.ts` - Local Discord OAuth callback
- `code/api/discord/link.ts` - Discord linking endpoint
@ -151,11 +160,11 @@ After Foundation OAuth callback, sessions work as follows:
```javascript
// Frontend makes authenticated requests:
const response = await fetch('/api/user/profile', {
const response = await fetch("/api/user/profile", {
headers: {
'Authorization': `Bearer ${getFoundationAccessToken()}`
Authorization: `Bearer ${getFoundationAccessToken()}`,
},
credentials: 'include' // Send cookies
credentials: "include", // Send cookies
});
// Backend validates token:
@ -166,6 +175,7 @@ const response = await fetch('/api/user/profile', {
```
**Files that need updates:**
- `code/server/index.ts` - Add Foundation token validation middleware
- `code/api/_supabase.ts` - Support Foundation token context
@ -224,11 +234,13 @@ START: User visits aethex.dev/login
After Phase 1 (Foundation setup) is complete, you'll receive:
1. **Foundation OAuth Details:**
- OAuth endpoint URLs (authorize, token)
- Client ID: `aethex-corp`
- Client Secret: (provide to FOUNDATION_OAUTH_CLIENT_SECRET env var)
2. **Foundation API Endpoints:**
- GET `/api/auth/me` - Get authenticated user profile
- POST `/api/oauth/authorize` - Authorization endpoint
- POST `/api/oauth/token` - Token exchange endpoint
@ -253,12 +265,14 @@ After Phase 1 (Foundation setup) is complete, you'll receive:
### Local Testing
1. **Set up environment:**
```bash
export VITE_FOUNDATION_URL=http://localhost:3001 # or staging URL
export FOUNDATION_OAUTH_CLIENT_SECRET=<test-secret>
```
2. **Test login flow:**
- Visit `http://localhost:5173/login`
- Click "Login with Foundation"
- Should redirect to Foundation auth page
@ -340,16 +354,19 @@ After Phase 3 stabilizes:
### Common Issues
**Issue: "Authorization code not received"**
- Check redirect_uri matches registered value
- Verify client_id=aethex-corp in Foundation
- Check Foundation environment is accessible
**Issue: "Token exchange failed"**
- Verify FOUNDATION_OAUTH_CLIENT_SECRET is correct
- Check Foundation token endpoint is accessible
- Review Foundation logs for errors
**Issue: "User profile not syncing"**
- Verify Supabase connection
- Check user_profiles table exists locally
- Review foundation-callback logs
@ -365,6 +382,7 @@ After Phase 3 stabilizes:
## Code References
**New files:**
- `code/client/lib/foundation-oauth.ts`
- `code/client/lib/foundation-auth.ts`
- `code/client/hooks/use-foundation-auth.ts`
@ -372,9 +390,11 @@ After Phase 3 stabilizes:
- `code/api/auth/exchange-token.ts`
**Modified files:**
- `code/client/pages/Login.tsx` - OAuth flow updated
**Deprecated (to remove):**
- `code/api/discord/oauth/start.ts`
- `code/api/discord/oauth/callback.ts`
- `code/api/discord/link.ts`

View file

@ -5,6 +5,7 @@
Before running tests, ensure:
1. **Environment variables are set:**
```bash
VITE_FOUNDATION_URL=https://aethex.foundation # or staging/localhost
FOUNDATION_OAUTH_CLIENT_SECRET=<received-from-foundation>
@ -12,6 +13,7 @@ Before running tests, ensure:
```
2. **Foundation is operational:**
- aethex.foundation is running
- OAuth endpoints are accessible
- Test user accounts exist
@ -30,11 +32,13 @@ Before running tests, ensure:
**Objective:** Verify the login page displays Foundation OAuth button
**Steps:**
1. Navigate to `http://localhost:5173/login` (or prod URL)
2. Look for "Login with Foundation" button
3. Verify button is visible and clickable
**Expected Result:**
```
✓ Login page displays
✓ "Login with Foundation" button visible
@ -51,11 +55,13 @@ Before running tests, ensure:
**Objective:** Verify clicking the button redirects to Foundation
**Steps:**
1. On login page, click "Login with Foundation" button
2. Observe browser URL change
3. Check redirect parameters
**Expected Result:**
```
Redirected to:
https://aethex.foundation/api/oauth/authorize
@ -75,12 +81,14 @@ https://aethex.foundation/api/oauth/authorize
**Objective:** User authenticates on Foundation
**Steps:**
1. You're now on Foundation login page
2. Enter test credentials
3. If prompted, grant aethex.dev permissions
4. Click "Authorize" or similar
**Expected Result:**
```
✓ Foundation accepts credentials
✓ Permission screen appears (if configured)
@ -96,11 +104,13 @@ https://aethex.foundation/api/oauth/authorize
**Objective:** Verify Foundation redirects back with authorization code
**Steps:**
1. After Foundation authentication completes
2. Observe browser URL change
3. Look for authorization code in URL
**Expected Result:**
```
Browser redirects to:
https://aethex.dev/api/auth/foundation-callback
@ -121,11 +131,13 @@ Check browser console:
**Objective:** Backend exchanges code for access token
**Steps:**
1. Monitor network requests in browser Dev Tools
2. Look for POST to `/api/auth/exchange-token`
3. Check response status
**Expected Result:**
```
Network:
POST /api/auth/exchange-token
@ -154,11 +166,13 @@ Cookies set:
**Objective:** Verify user profile created/updated in local database
**Steps:**
1. After successful login, check database
2. Query user_profiles table
3. Verify user exists with correct data
**Database Query:**
```sql
-- Check user was created/updated
SELECT id, email, username, profile_completed, updated_at
@ -184,11 +198,13 @@ LIMIT 1;
**Objective:** User redirected to dashboard after authentication
**Steps:**
1. After token exchange and profile sync
2. Browser should automatically redirect
3. Check final URL
**Expected Result:**
```
Browser URL: https://aethex.dev/dashboard
✓ Dashboard loads successfully
@ -205,23 +221,26 @@ Browser URL: https://aethex.dev/dashboard
**Objective:** User can make authenticated API calls
**Steps:**
1. On authenticated dashboard
2. Use browser console to test:
```javascript
const token = document.cookie
.split(';')
.find(c => c.trim().startsWith('foundation_access_token='))
?.split('=')[1];
.split(";")
.find((c) => c.trim().startsWith("foundation_access_token="))
?.split("=")[1];
fetch('/api/user/profile', {
headers: { 'Authorization': `Bearer ${token}` },
credentials: 'include'
fetch("/api/user/profile", {
headers: { Authorization: `Bearer ${token}` },
credentials: "include",
})
.then(r => r.json())
.then((r) => r.json())
.then(console.log);
```
**Expected Result:**
```javascript
// Console output:
{
@ -241,12 +260,14 @@ Browser URL: https://aethex.dev/dashboard
**Objective:** Verify logout clears Foundation auth
**Steps:**
1. On authenticated dashboard
2. Click logout/settings
3. Trigger logout action
4. Verify redirect to login
**Expected Result:**
```
✓ Logout triggered
✓ Cookies cleared:
@ -256,11 +277,13 @@ Browser URL: https://aethex.dev/dashboard
✓ Previous authenticated state lost
```
**Test command (if logout has UI):
\*\*Test command (if logout has UI):
```javascript
// Clear cookies manually in console
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;';
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;";
```
**Success Criteria:** ✅ Cookies cleared, session terminated
@ -272,6 +295,7 @@ document.cookie = 'auth_user_id=; expires=Thu, 01 Jan 1970 00:00:00 UTC;';
**Objective:** Verify redirect works when accessing protected page first
**Steps:**
1. Logout (or clear cookies)
2. Visit protected page: `http://localhost:5173/dashboard?next=/admin`
3. Get redirected to login
@ -279,6 +303,7 @@ document.cookie = 'auth_user_id=; expires=Thu, 01 Jan 1970 00:00:00 UTC;';
5. After auth, should redirect to `/admin` instead of `/dashboard`
**Expected Result:**
```
✓ Initial redirect to /login with ?next=/admin
✓ After Foundation auth, redirected to /admin
@ -293,10 +318,12 @@ document.cookie = 'auth_user_id=; expires=Thu, 01 Jan 1970 00:00:00 UTC;';
### Error 1: Invalid Authorization Code
**How to trigger:**
1. Manually modify URL code parameter: `?code=invalid_code`
2. Let callback process
**Expected Result:**
```
Error: token_exchange
Message: Failed to exchange authorization code
@ -310,10 +337,12 @@ Redirect to: /login?error=token_exchange
### Error 2: Missing Client Secret
**How to trigger:**
1. Unset `FOUNDATION_OAUTH_CLIENT_SECRET` env var
2. Attempt login
**Expected Result:**
```
Error: 500 or token_exchange error
Message: Missing environment variables
@ -327,11 +356,13 @@ Redirect to: /login with error
### Error 3: Foundation Unavailable
**How to trigger:**
1. Stop Foundation service
2. Attempt login
3. Foundation authorize redirects back
**Expected Result:**
```
Error: Token exchange fails
Message: Failed to connect to Foundation
@ -345,10 +376,12 @@ Redirect to: /login with error message
### Error 4: Expired Authorization Code
**How to trigger:**
1. Wait >10 minutes after Foundation redirect
2. Complete the callback
**Expected Result:**
```
Error: invalid_grant or code_expired
Message: Authorization code has expired
@ -371,6 +404,7 @@ Test on multiple browsers:
- [ ] Mobile Safari
**Checklist for each browser:**
- [ ] Login page renders correctly
- [ ] Redirect to Foundation works
- [ ] Cookies are set (check Dev Tools)
@ -460,7 +494,7 @@ Environment: [Staging/Production]
## Test Results
| Test | Status | Notes |
|------|--------|-------|
| ----------------------- | ------ | ----- |
| Test 1: Login Page | ✅/❌ | |
| Test 2: Redirect | ✅/❌ | |
| Test 3: Foundation Auth | ✅/❌ | |
@ -503,15 +537,18 @@ Environment: [Staging/Production]
### Key Metrics to Monitor
1. **Authentication Success Rate**
- Should be >99%
- Track failed logins
2. **Error Categories**
- Code exchange failures
- Token validation failures
- Profile sync failures
3. **Performance**
- Token exchange time (target <2s)
- Dashboard load time after auth
- API request latency
@ -524,6 +561,7 @@ Environment: [Staging/Production]
### Alert Thresholds
Set alerts for:
- Auth failure rate > 5%
- Token exchange time > 5 seconds
- Foundation connectivity issues
@ -534,6 +572,7 @@ Set alerts for:
## Rollback Triggers
Immediately rollback if:
- Auth failure rate > 25%
- Unable to authenticate any new users
- Data corruption in user_profiles