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 { VercelRequest, VercelResponse } from "@vercel/node";
import { getAdminClient } from "../_supabase"; 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_ID = process.env.FOUNDATION_OAUTH_CLIENT_ID || "aethex_corp";
const CLIENT_SECRET = process.env.FOUNDATION_OAUTH_CLIENT_SECRET; const CLIENT_SECRET = process.env.FOUNDATION_OAUTH_CLIENT_SECRET;
const API_BASE = process.env.VITE_API_BASE || "https://aethex.dev"; 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 // Handle Foundation errors
if (error) { 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( return res.redirect(
`/login?error=${error}&message=${encodeURIComponent(message)}`, `/login?error=${error}&message=${encodeURIComponent(message)}`,
); );
} }
if (!code) { 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 { try {
@ -64,7 +69,9 @@ export async function handleCallback(req: VercelRequest, res: VercelResponse) {
console.warn("[Foundation OAuth] Missing state parameter"); 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 // Store code in a temporary location for the exchange endpoint
// In a real implementation, you'd use a temporary token or session // 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 // Fetch user information from Foundation
const userInfo = await fetchUserInfoFromFoundation(exchangeResult.accessToken); const userInfo = await fetchUserInfoFromFoundation(
exchangeResult.accessToken,
);
// Sync user to local database // Sync user to local database
await syncUserToLocalDatabase(userInfo); await syncUserToLocalDatabase(userInfo);
@ -89,11 +98,12 @@ export async function handleCallback(req: VercelRequest, res: VercelResponse) {
console.log("[Foundation OAuth] User authenticated:", userInfo.id); console.log("[Foundation OAuth] User authenticated:", userInfo.id);
// Redirect to dashboard (or stored destination) // 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); return res.redirect(redirectTo);
} catch (error) { } catch (error) {
console.error("[Foundation OAuth] Callback error:", 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( return res.redirect(
`/login?error=auth_failed&message=${encodeURIComponent(message)}`, `/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 * Exchange authorization code for access token
* Called from frontend * Called from frontend
*/ */
export async function handleTokenExchange(req: VercelRequest, res: VercelResponse) { export async function handleTokenExchange(
req: VercelRequest,
res: VercelResponse,
) {
if (req.method !== "POST") { if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" }); 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); const exchangeResult = await performTokenExchange(code);
// Fetch user information from Foundation // Fetch user information from Foundation
const userInfo = await fetchUserInfoFromFoundation(exchangeResult.accessToken); const userInfo = await fetchUserInfoFromFoundation(
exchangeResult.accessToken,
);
// Sync user to local database // Sync user to local database
await syncUserToLocalDatabase(userInfo); 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`, `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({ return res.status(200).json({
accessToken: exchangeResult.accessToken, accessToken: exchangeResult.accessToken,
@ -139,7 +157,8 @@ export async function handleTokenExchange(req: VercelRequest, res: VercelRespons
}); });
} catch (error) { } catch (error) {
console.error("[Foundation OAuth] Token exchange error:", 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 }); 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 * Exchange authorization code for access token with Foundation
*/ */
async function performTokenExchange( async function performTokenExchange(code: string): Promise<{
code: string,
): Promise<{
accessToken: string; accessToken: string;
tokenType: string; tokenType: string;
expiresIn: number; expiresIn: number;
@ -205,7 +222,9 @@ async function performTokenExchange(
/** /**
* Fetch user information from Foundation using access token * 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`; const userInfoEndpoint = `${FOUNDATION_URL}/api/oauth/userinfo`;
console.log("[Foundation OAuth] Fetching user info from:", userInfoEndpoint); console.log("[Foundation OAuth] Fetching user info from:", userInfoEndpoint);
@ -234,30 +253,36 @@ async function fetchUserInfoFromFoundation(accessToken: string): Promise<Foundat
/** /**
* Sync Foundation user to local database * Sync Foundation user to local database
*/ */
async function syncUserToLocalDatabase(foundationUser: FoundationUserInfo): Promise<void> { async function syncUserToLocalDatabase(
foundationUser: FoundationUserInfo,
): Promise<void> {
const supabase = getAdminClient(); 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 // Upsert user profile
const { error } = await supabase const { error } = await supabase.from("user_profiles").upsert({
.from("user_profiles") id: foundationUser.id,
.upsert({ email: foundationUser.email,
id: foundationUser.id, username: foundationUser.username || null,
email: foundationUser.email, full_name: foundationUser.full_name || null,
username: foundationUser.username || null, avatar_url: foundationUser.avatar_url || null,
full_name: foundationUser.full_name || null, profile_completed: foundationUser.profile_complete || false,
avatar_url: foundationUser.avatar_url || null, updated_at: new Date().toISOString(),
profile_completed: foundationUser.profile_complete || false, });
updated_at: new Date().toISOString(),
});
if (error) { if (error) {
console.error("[Foundation OAuth] Failed to sync user profile:", error); console.error("[Foundation OAuth] Failed to sync user profile:", error);
throw new Error("Failed to create local user profile"); 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

@ -1,19 +1,17 @@
/** /**
* Token Exchange Endpoint * Token Exchange Endpoint
* *
* Frontend calls this endpoint after receiving the authorization code from Foundation. * Frontend calls this endpoint after receiving the authorization code from Foundation.
* It stores the Foundation's access token and returns user information. * It stores the Foundation's access token and returns user information.
*/ */
import { VercelRequest, VercelResponse } from "@vercel/node"; 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"; const API_BASE = process.env.VITE_API_BASE || "https://aethex.dev";
export default async function handler( export default async function handler(req: VercelRequest, res: VercelResponse) {
req: VercelRequest,
res: VercelResponse,
) {
if (req.method !== "POST") { if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" }); return res.status(405).json({ error: "Method not allowed" });
} }

View file

@ -1,6 +1,6 @@
/** /**
* Foundation OAuth Callback Handler * Foundation OAuth Callback Handler
* *
* This endpoint receives the authorization code from aethex.foundation after user authentication. * This endpoint receives the authorization code from aethex.foundation after user authentication.
* It exchanges the code for an access token and establishes a session on aethex.dev. * It exchanges the code for an access token and establishes a session on aethex.dev.
*/ */
@ -8,7 +8,8 @@
import { getAdminClient } from "../_supabase"; import { getAdminClient } from "../_supabase";
import { VercelRequest, VercelResponse } from "@vercel/node"; 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"; const API_BASE = process.env.VITE_API_BASE || "https://aethex.dev";
interface FoundationTokenResponse { interface FoundationTokenResponse {
@ -23,10 +24,7 @@ interface FoundationTokenResponse {
}; };
} }
export default async function handler( export default async function handler(req: VercelRequest, res: VercelResponse) {
req: VercelRequest,
res: VercelResponse,
) {
if (req.method !== "GET") { if (req.method !== "GET") {
return res.status(405).json({ error: "Method not allowed" }); return res.status(405).json({ error: "Method not allowed" });
} }
@ -36,11 +34,15 @@ export default async function handler(
// Handle Foundation errors // Handle Foundation errors
if (error) { if (error) {
const errorDesc = req.query.error_description || 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) { 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 { try {
@ -74,7 +76,7 @@ export default async function handler(
const errorData = await tokenResponse.json().catch(() => ({})); const errorData = await tokenResponse.json().catch(() => ({}));
console.error("[Foundation OAuth] Token exchange failed:", errorData); console.error("[Foundation OAuth] Token exchange failed:", errorData);
return res.redirect( 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) { if (!tokenData.access_token || !tokenData.user) {
console.error("[Foundation OAuth] Invalid token response"); 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 // Extract user information from Foundation response
@ -101,25 +105,33 @@ export default async function handler(
if (fetchError && fetchError.code !== "PGRST116") { if (fetchError && fetchError.code !== "PGRST116") {
// PGRST116 = no rows found (expected for new users) // 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) { if (!existingProfile) {
// Create user profile from Foundation data // Create user profile from Foundation data
const { error: createError } = await supabase.from("user_profiles").insert({ const { error: createError } = await supabase
id: user.id, .from("user_profiles")
email: user.email, .insert({
username: user.username || null, id: user.id,
full_name: user.full_name || null, email: user.email,
profile_completed: user.profile_complete || false, username: user.username || null,
created_at: new Date().toISOString(), full_name: user.full_name || null,
updated_at: new Date().toISOString(), profile_completed: user.profile_complete || false,
}); created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
});
if (createError) { 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( 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) { } catch (error) {
console.error("[Foundation OAuth] Callback error:", error); console.error("[Foundation OAuth] Callback error:", error);
return res.redirect( 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"); 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 // Clear auth parameters from URL and storage
const url = new URL(window.location.href); const url = new URL(window.location.href);
@ -95,16 +98,21 @@ export function useFoundationAuth(): UseFoundationAuthReturn {
// Redirect to dashboard or stored destination // Redirect to dashboard or stored destination
navigate(redirectTo, { replace: true }); navigate(redirectTo, { replace: true });
} catch (exchangeError) { } catch (exchangeError) {
const message = exchangeError instanceof Error const message =
? exchangeError.message exchangeError instanceof Error
: "Failed to exchange authorization code"; ? 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); throw new Error(message);
} }
} catch (err) { } catch (err) {
const message = err instanceof Error ? err.message : "Authentication failed"; const message =
err instanceof Error ? err.message : "Authentication failed";
setError(message); setError(message);
console.error("[Foundation Auth] Callback processing error:", err); console.error("[Foundation Auth] Callback processing error:", err);
@ -150,7 +158,9 @@ export function useFoundationAuthStatus(): {
useEffect(() => { useEffect(() => {
// Check for foundation_access_token cookie // Check for foundation_access_token cookie
const cookies = document.cookie.split(";").map((c) => c.trim()); 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=")); const userCookie = cookies.find((c) => c.startsWith("auth_user_id="));
if (tokenCookie && userCookie) { if (tokenCookie && userCookie) {

View file

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

View file

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

View file

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

View file

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

View file

@ -71,6 +71,7 @@ GET https://aethex.foundation/api/oauth/authorize
``` ```
**Parameters:** **Parameters:**
- `client_id` - aethex_corp (identifies this app) - `client_id` - aethex_corp (identifies this app)
- `redirect_uri` - Where Foundation redirects after auth - `redirect_uri` - Where Foundation redirects after auth
- `response_type` - Always "code" (OAuth 2.0 authorization code flow) - `response_type` - Always "code" (OAuth 2.0 authorization code flow)
@ -98,6 +99,7 @@ grant_type=authorization_code
``` ```
**Response:** **Response:**
```json ```json
{ {
"access_token": "eyJ...", "access_token": "eyJ...",
@ -143,22 +145,26 @@ See `code/api/auth/callback.ts` → `fetchUserInfoFromFoundation()`
### How PKCE Works ### How PKCE Works
1. **Client generates code verifier:** 1. **Client generates code verifier:**
```javascript ```javascript
verifier = randomString(64 chars, URL-safe) verifier = randomString(64 chars, URL-safe)
// Example: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~" // Example: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"
``` ```
2. **Client generates code challenge:** 2. **Client generates code challenge:**
```javascript ```javascript
challenge = base64url(SHA256(verifier)) challenge = base64url(SHA256(verifier));
``` ```
3. **Client sends challenge with authorization request:** 3. **Client sends challenge with authorization request:**
``` ```
GET /api/oauth/authorize?...&code_challenge=...&code_challenge_method=S256 GET /api/oauth/authorize?...&code_challenge=...&code_challenge_method=S256
``` ```
4. **Server stores challenge, validates with verifier on token exchange:** 4. **Server stores challenge, validates with verifier on token exchange:**
``` ```
POST /api/oauth/token?...&code_verifier=... POST /api/oauth/token?...&code_verifier=...
``` ```
@ -169,6 +175,7 @@ See `code/api/auth/callback.ts` → `fetchUserInfoFromFoundation()`
``` ```
**Why PKCE?** **Why PKCE?**
- Prevents authorization code interception attacks - Prevents authorization code interception attacks
- Secure for mobile apps and single-page applications - Secure for mobile apps and single-page applications
- Required by OAuth 2.1 best practices - Required by OAuth 2.1 best practices
@ -193,6 +200,7 @@ Updated to show Foundation OAuth button:
``` ```
Features: Features:
- Initiates Foundation OAuth flow - Initiates Foundation OAuth flow
- Generates PKCE parameters - Generates PKCE parameters
- Stores verifier and state in sessionStorage - Stores verifier and state in sessionStorage
@ -204,16 +212,16 @@ Core OAuth functionality:
```typescript ```typescript
// Generate PKCE parameters // Generate PKCE parameters
async function generatePKCEParams(): Promise<{ verifier, challenge }> async function generatePKCEParams(): Promise<{ verifier; challenge }>;
// Build authorization URL // Build authorization URL
async function getFoundationAuthorizationUrl(options?): Promise<string> async function getFoundationAuthorizationUrl(options?): Promise<string>;
// Initiate login // Initiate login
async function initiateFoundationLogin(redirectTo?: string): Promise<void> async function initiateFoundationLogin(redirectTo?: string): Promise<void>;
// Exchange code for token (called from backend) // 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`) #### 3. useFoundationAuth Hook (`code/client/hooks/use-foundation-auth.ts`)
@ -235,6 +243,7 @@ useFoundationAuthStatus(): { isAuthenticated, userId }
**Route:** `GET /auth/callback?code=...&state=...` **Route:** `GET /auth/callback?code=...&state=...`
**Flow:** **Flow:**
1. Receive authorization code from Foundation 1. Receive authorization code from Foundation
2. Validate state token (CSRF protection) 2. Validate state token (CSRF protection)
3. Exchange code for access token 3. Exchange code for access token
@ -244,29 +253,30 @@ useFoundationAuthStatus(): { isAuthenticated, userId }
7. Redirect to dashboard 7. Redirect to dashboard
**Code:** **Code:**
```typescript ```typescript
async function handleCallback(req, res) { async function handleCallback(req, res) {
// 1. Get code from URL // 1. Get code from URL
const { code, state } = req.query; const { code, state } = req.query;
// 2. Validate state // 2. Validate state
validateState(state); validateState(state);
// 3. Exchange for token // 3. Exchange for token
const token = await performTokenExchange(code); const token = await performTokenExchange(code);
// 4. Fetch user info // 4. Fetch user info
const user = await fetchUserInfoFromFoundation(token); const user = await fetchUserInfoFromFoundation(token);
// 5. Sync to database // 5. Sync to database
await syncUserToLocalDatabase(user); await syncUserToLocalDatabase(user);
// 6. Set cookies // 6. Set cookies
res.setHeader("Set-Cookie", [ res.setHeader("Set-Cookie", [
`foundation_access_token=${token}; ...`, `foundation_access_token=${token}; ...`,
`auth_user_id=${user.id}; ...` `auth_user_id=${user.id}; ...`,
]); ]);
// 7. Redirect // 7. Redirect
return res.redirect("/dashboard"); return res.redirect("/dashboard");
} }
@ -279,7 +289,7 @@ async function handleCallback(req, res) {
```typescript ```typescript
async function handleTokenExchange(req, res) { async function handleTokenExchange(req, res) {
const { code } = req.body; const { code } = req.body;
// Exchange code with Foundation // Exchange code with Foundation
// Fetch user info // Fetch user info
// Sync to database // Sync to database
@ -314,16 +324,16 @@ For authenticated API requests:
```typescript ```typescript
// Get token from cookie // Get token from cookie
const token = document.cookie const token = document.cookie
.split(';') .split(";")
.find(c => c.trim().startsWith('foundation_access_token=')) .find((c) => c.trim().startsWith("foundation_access_token="))
?.split('=')[1]; ?.split("=")[1];
// Use in requests // Use in requests
fetch('/api/user/profile', { fetch("/api/user/profile", {
headers: { headers: {
'Authorization': `Bearer ${token}` Authorization: `Bearer ${token}`,
}, },
credentials: 'include' credentials: "include",
}); });
``` ```
@ -333,13 +343,14 @@ On logout:
```typescript ```typescript
// Clear cookies // Clear cookies
document.cookie = 'foundation_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC;'; document.cookie =
document.cookie = 'auth_user_id=; expires=Thu, 01 Jan 1970 00:00:00 UTC;'; "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 // Optional: Notify Foundation of logout
await fetch(`${FOUNDATION_URL}/api/oauth/logout`, { await fetch(`${FOUNDATION_URL}/api/oauth/logout`, {
method: 'POST', method: "POST",
headers: { 'Authorization': `Bearer ${token}` } headers: { Authorization: `Bearer ${token}` },
}); });
``` ```
@ -376,13 +387,13 @@ Corp Local Database (user_profiles table):
```typescript ```typescript
await supabase.from("user_profiles").upsert({ await supabase.from("user_profiles").upsert({
id: foundationUser.id, // Match by ID id: foundationUser.id, // Match by ID
email: foundationUser.email, email: foundationUser.email,
username: foundationUser.username, username: foundationUser.username,
full_name: foundationUser.full_name, full_name: foundationUser.full_name,
avatar_url: foundationUser.avatar_url, avatar_url: foundationUser.avatar_url,
profile_completed: foundationUser.profile_complete, 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 ### Local Testing
1. **Set up environment:** 1. **Set up environment:**
```bash ```bash
export VITE_FOUNDATION_URL=http://localhost:3001 # Or staging URL export VITE_FOUNDATION_URL=http://localhost:3001 # Or staging URL
export FOUNDATION_OAUTH_CLIENT_ID=aethex_corp export FOUNDATION_OAUTH_CLIENT_ID=aethex_corp
@ -402,6 +414,7 @@ await supabase.from("user_profiles").upsert({
``` ```
2. **Test flow:** 2. **Test flow:**
- Visit `http://localhost:5173/login` - Visit `http://localhost:5173/login`
- Click "Login with Foundation" - Click "Login with Foundation"
- Should redirect to Foundation auth page - Should redirect to Foundation auth page
@ -417,6 +430,7 @@ await supabase.from("user_profiles").upsert({
### Error Scenarios ### Error Scenarios
**Invalid code:** **Invalid code:**
``` ```
GET /auth/callback?code=invalid GET /auth/callback?code=invalid
→ 400 "Token exchange failed" → 400 "Token exchange failed"
@ -424,6 +438,7 @@ GET /auth/callback?code=invalid
``` ```
**Invalid state:** **Invalid state:**
``` ```
GET /auth/callback?code=...&state=wrong_state GET /auth/callback?code=...&state=wrong_state
→ Error "Invalid state token - possible CSRF attack" → Error "Invalid state token - possible CSRF attack"
@ -431,6 +446,7 @@ GET /auth/callback?code=...&state=wrong_state
``` ```
**Foundation down:** **Foundation down:**
``` ```
POST /api/oauth/token → ECONNREFUSED POST /api/oauth/token → ECONNREFUSED
→ Error "Failed to exchange code" → Error "Failed to exchange code"
@ -442,6 +458,7 @@ POST /api/oauth/token → ECONNREFUSED
## Files Modified/Created ## Files Modified/Created
### New Files ### New Files
``` ```
code/ code/
├── client/ ├── client/
@ -454,6 +471,7 @@ code/
``` ```
### Modified Files ### Modified Files
``` ```
code/ code/
└── client/ └── client/
@ -461,7 +479,9 @@ code/
``` ```
### Deprecated Files ### Deprecated Files
These can be removed after testing completes: These can be removed after testing completes:
``` ```
code/api/discord/oauth/start.ts code/api/discord/oauth/start.ts
code/api/discord/oauth/callback.ts code/api/discord/oauth/callback.ts
@ -492,14 +512,17 @@ code/api/discord/verify-code.ts
### Key Metrics ### Key Metrics
1. **Auth Success Rate** 1. **Auth Success Rate**
- Target: >99% - Target: >99%
- Alert if: <95% - Alert if: <95%
2. **Token Exchange Time** 2. **Token Exchange Time**
- Target: <500ms - Target: <500ms
- Alert if: >2s - Alert if: >2s
3. **Error Categories** 3. **Error Categories**
- Track: invalid_code, state_mismatch, timeout, etc. - Track: invalid_code, state_mismatch, timeout, etc.
4. **Foundation Connectivity** 4. **Foundation Connectivity**
@ -528,6 +551,7 @@ Key points to log:
**Cause:** Foundation didn't redirect back properly **Cause:** Foundation didn't redirect back properly
**Solutions:** **Solutions:**
1. Check `redirect_uri` matches exactly (case-sensitive) 1. Check `redirect_uri` matches exactly (case-sensitive)
2. Verify Foundation OAuth settings 2. Verify Foundation OAuth settings
3. Check browser console for JavaScript errors 3. Check browser console for JavaScript errors
@ -537,6 +561,7 @@ Key points to log:
**Cause:** CSRF validation failed **Cause:** CSRF validation failed
**Solutions:** **Solutions:**
1. Check that state is generated consistently 1. Check that state is generated consistently
2. Verify sessionStorage isn't cleared between redirect 2. Verify sessionStorage isn't cleared between redirect
3. Check for multiple browser tabs (different state per tab) 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 **Cause:** Foundation token endpoint unavailable or code invalid
**Solutions:** **Solutions:**
1. Check Foundation is running and `/api/oauth/token` accessible 1. Check Foundation is running and `/api/oauth/token` accessible
2. Verify `client_id` and `client_secret` are correct 2. Verify `client_id` and `client_secret` are correct
3. Check code hasn't expired (usually 10 minutes) 3. Check code hasn't expired (usually 10 minutes)
@ -556,6 +582,7 @@ Key points to log:
**Cause:** Database error during sync **Cause:** Database error during sync
**Solutions:** **Solutions:**
1. Check `user_profiles` table exists and has proper schema 1. Check `user_profiles` table exists and has proper schema
2. Verify Supabase connection and permissions 2. Verify Supabase connection and permissions
3. Check user_id isn't duplicated in database 3. Check user_id isn't duplicated in database

View file

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

View file

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

View file

@ -58,6 +58,7 @@ Foundation Endpoints:
### New Implementation Files ### New Implementation Files
#### Frontend OAuth Client (`code/client/lib/foundation-oauth.ts`) #### Frontend OAuth Client (`code/client/lib/foundation-oauth.ts`)
✅ **Implements PKCE (Proof Key for Code Exchange)** ✅ **Implements PKCE (Proof Key for Code Exchange)**
- Generates code verifier (64-char random, URL-safe) - Generates code verifier (64-char random, URL-safe)
@ -68,14 +69,16 @@ Foundation Endpoints:
- Stores verifier/state in sessionStorage - Stores verifier/state in sessionStorage
**Key Functions:** **Key Functions:**
```typescript ```typescript
getFoundationAuthorizationUrl() // Build auth URL getFoundationAuthorizationUrl(); // Build auth URL
initiateFoundationLogin() // Redirect to Foundation initiateFoundationLogin(); // Redirect to Foundation
exchangeCodeForToken() // Exchange code (called from backend) exchangeCodeForToken(); // Exchange code (called from backend)
validateState() // CSRF validation validateState(); // CSRF validation
``` ```
#### Token & Cookie Management (`code/client/lib/foundation-auth.ts`) #### Token & Cookie Management (`code/client/lib/foundation-auth.ts`)
✅ **Handles session cookies and authentication state** ✅ **Handles session cookies and authentication state**
- Get/check Foundation access token from cookies - Get/check Foundation access token from cookies
@ -85,16 +88,18 @@ validateState() // CSRF validation
- Logout notification to Foundation - Logout notification to Foundation
**Key Functions:** **Key Functions:**
```typescript ```typescript
getFoundationAccessToken() // Get JWT from cookie getFoundationAccessToken(); // Get JWT from cookie
getAuthUserId() // Get user UUID from cookie getAuthUserId(); // Get user UUID from cookie
isFoundationAuthenticated() // Check auth status isFoundationAuthenticated(); // Check auth status
clearFoundationAuth() // Logout clearFoundationAuth(); // Logout
makeAuthenticatedRequest() // API call with token makeAuthenticatedRequest(); // API call with token
logoutFromFoundation() // Full logout flow logoutFromFoundation(); // Full logout flow
``` ```
#### OAuth Callback Hook (`code/client/hooks/use-foundation-auth.ts`) #### OAuth Callback Hook (`code/client/hooks/use-foundation-auth.ts`)
✅ **Detects OAuth callback and handles token exchange** ✅ **Detects OAuth callback and handles token exchange**
- Detects authorization code in URL - Detects authorization code in URL
@ -105,16 +110,20 @@ logoutFromFoundation() // Full logout flow
- Error handling with user feedback - Error handling with user feedback
**Key Functions:** **Key Functions:**
```typescript ```typescript
useFoundationAuth() // Process OAuth callback useFoundationAuth(); // Process OAuth callback
useFoundationAuthStatus() // Check auth status useFoundationAuthStatus(); // Check auth status
``` ```
#### OAuth Callback Handler (`code/api/auth/callback.ts`) #### OAuth Callback Handler (`code/api/auth/callback.ts`)
✅ **Backend endpoint for OAuth flow completion** ✅ **Backend endpoint for OAuth flow completion**
**Two routes:** **Two routes:**
1. `GET /auth/callback?code=...&state=...` 1. `GET /auth/callback?code=...&state=...`
- Receives authorization code from Foundation - Receives authorization code from Foundation
- Validates state (CSRF) - Validates state (CSRF)
- Exchanges code for token - Exchanges code for token
@ -130,15 +139,17 @@ useFoundationAuthStatus() // Check auth status
- Sets secure cookies - Sets secure cookies
**Key Functions:** **Key Functions:**
```typescript ```typescript
handleCallback() // GET /auth/callback handleCallback(); // GET /auth/callback
handleTokenExchange() // POST /auth/callback/exchange handleTokenExchange(); // POST /auth/callback/exchange
performTokenExchange() // Code → token exchange performTokenExchange(); // Code → token exchange
fetchUserInfoFromFoundation() // Fetch user profile fetchUserInfoFromFoundation(); // Fetch user profile
syncUserToLocalDatabase() // Upsert to local DB syncUserToLocalDatabase(); // Upsert to local DB
``` ```
#### Updated Login Page (`code/client/pages/Login.tsx`) #### Updated Login Page (`code/client/pages/Login.tsx`)
✅ **New Foundation OAuth button** ✅ **New Foundation OAuth button**
- Added "Login with Foundation" button (primary option) - Added "Login with Foundation" button (primary option)
@ -147,6 +158,7 @@ syncUserToLocalDatabase() // Upsert to local DB
- Discord now managed by Foundation instead - Discord now managed by Foundation instead
**Changes:** **Changes:**
```typescript ```typescript
// NEW // NEW
<Button onClick={() => initiateFoundationLogin()}> <Button onClick={() => initiateFoundationLogin()}>
@ -160,6 +172,7 @@ syncUserToLocalDatabase() // Upsert to local DB
### Configuration Files ### Configuration Files
#### Example Environment Variables (`.env.foundation-oauth.example`) #### Example Environment Variables (`.env.foundation-oauth.example`)
```bash ```bash
VITE_FOUNDATION_URL=https://aethex.foundation VITE_FOUNDATION_URL=https://aethex.foundation
FOUNDATION_OAUTH_CLIENT_ID=aethex_corp FOUNDATION_OAUTH_CLIENT_ID=aethex_corp
@ -171,6 +184,7 @@ FOUNDATION_OAUTH_CLIENT_SECRET=bcoEtyQVGr6Z4557658eUXpDF5FDni2TGNahH3HT-FtylNrLC
✅ **Complete Documentation Provided:** ✅ **Complete Documentation Provided:**
1. **FOUNDATION-OAUTH-IMPLEMENTATION.md** (601 lines) 1. **FOUNDATION-OAUTH-IMPLEMENTATION.md** (601 lines)
- Complete technical guide - Complete technical guide
- PKCE explanation - PKCE explanation
- All endpoints documented - All endpoints documented
@ -268,7 +282,7 @@ FOUNDATION_OAUTH_CLIENT_SECRET=bcoEtyQVGr6Z4557658eUXpDF5FDni2TGNahH3HT-FtylNrLC
302 /dashboard 302 /dashboard
17. User appears logged in on aethex.dev dashboard ✅ 17. User appears logged in on aethex.dev dashboard ✅
Session established: Session established:
- foundation_access_token in cookie (HttpOnly, Secure) - foundation_access_token in cookie (HttpOnly, Secure)
- auth_user_id in cookie (Secure) - auth_user_id in cookie (Secure)
@ -297,7 +311,7 @@ Server stores challenge, issues code
On token exchange: On token exchange:
Client sends: code_verifier Client sends: code_verifier
Server verifies: SHA256(verifier) == stored_challenge Server verifies: SHA256(verifier) == stored_challenge
Result: Code can't be reused even if intercepted Result: Code can't be reused even if intercepted
``` ```
@ -335,20 +349,20 @@ document.cookie
const token = getFoundationAccessToken(); const token = getFoundationAccessToken();
// Make authenticated request // Make authenticated request
fetch('/api/user/profile', { fetch("/api/user/profile", {
headers: { 'Authorization': `Bearer ${token}` }, headers: { Authorization: `Bearer ${token}` },
credentials: 'include' // Include cookies credentials: "include", // Include cookies
}); });
// Or use helper // Or use helper
import { makeAuthenticatedRequest } from '@/lib/foundation-auth'; import { makeAuthenticatedRequest } from "@/lib/foundation-auth";
const response = await makeAuthenticatedRequest('/api/user/profile'); const response = await makeAuthenticatedRequest("/api/user/profile");
``` ```
### Logout ### Logout
```typescript ```typescript
import { logoutFromFoundation } from '@/lib/foundation-auth'; import { logoutFromFoundation } from "@/lib/foundation-auth";
// Logout button click handler // Logout button click handler
await logoutFromFoundation(); await logoutFromFoundation();
@ -398,13 +412,13 @@ Corp Local Database:
```typescript ```typescript
// On OAuth callback, sync user: // On OAuth callback, sync user:
await supabase.from("user_profiles").upsert({ await supabase.from("user_profiles").upsert({
id: foundationUser.id, // Use Foundation UUID id: foundationUser.id, // Use Foundation UUID
email: foundationUser.email, email: foundationUser.email,
username: foundationUser.username, username: foundationUser.username,
full_name: foundationUser.full_name, full_name: foundationUser.full_name,
avatar_url: foundationUser.avatar_url, avatar_url: foundationUser.avatar_url,
profile_completed: foundationUser.profile_complete, profile_completed: foundationUser.profile_complete,
updated_at: new Date().toISOString() updated_at: new Date().toISOString(),
}); });
// Result: // Result:
@ -494,15 +508,15 @@ Why? Foundation now handles all Discord OAuth
## Key Differences from Before ## Key Differences from Before
| Aspect | Before Phase 3 | After Phase 3 | | Aspect | Before Phase 3 | After Phase 3 |
|--------|---|---| | ---------------------- | --------------------- | ------------------------------------------ |
| **Identity Provider** | aethex.dev (local) | aethex.foundation (remote) | | **Identity Provider** | aethex.dev (local) | aethex.foundation (remote) |
| **Discord OAuth** | Handled on aethex.dev | Handled on Foundation | | **Discord OAuth** | Handled on aethex.dev | Handled on Foundation |
| **Session Token** | Supabase JWT | Foundation JWT | | **Session Token** | Supabase JWT | Foundation JWT |
| **User Profile Owner** | aethex.dev | aethex.foundation | | **User Profile Owner** | aethex.dev | aethex.foundation |
| **Login Flow** | Local Discord button | Redirect to Foundation | | **Login Flow** | Local Discord button | Redirect to Foundation |
| **Profile Updates** | Direct to Supabase | Sync from Foundation | | **Profile Updates** | Direct to Supabase | Sync from Foundation |
| **Passport Issuer** | Distributed | aethex.foundation (Single source of truth) | | **Passport Issuer** | Distributed | aethex.foundation (Single source of truth) |
--- ---
@ -529,7 +543,9 @@ Phase 3 is **successfully deployed when:**
## Documentation Provided ## Documentation Provided
### Implementation Guide ### Implementation Guide
📖 **`FOUNDATION-OAUTH-IMPLEMENTATION.md`** (601 lines) 📖 **`FOUNDATION-OAUTH-IMPLEMENTATION.md`** (601 lines)
- Technical deep-dive - Technical deep-dive
- PKCE explanation - PKCE explanation
- All endpoints documented - All endpoints documented
@ -538,7 +554,9 @@ Phase 3 is **successfully deployed when:**
- Troubleshooting guide - Troubleshooting guide
### Deployment Guide ### Deployment Guide
📖 **`DEPLOYMENT-CHECKLIST.md`** (470 lines) 📖 **`DEPLOYMENT-CHECKLIST.md`** (470 lines)
- Step-by-step deployment - Step-by-step deployment
- Environment setup - Environment setup
- Testing plan - Testing plan
@ -547,7 +565,9 @@ Phase 3 is **successfully deployed when:**
- Success criteria - Success criteria
### Code Documentation ### Code Documentation
✅ **Inline code comments** ✅ **Inline code comments**
- `foundation-oauth.ts` - PKCE + auth flow - `foundation-oauth.ts` - PKCE + auth flow
- `foundation-auth.ts` - Token management - `foundation-auth.ts` - Token management
- `use-foundation-auth.ts` - React hooks - `use-foundation-auth.ts` - React hooks
@ -558,12 +578,14 @@ Phase 3 is **successfully deployed when:**
## Next Steps ## Next Steps
### Immediate (Today) ### Immediate (Today)
1. Review implementation 1. Review implementation
2. Verify credentials are correct 2. Verify credentials are correct
3. Set environment variables in deployment platform 3. Set environment variables in deployment platform
4. Deploy to staging 4. Deploy to staging
### Short-term (This Week) ### Short-term (This Week)
1. Test complete OAuth flow 1. Test complete OAuth flow
2. Verify user syncing 2. Verify user syncing
3. Monitor logs for errors 3. Monitor logs for errors
@ -571,6 +593,7 @@ Phase 3 is **successfully deployed when:**
5. Deploy to production 5. Deploy to production
### Long-term (Next Week+) ### Long-term (Next Week+)
1. Monitor metrics (auth success rate, response times) 1. Monitor metrics (auth success rate, response times)
2. Remove old Discord OAuth code 2. Remove old Discord OAuth code
3. Update user documentation 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 ### Client-side OAuth Utilities
1. **`code/client/lib/foundation-oauth.ts`** 1. **`code/client/lib/foundation-oauth.ts`**
- OAuth flow initialization - OAuth flow initialization
- URL generation for Foundation redirect - URL generation for Foundation redirect
- `initiateFoundationLogin(redirectTo?)` - Main entry point for login - `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 - Storage helpers for redirect destinations
2. **`code/client/lib/foundation-auth.ts`** 2. **`code/client/lib/foundation-auth.ts`**
- Token and session management - Token and session management
- Cookie handling (`foundation_access_token`, `auth_user_id`) - Cookie handling (`foundation_access_token`, `auth_user_id`)
- User profile fetching from Foundation - User profile fetching from Foundation
@ -37,6 +39,7 @@ This Phase 3 implementation transforms `aethex.dev` from an identity provider in
### Backend OAuth Endpoints ### Backend OAuth Endpoints
4. **`code/api/auth/foundation-callback.ts`** 4. **`code/api/auth/foundation-callback.ts`**
- Callback endpoint: `GET /api/auth/foundation-callback?code=...&state=...` - Callback endpoint: `GET /api/auth/foundation-callback?code=...&state=...`
- Validates authorization code from Foundation - Validates authorization code from Foundation
- Exchanges code for access token - Exchanges code for access token
@ -54,6 +57,7 @@ This Phase 3 implementation transforms `aethex.dev` from an identity provider in
### Configuration & Documentation ### Configuration & Documentation
6. **`code/.env.foundation-oauth.example`** 6. **`code/.env.foundation-oauth.example`**
- Example environment variables - Example environment variables
- `VITE_FOUNDATION_URL` - Foundation identity provider URL - `VITE_FOUNDATION_URL` - Foundation identity provider URL
- `FOUNDATION_OAUTH_CLIENT_SECRET` - OAuth credentials - `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`** ### **`code/client/pages/Login.tsx`**
**Changes:** **Changes:**
- Added Foundation URL import - Added Foundation URL import
- Added `initiateFoundationLogin` import - Added `initiateFoundationLogin` import
- Replaced Discord OAuth button with "Login with Foundation" button - 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) - **Other Options** (Roblox, Ethereum)
**Old button removed:** **Old button removed:**
```javascript ```javascript
// REMOVED - Discord OAuth now handled by Foundation // 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 Discord
</Button> </Button>
``` ```
**New button added:** **New button added:**
```javascript ```javascript
<Button onClick={() => initiateFoundationLogin(redirectTo)}> <Button onClick={() => initiateFoundationLogin(redirectTo)}>
<Shield /> Login with Foundation <Shield /> Login with Foundation
@ -145,6 +152,7 @@ FOUNDATION_OAUTH_CLIENT_SECRET=<secret-from-foundation-setup>
``` ```
**Obtain these from Foundation admin:** **Obtain these from Foundation admin:**
1. After Foundation's Phase 1 setup is complete 1. After Foundation's Phase 1 setup is complete
2. Request OAuth client secret for `aethex-corp` 2. Request OAuth client secret for `aethex-corp`
3. Verify Foundation endpoints are operational 3. Verify Foundation endpoints are operational
@ -154,26 +162,28 @@ FOUNDATION_OAUTH_CLIENT_SECRET=<secret-from-foundation-setup>
## Key Differences from Phase 2 ## Key Differences from Phase 2
| Aspect | Phase 2 | Phase 3 | | Aspect | Phase 2 | Phase 3 |
|--------|--------|--------| | ------------------- | ------------------- | ------------------------------- |
| **Auth Provider** | Supabase (local) | Foundation (remote) | | **Auth Provider** | Supabase (local) | Foundation (remote) |
| **Identity Issuer** | aethex.dev | aethex.foundation | | **Identity Issuer** | aethex.dev | aethex.foundation |
| **Discord OAuth** | Handled locally | Handled by Foundation | | **Discord OAuth** | Handled locally | Handled by Foundation |
| **User Source** | Local Supabase | Foundation → synced locally | | **User Source** | Local Supabase | Foundation → synced locally |
| **Session Tokens** | Supabase JWT | Foundation JWT | | **Session Tokens** | Supabase JWT | Foundation JWT |
| **Profile Updates** | Direct to Supabase | Via Foundation sync | | **Profile Updates** | Direct to Supabase | Via Foundation sync |
| **Logout** | Clear local session | Notify Foundation + clear local | | **Logout** | Clear local session | Notify Foundation + clear local |
--- ---
## What Happens to Discord OAuth? ## What Happens to Discord OAuth?
**Old flow (Phase 2):** **Old flow (Phase 2):**
- User clicks Discord button on aethex.dev - User clicks Discord button on aethex.dev
- aethex.dev handles OAuth with Discord - aethex.dev handles OAuth with Discord
- Discord connects to aethex.dev's Supabase - Discord connects to aethex.dev's Supabase
**New flow (Phase 3):** **New flow (Phase 3):**
- User clicks "Login with Foundation" on aethex.dev - User clicks "Login with Foundation" on aethex.dev
- Redirected to aethex.foundation for authentication - Redirected to aethex.foundation for authentication
- User connects Discord on Foundation (if needed) - User connects Discord on Foundation (if needed)
@ -196,15 +206,15 @@ import { useFoundationAuth } from '@/hooks/use-foundation-auth';
export function App() { export function App() {
// Detects auth code in URL and handles exchange // Detects auth code in URL and handles exchange
const { isProcessing, error } = useFoundationAuth(); const { isProcessing, error } = useFoundationAuth();
if (isProcessing) { if (isProcessing) {
return <LoadingScreen />; // Show while processing Foundation callback return <LoadingScreen />; // Show while processing Foundation callback
} }
if (error) { if (error) {
// Handle auth error // Handle auth error
} }
return <AppContent />; return <AppContent />;
} }
``` ```
@ -218,11 +228,11 @@ import { useFoundationAuthStatus } from '@/hooks/use-foundation-auth';
function ProtectedRoute() { function ProtectedRoute() {
const { isAuthenticated } = useFoundationAuthStatus(); const { isAuthenticated } = useFoundationAuthStatus();
if (!isAuthenticated) { if (!isAuthenticated) {
return <Navigate to="/login" />; return <Navigate to="/login" />;
} }
return <Dashboard />; return <Dashboard />;
} }
``` ```
@ -232,15 +242,15 @@ function ProtectedRoute() {
Send Foundation token with requests: Send Foundation token with requests:
```typescript ```typescript
import { getFoundationAccessToken } from '@/lib/foundation-auth'; import { getFoundationAccessToken } from "@/lib/foundation-auth";
const token = getFoundationAccessToken(); const token = getFoundationAccessToken();
fetch('/api/user/profile', { fetch("/api/user/profile", {
headers: { headers: {
'Authorization': `Bearer ${token}` Authorization: `Bearer ${token}`,
}, },
credentials: 'include' credentials: "include",
}); });
``` ```
@ -266,6 +276,7 @@ fetch('/api/user/profile', {
## Next Steps ## Next Steps
### Immediate (Week 1) ### Immediate (Week 1)
1. ✅ Implement Phase 3 code (this document) 1. ✅ Implement Phase 3 code (this document)
2. ⏳ Set Foundation OAuth credentials 2. ⏳ Set Foundation OAuth credentials
3. ⏳ Deploy to staging 3. ⏳ Deploy to staging
@ -273,6 +284,7 @@ fetch('/api/user/profile', {
5. ⏳ Monitor for errors and issues 5. ⏳ Monitor for errors and issues
### Short-term (Week 2-3) ### Short-term (Week 2-3)
1. ⏳ Verify all existing users can re-authenticate 1. ⏳ Verify all existing users can re-authenticate
2. ⏳ Confirm user profile syncing works 2. ⏳ Confirm user profile syncing works
3. ⏳ Test role/permission inheritance from Foundation 3. ⏳ Test role/permission inheritance from Foundation
@ -280,6 +292,7 @@ fetch('/api/user/profile', {
5. ⏳ Update documentation 5. ⏳ Update documentation
### Future (Phase 4+) ### Future (Phase 4+)
1. ⏳ Remove email/password auth from aethex.dev 1. ⏳ Remove email/password auth from aethex.dev
2. ⏳ Remove Roblox/Ethereum OAuth (centralize at Foundation) 2. ⏳ Remove Roblox/Ethereum OAuth (centralize at Foundation)
3. ⏳ Implement cross-domain SSO 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 ## What You Need To Know
### Before Foundation Migrate ### Before Foundation Migrate
- **aethex.dev** handled all authentication (Discord OAuth, email/password) - **aethex.dev** handled all authentication (Discord OAuth, email/password)
- User identity was distributed across multiple systems - User identity was distributed across multiple systems
- Each application had its own auth logic - Each application had its own auth logic
### After Phase 3 Deployed ### After Phase 3 Deployed
- **aethex.foundation** is the authoritative identity provider - **aethex.foundation** is the authoritative identity provider
- **aethex.dev** redirects users to Foundation for authentication - **aethex.dev** redirects users to Foundation for authentication
- All Discord connections handled by Foundation - 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: The following files are new and handle Foundation OAuth:
**Client-side:** **Client-side:**
- `code/client/lib/foundation-oauth.ts` - OAuth flow - `code/client/lib/foundation-oauth.ts` - OAuth flow
- `code/client/lib/foundation-auth.ts` - Token management - `code/client/lib/foundation-auth.ts` - Token management
- `code/client/hooks/use-foundation-auth.ts` - React hooks - `code/client/hooks/use-foundation-auth.ts` - React hooks
- `code/client/pages/Login.tsx` - UPDATED with Foundation button - `code/client/pages/Login.tsx` - UPDATED with Foundation button
**Server-side:** **Server-side:**
- `code/api/auth/foundation-callback.ts` - OAuth callback handler - `code/api/auth/foundation-callback.ts` - OAuth callback handler
- `code/api/auth/exchange-token.ts` - Token exchange endpoint - `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 ## Key Changes in This Phase
### Login Page ### Login Page
- **Old:** Discord button redirected to local `/api/discord/oauth/start` - **Old:** Discord button redirected to local `/api/discord/oauth/start`
- **New:** "Login with Foundation" button redirects to `aethex.foundation` - **New:** "Login with Foundation" button redirects to `aethex.foundation`
### Authentication Flow ### Authentication Flow
- **Old:** Local Supabase auth → Discord OAuth locally → Session on aethex.dev - **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 - **New:** Redirect to Foundation → User auth on Foundation → Session on aethex.dev with Foundation token
### User Profile ### User Profile
- **Old:** Stored directly in aethex.dev's Supabase - **Old:** Stored directly in aethex.dev's Supabase
- **New:** Synced from Foundation's Supabase to aethex.dev's local copy - **New:** Synced from Foundation's Supabase to aethex.dev's local copy
### Discord Management ### Discord Management
- **Old:** aethex.dev handled all Discord connections - **Old:** aethex.dev handled all Discord connections
- **New:** Foundation handles all Discord connections; aethex.dev consumes the result - **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 ## Important Files
### New Components (Phase 3 Specific) ### New Components (Phase 3 Specific)
``` ```
code/ code/
├── client/ ├── client/
@ -106,6 +115,7 @@ code/
``` ```
### Configuration Files ### Configuration Files
``` ```
code/ code/
└── .env.foundation-oauth.example ← Example env vars └── .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 ## User Experience After Phase 3
### For New Users ### For New Users
1. Visit aethex.dev/login 1. Visit aethex.dev/login
2. See "Login with Foundation" button (primary option) 2. See "Login with Foundation" button (primary option)
3. Click it 3. Click it
@ -162,6 +173,7 @@ Discord OAuth is now **managed entirely by aethex.foundation**.
6. Complete onboarding with pre-filled Foundation data 6. Complete onboarding with pre-filled Foundation data
### For Existing Users ### For Existing Users
1. Existing sessions will be cleared (they had aethex.dev Supabase tokens) 1. Existing sessions will be cleared (they had aethex.dev Supabase tokens)
2. They'll be redirected to login page 2. They'll be redirected to login page
3. They click "Login with Foundation" 3. They click "Login with Foundation"
@ -208,15 +220,18 @@ Discord OAuth is now **managed entirely by aethex.foundation**.
If critical issues arise: If critical issues arise:
1. **Revert code:** 1. **Revert code:**
```bash ```bash
git revert <Phase3-commit-hash> git revert <Phase3-commit-hash>
``` ```
2. **Restore environment:** 2. **Restore environment:**
- Remove VITE_FOUNDATION_URL - Remove VITE_FOUNDATION_URL
- Remove FOUNDATION_OAUTH_CLIENT_SECRET - Remove FOUNDATION_OAUTH_CLIENT_SECRET
3. **Tell users:** 3. **Tell users:**
- "We've temporarily disabled Foundation integration" - "We've temporarily disabled Foundation integration"
- "Please use local login or Discord OAuth" - "Please use local login or Discord OAuth"
@ -229,6 +244,7 @@ If critical issues arise:
## Deployment Recommendations ## Deployment Recommendations
### Staging Deployment (First) ### Staging Deployment (First)
1. Deploy Phase 3 code to staging 1. Deploy Phase 3 code to staging
2. Set Foundation OAuth credentials on staging 2. Set Foundation OAuth credentials on staging
3. Test according to `PHASE3-TESTING-PLAN.md` 3. Test according to `PHASE3-TESTING-PLAN.md`
@ -236,6 +252,7 @@ If critical issues arise:
5. Monitor staging for 24 hours 5. Monitor staging for 24 hours
### Production Deployment ### Production Deployment
1. Create backup of current auth system 1. Create backup of current auth system
2. Deploy Phase 3 code gradually (canary deployment if possible) 2. Deploy Phase 3 code gradually (canary deployment if possible)
3. Set Foundation OAuth credentials in production 3. Set Foundation OAuth credentials in production
@ -244,6 +261,7 @@ If critical issues arise:
6. Communicate with users 6. Communicate with users
### Monitoring ### Monitoring
- Auth success rate (target >99%) - Auth success rate (target >99%)
- Token exchange time (target <2s) - Token exchange time (target <2s)
- Error messages in logs - Error messages in logs
@ -280,12 +298,14 @@ A: Users link Discord on Foundation instead. No linking needed on aethex.dev.
## Next Steps ## Next Steps
### Week 1: Setup ### Week 1: Setup
1. ✅ Code implemented (DONE) 1. ✅ Code implemented (DONE)
2. ⏳ Get Foundation OAuth credentials 2. ⏳ Get Foundation OAuth credentials
3. ⏳ Set environment variables 3. ⏳ Set environment variables
4. ⏳ Deploy to staging 4. ⏳ Deploy to staging
### Week 2: Testing ### Week 2: Testing
5. ⏳ Test complete auth flow 5. ⏳ Test complete auth flow
6. ⏳ Test error scenarios 6. ⏳ Test error scenarios
7. ⏳ Test on multiple browsers 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 9. ⏳ Get team approval
### Week 3: Deployment ### Week 3: Deployment
10. ⏳ Deploy to production 10. ⏳ Deploy to production
11. ⏳ Monitor closely for issues 11. ⏳ Monitor closely for issues
12. ⏳ Document any bugs found 12. ⏳ Document any bugs found
13. ⏳ Communicate with users 13. ⏳ Communicate with users
### Week 4+: Optimization ### Week 4+: Optimization
14. ⏳ Remove old Discord OAuth endpoints 14. ⏳ Remove old Discord OAuth endpoints
15. ⏳ Optimize token handling 15. ⏳ Optimize token handling
16. ⏳ Update documentation 16. ⏳ Update documentation
@ -322,16 +344,19 @@ Detailed documentation available:
If you encounter issues: If you encounter issues:
1. **Check logs:** 1. **Check logs:**
- Foundation callback logs (Vercel deployment) - Foundation callback logs (Vercel deployment)
- Token exchange errors - Token exchange errors
- Profile sync failures - Profile sync failures
2. **Verify environment:** 2. **Verify environment:**
- VITE_FOUNDATION_URL is correct - VITE_FOUNDATION_URL is correct
- FOUNDATION_OAUTH_CLIENT_SECRET is correct - FOUNDATION_OAUTH_CLIENT_SECRET is correct
- Foundation service is running - Foundation service is running
3. **Test manually:** 3. **Test manually:**
- Use curl to test token endpoint - Use curl to test token endpoint
- Check database for user profiles - Check database for user profiles
- Inspect cookies in browser - Inspect cookies in browser

View file

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

View file

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