Last active
July 25, 2024 04:58
-
-
Save xuannghia/36a4f64c7d3a3ccb359a72c18a9a1643 to your computer and use it in GitHub Desktop.
React Google OAuth2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | |
) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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