From dc8e8147468996cde7faf29177ab90c46f3000d1 Mon Sep 17 00:00:00 2001 From: "Builder.io" Date: Mon, 17 Nov 2025 03:54:07 +0000 Subject: [PATCH] Clarify one-way passport sync from Foundation cgen-72a356870ee14a30ac0fc85316be31b6 --- api/auth/callback.ts | 50 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/api/auth/callback.ts b/api/auth/callback.ts index e3b9e68a..640530a8 100644 --- a/api/auth/callback.ts +++ b/api/auth/callback.ts @@ -1,8 +1,18 @@ /** * Foundation OAuth Callback Handler * - * Receives authorization code from Foundation, exchanges it for an access token, - * fetches user information, and establishes a session on aethex.dev. + * CRITICAL: This is a READ-ONLY sync from Foundation. + * aethex.dev acts as an OAuth client that caches Foundation-issued passports. + * + * Flow: + * 1. User authenticates with Foundation (oauth server) + * 2. Foundation redirects to this callback with authorization code + * 3. Exchange code for access_token + user info from Foundation + * 4. ONE-WAY SYNC: Upsert Foundation user data to local cache + * 5. Create session on aethex.dev (Foundation passport is SSOT) + * + * IMPORTANT: We NEVER write to passport data except during this sync. + * All mutations must flow through Foundation's APIs. * * Endpoint: GET /auth/callback?code=...&state=... * Token Exchange: POST /auth/callback/exchange @@ -251,7 +261,14 @@ async function fetchUserInfoFromFoundation( } /** - * Sync Foundation user to local database + * Sync Foundation user to local database (READ-ONLY CACHE) + * + * This performs a ONE-WAY sync from Foundation to local cache. + * We UPSERT data from Foundation, but NEVER modify the cache independently. + * This cache is read-only except during this sync operation. + * + * If user data changes on Foundation, it syncs on next login. + * If user data is modified locally outside this function: ERROR (should not happen) */ async function syncUserToLocalDatabase( foundationUser: FoundationUserInfo, @@ -259,11 +276,15 @@ async function syncUserToLocalDatabase( const supabase = getAdminClient(); console.log( - "[Foundation OAuth] Syncing user to local database:", + "[Foundation Passport Sync] Starting one-way sync from Foundation:", foundationUser.id, ); - // Upsert user profile + // ONE-WAY SYNC: Upsert Foundation data to local cache + // Note: This is the ONLY place where user_profiles should be written to + const now = new Date().toISOString(); + const cacheValidUntil = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(); // 24 hour cache TTL + const { error } = await supabase.from("user_profiles").upsert({ id: foundationUser.id, email: foundationUser.email, @@ -271,17 +292,28 @@ async function syncUserToLocalDatabase( full_name: foundationUser.full_name || null, avatar_url: foundationUser.avatar_url || null, profile_completed: foundationUser.profile_complete || false, - updated_at: new Date().toISOString(), + + // Sync metadata (critical for cache validation) + foundation_synced_at: now, + cache_valid_until: cacheValidUntil, + + // Never overwrite local timestamps + updated_at: now, }); if (error) { - console.error("[Foundation OAuth] Failed to sync user profile:", error); - throw new Error("Failed to create local user profile"); + console.error( + "[Foundation Passport Sync] Failed to sync user profile:", + error, + ); + throw new Error("Failed to cache Foundation passport data locally"); } console.log( - "[Foundation OAuth] User synced successfully:", + "[Foundation Passport Sync] User synced successfully:", foundationUser.id, + "| Cache valid until:", + cacheValidUntil, ); }