Skip to content

Commit 7f09488

Browse files
Programcodeclaude
andcommitted
fix: use SDK PKCE flow for Google sign-in (no fallback URL)
The previous Promise.race between signInWithOAuth and a 1200ms timeout could time-out under any kind of network blip and fall back to a hand-built OAuth URL via buildGoogleOAuthUrl. That fallback URL does not include the PKCE code_verifier that the SDK stores in localStorage when it returns its own URL — so when Google called back to /app, the SDK could not complete the code-for-token exchange and the user was stuck on "Redirecting…" until they refreshed and tried again. Now we always use the SDK URL. If the SDK fails outright we surface a clean error instead of silently degrading to a broken flow. Ported from Part 5 commit 1e29930 with no dependency on the Capacitor native helpers or edge-function-error wrapper. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 137bf53 commit 7f09488

1 file changed

Lines changed: 24 additions & 24 deletions

File tree

‎src/lib/supabase/AuthProvider.tsx‎

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
33
import type { Session, User } from '@supabase/supabase-js';
44
import { supabase } from './client';
5-
import { buildGoogleOAuthUrl } from './oauth-url';
65
import { useSessionStore } from '@/lib/stores/session-store';
76
import { useSettingsStore } from '@/lib/stores/settings-store';
87

@@ -151,29 +150,30 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
151150
user: session?.user ?? null,
152151
loading,
153152
signInWithGoogle: async ({ driveAccess = false } = {}) => {
154-
const fallbackUrl = buildGoogleOAuthUrl({ driveAccess });
155-
const result = await Promise.race([
156-
supabase.auth.signInWithOAuth({
157-
provider: 'google',
158-
options: {
159-
redirectTo: `${window.location.origin}/app`,
160-
skipBrowserRedirect: true,
161-
...(driveAccess ? { scopes: 'https://www.googleapis.com/auth/drive.file' } : {}),
162-
queryParams: driveAccess
163-
? {
164-
access_type: 'offline',
165-
prompt: 'consent',
166-
}
167-
: undefined,
168-
},
169-
}),
170-
new Promise<null>((resolve) => setTimeout(() => resolve(null), 1200)),
171-
]);
172-
173-
if (result?.error) throw result.error;
174-
175-
const redirectUrl = result?.data?.url || fallbackUrl;
176-
window.location.assign(redirectUrl);
153+
// Always use the SDK-generated URL so PKCE's code_verifier is written
154+
// to storage before redirect. The previous Promise.race against a 1200ms
155+
// timeout could fall back to a hand-built URL that skipped PKCE — Google
156+
// would then call back to /app but the SDK couldn't exchange the code,
157+
// leaving the user stuck on "Redirecting…" until manual refresh.
158+
const { data, error } = await supabase.auth.signInWithOAuth({
159+
provider: 'google',
160+
options: {
161+
redirectTo: `${window.location.origin}/app`,
162+
skipBrowserRedirect: true,
163+
...(driveAccess ? { scopes: 'https://www.googleapis.com/auth/drive.file' } : {}),
164+
queryParams: driveAccess
165+
? {
166+
access_type: 'offline',
167+
prompt: 'consent',
168+
}
169+
: undefined,
170+
},
171+
});
172+
173+
if (error) throw error;
174+
if (!data?.url) throw new Error('Google sign-in could not start. Please try again.');
175+
176+
window.location.assign(data.url);
177177
},
178178
signOut: async () => {
179179
await supabase.auth.signOut();

0 commit comments

Comments
 (0)