Skip to content

Commit d37e2b2

Browse files
committed
refactor
1 parent 9b7556e commit d37e2b2

1 file changed

Lines changed: 34 additions & 15 deletions

File tree

packages/webapp/components/GoogleOneTapAuth.tsx

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,24 @@ import { CredentialResponse } from 'google-one-tap'
44
import { useRouter } from 'next/router'
55
import { useEffect, useCallback, useRef } from 'react'
66

7+
// generate nonce to use for google id token sign-in
8+
const generateNonce = async (): Promise<string[]> => {
9+
const nonce = btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(32))))
10+
const encoder = new TextEncoder()
11+
const encodedNonce = encoder.encode(nonce)
12+
const hashBuffer = await crypto.subtle.digest('SHA-256', encodedNonce)
13+
const hashArray = Array.from(new Uint8Array(hashBuffer))
14+
const hashedNonce = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
15+
16+
return [nonce, hashedNonce]
17+
}
18+
719
const OneTapComponent = () => {
820
const supabase = createClient()
921
const router = useRouter()
1022
const initializationRef = useRef(false)
1123
const abortControllerRef = useRef<AbortController | null>(null)
24+
const nonceRef = useRef<string | null>(null)
1225

1326
const handleCredentialResponse = useCallback(
1427
async (response: CredentialResponse) => {
@@ -18,27 +31,28 @@ const OneTapComponent = () => {
1831
return
1932
}
2033

21-
const { data, error } = await supabase.auth
22-
.signInWithIdToken({
23-
provider: 'google',
24-
token: response.credential
25-
})
26-
.catch((err) => {
27-
throw new Error(`Authentication failed: ${err.message}`)
28-
})
29-
30-
if (error) {
31-
console.error('Supabase auth error:', error.message)
32-
throw error
34+
if (!nonceRef.current) {
35+
console.error('Nonce not available')
36+
return
3337
}
3438

35-
console.info('Successfully logged in with Google One Tap')
36-
await router.reload()
39+
// send id token returned in response.credential to supabase
40+
const { data, error } = await supabase.auth.signInWithIdToken({
41+
provider: 'google',
42+
token: response.credential,
43+
nonce: nonceRef.current
44+
})
45+
46+
if (error) throw error
47+
console.log('Session data:', data)
48+
console.log('Successfully logged in with Google One Tap')
49+
// TODO: many states depend on profile auth, so silently updating the profile isn't enough. we need a better solution or a different call flow later.
50+
router.reload()
3751
} catch (error) {
3852
console.error('Error logging in with Google One Tap:', error)
3953
}
4054
},
41-
[supabase.auth, router]
55+
[supabase.auth]
4256
)
4357

4458
const initializeGoogleOneTap = useCallback(async () => {
@@ -67,6 +81,10 @@ const OneTapComponent = () => {
6781
return
6882
}
6983

84+
// Generate nonce
85+
const [nonce, hashedNonce] = await generateNonce()
86+
nonceRef.current = nonce
87+
7088
// Wait for Google script with retry mechanism
7189
let attempts = 0
7290
const maxAttempts = 20 // 4 seconds max wait
@@ -96,6 +114,7 @@ const OneTapComponent = () => {
96114
window.google.accounts.id.initialize({
97115
client_id: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
98116
callback: handleCredentialResponse,
117+
nonce: hashedNonce,
99118
auto_select: false,
100119
cancel_on_tap_outside: true,
101120
use_fedcm_for_prompt: true

0 commit comments

Comments
 (0)