Merge pull request #1065 from KrishavRajSingh/feat/remove_one_tap

removes one tap login
This commit is contained in:
Marko Kraemer 2025-07-25 00:19:00 +02:00 committed by GitHub
commit 3da5851890
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 26 additions and 188 deletions

View File

@ -1,220 +1,58 @@
'use client'; 'use client';
import { useEffect, useState } from 'react'; import { useState } from 'react';
import Script from 'next/script';
import { createClient } from '@/lib/supabase/client'; import { createClient } from '@/lib/supabase/client';
import { useAuthMethodTracking } from '@/lib/stores/auth-tracking';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { FcGoogle } from "react-icons/fc"; import { FcGoogle } from "react-icons/fc";
import { Loader2 } from 'lucide-react'; import { Loader2 } from 'lucide-react';
declare global {
interface Window {
google?: {
accounts: {
id: {
initialize: (config: any) => void;
renderButton: (element: HTMLElement, config: any) => void;
prompt: (notification?: (notification: any) => void) => void;
};
};
};
}
}
interface GoogleSignInProps { interface GoogleSignInProps {
returnUrl?: string; returnUrl?: string;
} }
export default function GoogleSignIn({ returnUrl }: GoogleSignInProps) { export default function GoogleSignIn({ returnUrl }: GoogleSignInProps) {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [isGoogleLoaded, setIsGoogleLoaded] = useState(false);
const { wasLastMethod, markAsUsed } = useAuthMethodTracking('google');
const supabase = createClient(); const supabase = createClient();
const handleGoogleResponse = async (response: any) => { const handleGoogleSignIn = async () => {
try { try {
setIsLoading(true); setIsLoading(true);
markAsUsed(); console.log('returnUrl', returnUrl);
const { data, error } = await supabase.auth.signInWithIdToken({ const { error } = await supabase.auth.signInWithOAuth({
provider: 'google', provider: 'google',
token: response.credential, options: {
redirectTo: `${window.location.origin}/auth/callback${
returnUrl ? `?returnUrl=${encodeURIComponent(returnUrl)}` : ''
}`,
},
}); });
if (error) { if (error) {
const redirectTo = `${window.location.origin}/auth/callback${returnUrl ? `?returnUrl=${encodeURIComponent(returnUrl)}` : ''}`; throw error;
console.log('OAuth redirect URI:', redirectTo);
const { error: oauthError } = await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
redirectTo,
},
});
if (oauthError) {
throw oauthError;
}
} else {
window.location.href = returnUrl || '/dashboard';
} }
} catch (error: any) { } catch (error: any) {
console.error('Google sign-in error:', error); console.error('Google sign-in error:', error);
toast.error(error.message || 'Failed to sign in with Google');
if (error.message?.includes('redirect_uri_mismatch')) {
const redirectUri = `${window.location.origin}/auth/callback`;
toast.error(
`Google OAuth configuration error. Add this exact URL to your Google Cloud Console: ${redirectUri}`,
{ duration: 10000 }
);
} else {
toast.error(error.message || 'Failed to sign in with Google');
}
setIsLoading(false); setIsLoading(false);
} }
}; };
useEffect(() => {
const initializeGoogleSignIn = () => {
if (!window.google || !process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID) return;
window.google.accounts.id.initialize({
client_id: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
callback: handleGoogleResponse,
auto_select: false,
cancel_on_tap_outside: false,
});
setIsGoogleLoaded(true);
};
if (window.google) {
initializeGoogleSignIn();
}
}, [returnUrl, markAsUsed, supabase]);
const handleScriptLoad = () => {
if (window.google && process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID) {
window.google.accounts.id.initialize({
client_id: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
callback: handleGoogleResponse,
auto_select: false,
cancel_on_tap_outside: false,
});
setIsGoogleLoaded(true);
}
};
const handleGoogleSignIn = () => {
if (!window.google || !isGoogleLoaded) {
toast.error('Google Sign-In is still loading. Please try again.');
return;
}
try {
window.google.accounts.id.prompt((notification: any) => {
if (notification.isNotDisplayed() || notification.isSkippedMoment()) {
console.log('One Tap not displayed, using OAuth flow');
setIsLoading(true);
const redirectTo = `${window.location.origin}/auth/callback${returnUrl ? `?returnUrl=${encodeURIComponent(returnUrl)}` : ''}`;
console.log('OAuth redirect URI:', redirectTo);
supabase.auth.signInWithOAuth({
provider: 'google',
options: {
redirectTo,
},
}).then(({ error }) => {
if (error) {
console.error('OAuth error:', error);
if (error.message?.includes('redirect_uri_mismatch')) {
const redirectUri = `${window.location.origin}/auth/callback`;
toast.error(
`Google OAuth configuration error. Add this exact URL to your Google Cloud Console: ${redirectUri}`,
{ duration: 10000 }
);
} else {
toast.error(error.message || 'Failed to sign in with Google');
}
setIsLoading(false);
}
});
}
});
} catch (error) {
console.error('Error triggering Google sign-in:', error);
setIsLoading(true);
const redirectTo = `${window.location.origin}/auth/callback${returnUrl ? `?returnUrl=${encodeURIComponent(returnUrl)}` : ''}`;
supabase.auth.signInWithOAuth({
provider: 'google',
options: {
redirectTo,
},
}).then(({ error }) => {
if (error) {
console.error('OAuth error:', error);
if (error.message?.includes('redirect_uri_mismatch')) {
const redirectUri = `${window.location.origin}/auth/callback`;
toast.error(
`Google OAuth configuration error. Add this exact URL to your Google Cloud Console: ${redirectUri}`,
{ duration: 10000 }
);
} else {
toast.error(error.message || 'Failed to sign in with Google');
}
setIsLoading(false);
}
});
}
};
if (!process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID) {
return (
<div className="w-full text-center text-sm text-gray-500 py-3">
Google Sign-In not configured
</div>
);
}
return ( return (
<div className="relative"> <button
<button onClick={handleGoogleSignIn}
onClick={handleGoogleSignIn} disabled={isLoading}
disabled={isLoading || !isGoogleLoaded} className="w-full h-12 flex items-center justify-center text-sm font-medium tracking-wide rounded-full bg-background text-foreground border border-border hover:bg-accent/30 transition-all duration-200 disabled:opacity-60 disabled:cursor-not-allowed font-sans"
className="w-full h-12 flex items-center justify-center text-sm font-medium tracking-wide rounded-full bg-background text-foreground border border-border hover:bg-accent/30 transition-all duration-200 disabled:opacity-60 disabled:cursor-not-allowed font-sans" type="button"
aria-label={isLoading ? 'Signing in with Google...' : 'Sign in with Google'} >
type="button" {isLoading ? (
> <Loader2 className="w-4 h-4 mr-2 animate-spin" />
{isLoading ? ( ) : (
<Loader2 className="w-4 h-4 mr-2 animate-spin" /> <FcGoogle className="w-4 h-4 mr-2" />
) : (
<FcGoogle className="w-4 h-4 mr-2" />
)}
<span className="font-medium">
{isLoading ? 'Signing in...' : 'Continue with Google'}
</span>
</button>
{wasLastMethod && (
<div className="absolute -top-1 -right-1 w-3 h-3 bg-green-500 rounded-full border-2 border-background shadow-sm">
<div className="w-full h-full bg-green-500 rounded-full animate-pulse" />
</div>
)} )}
<span className="font-medium">
<Script {isLoading ? 'Signing in...' : 'Continue with Google'}
src="https://accounts.google.com/gsi/client" </span>
strategy="afterInteractive" </button>
onLoad={handleScriptLoad}
/>
</div>
); );
} }