TLDR:Rul ned og kopier koden. Du behøver kun at tilføje din login-logik.
Denne artikel vil dække:
- En kort introduktion til det nye Google Log In-api
- Sådan implementeres det ved hjælp af React og Typescript
- Tilføj relevante indtastninger på globalt vinduesobjekt
Intro
Google annoncerede for nylig, at de stopper deres gamle godkendelsestjeneste "Google Log-In" til fordel for deres nye og forbedrede tjeneste "Log ind med Google".
Deres nye service kommer i to dele:
- Login-knap
- Et tryk
Du kan læse mere om dem her.
Vi vil dække den første i denne artikel, hvordan det virker, og hvordan man implementerer det i React with Typescript.
Sammenlignet med den gamle tjeneste er denne meget nemmere at bruge. Det er ligetil nok at implementere login-knappen selv uden at skulle bruge et bibliotek som (den fantastiske) react-google-login
det er go-to-løsningen til det gamle api.
Google Auth-introduktion
Jeg vil lige gennemgå det grundlæggende her.
Ansvarsfraskrivelse:Der er muligvis en meget bedre måde at gøre dette på. Jeg ville være glad for at vide hvordan, så læg en kommentar! Jeg kunne ikke finde nogle eksempler på dette, så jeg regnede med, at jeg ville poste min implementering og forhåbentlig hjælpe en anden.
Selvom det nye auth api er lidt vanskeligt at få hovedet omkring i starten, når du bruger React, kan vi få det til at fungere. Tricket er at forstå, hvordan scriptet indlæser klienten, og hvordan det passer med Reacts indlæsning og gengivelse.
Google-dokumentationen dækker både html- og javascript-api, og vi vil bruge sidstnævnte. Men da vi bygger med React, bruger vi for det meste trin-for-trin guiden til at finde ud af, hvordan auth api'en fungerer. Vi skal redegøre for, hvordan React indlæser og gengiver elementer. Desværre betyder det, at vi ikke bare statisk kan sætte det i overskriften, som guiden instruerer.
Når du har fulgt opsætningsprocessen, fortæller dokumentationen dig, at du skal tilføje et script-tag til din header (i public/index.html
), men da vi bruger React, er vi ikke vil gøre det. Vi kommer til at kontrollere, hvornår og hvor vi kører det script, og dermed starte google auth-klienten. Vi gør dette, fordi scriptet starter en klient, og vi ønsker at give det vores egen tilbagekaldsfunktion, som vi definerer med react.
// The script that runs and load the new google auth client.
// We're not(!) adding it to our header like the guide says.
<script src="https://accounts.google.com/gsi/client" async defer></script>
Lad os komme i gang
For det første vil Typescript klage over manglende typer på window
objekt. Vi ordner det ordentligt senere.
Det, vi først implementerer, er at tilføje scriptet, der indlæser Google Auth-klienten, når vores login-side gengives, tilføje "target div", som scriptet leder efter, og starte klienten med vores tilbagekaldsfunktion.
Problemet
At knytte denne tilbagekaldsfunktion til Google-klienten er det, der gør det lidt besværligt at bruge det nye auth-api med React. (men endnu mere ved at bruge den gamle!). Hvis vi tilføjer script-tagget til den statiske html, som dokumenterne siger, kan vi ikke videregive nogen funktion defineret i react. Vi kunne måske håndtere ting ved at definere en funktion på serversiden af tingene, men jeg vil gerne blive inden for React og håndtere dette på front-end og bruge mine graphql-hooks til at logge ind.
Processen
Når vores login-side gengives, vedhæfter vi Google-klient-scriptet til headeren inde fra en useEffect
krog. Vi tilføjer en initialiseringsfunktion til onLoad
-eventlistener for det script-tag. OnLoad-hændelsen vil derefter udløse og initialisere Google Auth-klienten med vores tilbagekald tilknyttet.
Google-klienten vil derefter på magisk vis finde vores allerede gengivet div
med id=g_id_signin
og gengiv login-knappen.
En flot, personlig google-logon-knap skulle nu være synlig for brugeren.
Koden
import { Button } from "@material-ui/core"
import { useEffect, useState } from "react"
export default function GoogleSignin() {
const [gsiScriptLoaded, setGsiScriptLoaded] = useState(false)
const [user, setUser] = useState(undefined)
useEffect(() => {
if (user?._id || gsiScriptLoaded) return
const initializeGsi = () => {
// Typescript will complain about window.google
// Add types to your `react-app-env.d.ts` or //@ts-ignore it.
if (!window.google || gsiScriptLoaded) return
setGsiScriptLoaded(true)
window.google.accounts.id.initialize({
client_id: GOOGLE_CLIENT_ID,
callback: handleGoogleSignIn,
})
}
const script = document.createElement("script")
script.src = "https://accounts.google.com/gsi/client"
script.onload = initializeGsi
script.async = true
script.id = "google-client-script"
document.querySelector("body")?.appendChild(script)
return () => {
// Cleanup function that runs when component unmounts
window.google?.accounts.id.cancel()
document.getElementById("google-client-script")?.remove()
}
}, [handleGoogleSignIn, initializeGsi, user?._id])
const handleGoogleSignIn = (res: CredentialResponse) => {
if (!res.clientId || !res.credential) return
// Implement your login mutations and logic here.
// Set cookies, call your backend, etc.
setUser(val.data?.login.user)
})
}
return <Button className={"g_id_signin"} />
}
Du vil måske tilføje nogle flere implementeringsdetaljer her og der. Men dette er kernen i det! Du kan i det mindste bruge det som udgangspunkt. Håber det hjælper!
Reparation af vinduestyperne
Hvis du bruger create-react-app
, vil du allerede have filen react-app-env.d.ts
i dit projektrod. Du kan tilføje typerne til google auth api der. Jeg oversatte api-dokumentationen til typescript-typer. Der kan være nogle fejl, da jeg ikke har brugt og testet alle funktionerne. Men det skal være korrekt.
/// <reference types="react-scripts" />
interface IdConfiguration {
client_id: string
auto_select?: boolean
callback: (handleCredentialResponse: CredentialResponse) => void
login_uri?: string
native_callback?: Function
cancel_on_tap_outside?: boolean
prompt_parent_id?: string
nonce?: string
context?: string
state_cookie_domain?: string
ux_mode?: "popup" | "redirect"
allowed_parent_origin?: string | string[]
intermediate_iframe_close_callback?: Function
}
interface CredentialResponse {
credential?: string
select_by?:
| "auto"
| "user"
| "user_1tap"
| "user_2tap"
| "btn"
| "btn_confirm"
| "brn_add_session"
| "btn_confirm_add_session"
clientId?: string
}
interface GsiButtonConfiguration {
type: "standard" | "icon"
theme?: "outline" | "filled_blue" | "filled_black"
size?: "large" | "medium" | "small"
text?: "signin_with" | "signup_with" | "continue_with" | "signup_with"
shape?: "rectangular" | "pill" | "circle" | "square"
logo_alignment?: "left" | "center"
width?: string
local?: string
}
interface PromptMomentNotification {
isDisplayMoment: () => boolean
isDisplayed: () => boolean
isNotDisplayed: () => boolean
getNotDisplayedReason: () =>
| "browser_not_supported"
| "invalid_client"
| "missing_client_id"
| "opt_out_or_no_session"
| "secure_http_required"
| "suppressed_by_user"
| "unregistered_origin"
| "unknown_reason"
isSkippedMoment: () => boolean
getSkippedReason: () =>
| "auto_cancel"
| "user_cancel"
| "tap_outside"
| "issuing_failed"
isDismissedMoment: () => boolean
getDismissedReason: () =>
| "credential_returned"
| "cancel_called"
| "flow_restarted"
getMomentType: () => "display" | "skipped" | "dismissed"
}
interface Window {
google?: {
accounts: {
id: {
initialize: (input: IdConfiguration) => void
prompt: (
momentListener: (res: PromptMomentNotification) => void
) => void
renderButton: (
parent: HTMLElement,
options: GsiButtonConfiguration,
clickHandler: Function
) => void
disableAutoSelect: Function
storeCredential: Function<{
credentials: { id: string; password: string }
callback: Function
}>
cancel: () => void
onGoogleLibraryLoad: Function
revoke: Function<{
hint: string
callback: Function<{ successful: boolean; error: string }>
}>
}
}
}
}
Skamløst stik
Hvis du kan lide denne slags ting og leder efter et job i Sverige, Gøteborg, så kontakt mig!