From 3cc466c12d1af069eafbab9e73e50aaf27574d11 Mon Sep 17 00:00:00 2001 From: aewing Date: Sun, 18 Jan 2026 04:15:37 +0000 Subject: [PATCH] Fix: Add fallback token retrieval endpoint and improved token extraction debugging --- Mode3Test/backend/server.ts | 42 ++++++++++++++++++++++++++++++++++ Mode3Test/frontend/index.html | 38 +++++++++++++++++++++++++----- Mode3Test/frontend/signin.html | 38 ++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 6 deletions(-) diff --git a/Mode3Test/backend/server.ts b/Mode3Test/backend/server.ts index 2303013..72bc251 100644 --- a/Mode3Test/backend/server.ts +++ b/Mode3Test/backend/server.ts @@ -82,6 +82,48 @@ app.get('/version', (c) => { } }); +// Get Graph token from PocketBase auth data +app.get('/api/graph-token', async (c) => { + try { + const authHeader = c.req.header('Authorization'); + if (!authHeader || !authHeader.startsWith('Bearer ')) { + return c.json({ error: 'Unauthorized' }, 401); + } + + const pbToken = authHeader.substring(7); + + // Fetch the authenticated user's data from PocketBase + const response = await fetch('https://pocketbase.ccllc.pro/api/collections/users/auth-refresh', { + method: 'POST', + headers: { 'Authorization': `Bearer ${pbToken}` } + }); + + if (!response.ok) { + return c.json({ error: 'Failed to refresh auth' }, response.status); + } + + const data = await response.json(); + const meta = data?.meta || {}; + + // Extract Graph token from all possible locations + const graphToken = + meta?.graphAccessToken || + meta?.graph_token || + meta?.graphToken || + meta?.accessToken || + meta?.access_token || + meta?.token || + meta?.rawToken || + meta?.authData?.access_token || + ''; + + return c.json({ token: graphToken }); + } catch (err) { + logLine('logs/error.log', `graph-token endpoint error: ${(err as Error)?.message || String(err)}`); + return c.json({ error: 'Failed to get token', token: '' }, 500); + } +}); + // Jobs endpoint with Redis caching app.get('/api/jobs', async (c) => { const page = parseInt(c.req.query('page') || '1'); diff --git a/Mode3Test/frontend/index.html b/Mode3Test/frontend/index.html index 1fafeae..947d00d 100644 --- a/Mode3Test/frontend/index.html +++ b/Mode3Test/frontend/index.html @@ -280,6 +280,7 @@ let refreshPromise = null; let lastRefreshTime = 0; const REFRESH_COOLDOWN = 2000; // 2 second cooldown between refreshes + let refreshFailureCount = 0; const refreshGraphToken = async () => { try { @@ -290,7 +291,9 @@ // If refreshed recently, return cached token if (Date.now() - lastRefreshTime < REFRESH_COOLDOWN) { - return getGraphToken(); + const cached = getGraphToken(); + if (cached) return cached; + // Even within cooldown, if no cached token, still try refresh } // Start new refresh @@ -299,9 +302,22 @@ const authData = await pb.collection('users').authRefresh(); const meta = authData?.meta || pb?.authStore?.model?.meta || {}; const token = extractGraphToken(meta); - if (token) localStorage.setItem(GRAPH_TOKEN_KEY, token); - lastRefreshTime = Date.now(); - return token || ''; + + if (token) { + localStorage.setItem(GRAPH_TOKEN_KEY, token); + lastRefreshTime = Date.now(); + refreshFailureCount = 0; + console.log('✓ Graph token refreshed'); + return token; + } else { + console.warn('Graph token missing from auth response'); + refreshFailureCount++; + return ''; + } + } catch (err) { + console.warn('Token refresh request failed:', err?.message||err); + refreshFailureCount++; + throw err; } finally { refreshPromise = null; } @@ -310,8 +326,18 @@ return await refreshPromise; } catch(err){ refreshPromise = null; - console.warn('Graph token refresh failed:', err?.message||err); - return getGraphToken(); // Return cached token if refresh fails + console.error('❌ Graph token refresh failed completely:', err?.message||err); + // If refresh failed multiple times, force sign-in + if (refreshFailureCount >= 2) { + console.warn('Token refresh failed multiple times. Forcing sign-in.'); + localStorage.removeItem(GRAPH_TOKEN_KEY); + try { pb?.authStore?.clear?.(); } catch {} + try { localStorage.removeItem('pocketbase_auth'); } catch {} + try { sessionStorage.clear(); } catch {} + window.location.href = 'signin.html?expired=1'; + return ''; + } + return ''; } } diff --git a/Mode3Test/frontend/signin.html b/Mode3Test/frontend/signin.html index a0c3792..4ce6193 100644 --- a/Mode3Test/frontend/signin.html +++ b/Mode3Test/frontend/signin.html @@ -101,13 +101,51 @@ const email = authData?.meta?.rawUser?.email || authData?.record?.email || pb.authStore.model?.email || 'Not available'; const graphToken = extractGraphToken(authData); console.log('Extracted Graph token:', graphToken ? graphToken.substring(0, 30) + '...' : 'NOT FOUND'); + console.log('Full meta object:', JSON.stringify(authData?.meta, null, 2)); + if (graphToken) { localStorage.setItem(GRAPH_TOKEN_KEY, graphToken); localStorage.removeItem(GRAPH_REAUTH_FLAG); + console.log('✓ Graph token stored'); + } else { + console.error('❌ Graph token NOT found in auth response!'); + console.error('Available meta keys:', Object.keys(authData?.meta || {})); + // If token not in response, try fetching it from the backend + console.log('Attempting to fetch token from backend...'); + fetchTokenFromBackend().then(token => { + if (token) { + localStorage.setItem(GRAPH_TOKEN_KEY, token); + console.log('✓ Graph token fetched from backend'); + } else { + console.warn('⚠ Graph token could not be obtained from backend'); + } + showAuthedUI(displayName, email); + }).catch(err => { + console.error('Failed to fetch token from backend:', err); + showAuthedUI(displayName, email); + }); + return; } showAuthedUI(displayName, email); } + async function fetchTokenFromBackend() { + try { + const response = await fetch('/api/graph-token', { + headers: { + 'Authorization': `Bearer ${pb.authStore.token}` + } + }); + if (response.ok) { + const data = await response.json(); + return data.token || ''; + } + } catch (err) { + console.warn('Token fetch error:', err.message); + } + return ''; + } + (async () => { const params = new URLSearchParams(window.location.search); const reauth = params.get('reauth') === '1';