Skip to content

Instantly share code, notes, and snippets.

@xuannghia
Last active July 25, 2024 04:58
Show Gist options
  • Save xuannghia/36a4f64c7d3a3ccb359a72c18a9a1643 to your computer and use it in GitHub Desktop.
Save xuannghia/36a4f64c7d3a3ccb359a72c18a9a1643 to your computer and use it in GitHub Desktop.
React Google OAuth2
import { useState } from 'react'
import { useGoogleOAuth2 } from './use-google-oauth2.ts'
export function App() {
const [isLoggingIn, setIsLoggingIn] = useState(false)
const { loaded, signIn } = useGoogleOAuth2({
clientId: 'GOOGLE_CLIENT_ID',
})
const handleSignIn = async () => {
setIsLoggingIn(true)
try {
const { code } = await signIn()
const res = await fetch('/api/check-google-code', { method: "POST", body: JSON.stringify({ code }) })
if (res.ok) alert('Done')
else throw new Error("Failed to login with Google, please try again")
} catch (error) {
alert('Failed to login with Google, please try again')
} finally {
setIsLoggingIn(false)
}
}
return (
<button onClick={handleSignIn} className="w-full" disabled={!loaded}>
<IconGoogleFilled className="mr-2" />
{isLoggingIn ? 'Logging in...' : 'Continue with Google'}
</button>
)
}
import { useCallback, useEffect, useState } from 'react'
const LIB_URL = 'https://accounts.google.com/gsi/client'
const DOM_ID = 'google-signin-script'
function useGoogleSigninScript() {
const [loaded, setLoaded] = useState(false)
useEffect(() => {
if (document.getElementById(DOM_ID)) {
return
}
const script = document.createElement('script')
script.id = DOM_ID
script.src = LIB_URL
script.async = true
script.defer = true
script.onload = () => {
setLoaded(true)
}
document.body.appendChild(script)
return () => {
document.body.removeChild(script)
}
}, [])
return { loaded }
}
interface Client {
requestAccessToken: () => void
}
interface SuccessResponse {
accessToken: string
}
interface ErrorResponse {
message: string
type: 'popup_failed_to_open' | 'popup_closed' | 'unknown'
}
interface GFormsClientParams {
clientId: string
onSuccess?: (res: SuccessResponse) => void
onError?: (err: ErrorResponse) => void
}
export function useGFormsClient(params: GFormsClientParams) {
const { clientId, onSuccess, onError } = params
const { loaded } = useGoogleSigninScript()
const [client, setClient] = useState<Client>()
useEffect(() => {
if (!loaded) return
const _client = google.accounts.oauth2.initTokenClient({
client_id: clientId,
scope: 'openid email profile https://www.googleapis.com/auth/forms https://www.googleapis.com/auth/drive',
callback: (res: { access_token: string }) => {
onSuccess?.({ accessToken: res.access_token })
},
error_callback: (err: ErrorResponse) => {
onError?.(err)
},
})
setClient(_client)
}, [loaded, clientId, onSuccess, onError])
return { client, loaded }
}
export function useGoogleOAuth2({ clientId }: { clientId: string }) {
const { loaded } = useGoogleSigninScript()
const signIn = useCallback(async (): Promise<{ code: string }> => {
return new Promise((resolve, reject) => {
if (!loaded) {
reject({ message: 'Google Signin Script not loaded', type: 'unknown' })
return
}
const _client = google.accounts.oauth2.initCodeClient({
client_id: clientId,
scope: 'openid email profile',
ux_mode: 'popup',
callback: (res: { code: string }) => {
resolve(res)
},
error_callback: (err: ErrorResponse) => {
reject(err)
},
})
_client.requestCode()
})
}, [loaded, clientId])
return { loaded, signIn }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment