From 45e2d1b2d210ff294cd3dba32e1a416d67824a98 Mon Sep 17 00:00:00 2001 From: Bayan Bennett <16971062+BayanBennett@users.noreply.github.com> Date: Thu, 17 Jul 2025 11:53:53 -0700 Subject: [PATCH] docs(auth-google): use `onReady` in Next.js one-tap component example (#37227) * Update auth-google.mdx * Update apps/docs/content/guides/auth/social-login/auth-google.mdx * prettier fix --- .../guides/auth/social-login/auth-google.mdx | 124 ++++++++---------- 1 file changed, 57 insertions(+), 67 deletions(-) diff --git a/apps/docs/content/guides/auth/social-login/auth-google.mdx b/apps/docs/content/guides/auth/social-login/auth-google.mdx index aa82bd3dcaf..bb15372b26b 100644 --- a/apps/docs/content/guides/auth/social-login/auth-google.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-google.mdx @@ -376,82 +376,72 @@ If you're integrating Google One-Tap with your Next.js application, you can refe import Script from 'next/script' import { createClient } from '@/utils/supabase/client' -import { CredentialResponse } from 'google-one-tap' +import type { accounts, CredentialResponse } from 'google-one-tap' import { useRouter } from 'next/navigation' -import { useEffect } from 'react' + +declare const google: { accounts: accounts } + +// generate nonce to use for google id token sign-in +const generateNonce = async (): Promise => { + const nonce = btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(32)))) + const encoder = new TextEncoder() + const encodedNonce = encoder.encode(nonce) + const hashBuffer = await crypto.subtle.digest('SHA-256', encodedNonce) + const hashArray = Array.from(new Uint8Array(hashBuffer)) + const hashedNonce = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('') + + return [nonce, hashedNonce] +} const OneTapComponent = () => { const supabase = createClient() const router = useRouter() - // generate nonce to use for google id token sign-in - const generateNonce = async (): Promise => { - const nonce = btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(32)))) - const encoder = new TextEncoder() - const encodedNonce = encoder.encode(nonce) - const hashBuffer = await crypto.subtle.digest('SHA-256', encodedNonce) - const hashArray = Array.from(new Uint8Array(hashBuffer)) - const hashedNonce = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('') + const initializeGoogleOneTap = () => { + console.log('Initializing Google One Tap') + const [nonce, hashedNonce] = await generateNonce() + console.log('Nonce: ', nonce, hashedNonce) - return [nonce, hashedNonce] + // check if there's already an existing session before initializing the one-tap UI + const { data, error } = await supabase.auth.getSession() + if (error) { + console.error('Error getting session', error) + } + if (data.session) { + router.push('/') + return + } + + /* global google */ + google.accounts.id.initialize({ + client_id: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID, + callback: async (response: CredentialResponse) => { + try { + // send id token returned in response.credential to supabase + const { data, error } = await supabase.auth.signInWithIdToken({ + provider: 'google', + token: response.credential, + nonce, + }) + + if (error) throw error + console.log('Session data: ', data) + console.log('Successfully logged in with Google One Tap') + + // redirect to protected page + router.push('/') + } catch (error) { + console.error('Error logging in with Google One Tap', error) + } + }, + nonce: hashedNonce, + // with chrome's removal of third-party cookies, we need to use FedCM instead (https://developers.google.com/identity/gsi/web/guides/fedcm-migration) + use_fedcm_for_prompt: true, + }) + google.accounts.id.prompt() // Display the One Tap UI } - useEffect(() => { - const initializeGoogleOneTap = () => { - console.log('Initializing Google One Tap') - window.addEventListener('load', async () => { - const [nonce, hashedNonce] = await generateNonce() - console.log('Nonce: ', nonce, hashedNonce) - - // check if there's already an existing session before initializing the one-tap UI - const { data, error } = await supabase.auth.getSession() - if (error) { - console.error('Error getting session', error) - } - if (data.session) { - router.push('/') - return - } - - /* global google */ - google.accounts.id.initialize({ - client_id: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID, - callback: async (response: CredentialResponse) => { - try { - // send id token returned in response.credential to supabase - const { data, error } = await supabase.auth.signInWithIdToken({ - provider: 'google', - token: response.credential, - nonce, - }) - - if (error) throw error - console.log('Session data: ', data) - console.log('Successfully logged in with Google One Tap') - - // redirect to protected page - router.push('/') - } catch (error) { - console.error('Error logging in with Google One Tap', error) - } - }, - nonce: hashedNonce, - // with chrome's removal of third-party cookies, we need to use FedCM instead (https://developers.google.com/identity/gsi/web/guides/fedcm-migration) - use_fedcm_for_prompt: true, - }) - google.accounts.id.prompt() // Display the One Tap UI - }) - } - initializeGoogleOneTap() - return () => window.removeEventListener('load', initializeGoogleOneTap) - }, []) - - return ( - <> -