For a example on how to request a code/link, see:
cloudgarden-passwordless-react-example-with-pkce/src/App.tsx
Lines 41 to 68 in d51fb91
| function PasswordlessLoginRequestCodePage() { | |
| let navigate = useNavigate(); | |
| const { requestLoginCode, loading } = usePasswordless(REDIRECT_URI, CLIENT_ID) | |
| async function handleSubmit(event: React.FormEvent<HTMLFormElement>) { | |
| event.preventDefault(); | |
| let formData = new FormData(event.currentTarget) | |
| let email = formData.get("email") as string | |
| await requestLoginCode(email) | |
| navigate('/claim') | |
| } | |
| if (loading) return <p>Loading...</p> | |
| return ( | |
| <div> | |
| <form onSubmit={handleSubmit}> | |
| <label> | |
| Email: <input name="email" type="email" /> | |
| </label> | |
| <button type="submit">Send code</button> | |
| </form> | |
| </div> | |
| ); | |
| } |
| const requestLoginCode = async (email: string, clientId?: string, redirectUri?: string): Promise<void> => { | |
| setLoading(true) | |
| const codeVerifier = generateCodeVerifier() | |
| const codeChallengeMethod = 'sha256' | |
| const codeChallenge = await generateCodechallenge(codeVerifier) | |
| await fetch(`${BASE_ENDPOINT}/auth/passwordlessLogin/code`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| credentials: 'same-origin', | |
| body: JSON.stringify({ | |
| email, | |
| client_id: clientId || clientIdDefault, | |
| redirect_uri: redirectUri || redirectUriDefault, | |
| code_challenge_method: codeChallengeMethod, | |
| code_challenge: codeChallenge | |
| }) | |
| }) | |
| setCodeVerifier(codeVerifier) | |
| setLoading(false) | |
| } |
For a example on how to use the code/link, see:
cloudgarden-passwordless-react-example-with-pkce/src/App.tsx
Lines 70 to 117 in d51fb91
| function PasswordlessLoginClaimCodePage() { | |
| const { code } = useQuery() | |
| const [tokens, setTokens] = React.useState() | |
| const { claimLoginCode, loading } = usePasswordless(REDIRECT_URI, CLIENT_ID) | |
| async function handleSubmit(event: React.FormEvent<HTMLFormElement>) { | |
| event.preventDefault(); | |
| let formData = new FormData(event.currentTarget); | |
| let code = formData.get("code") as string; | |
| const res = await claimLoginCode(code) | |
| setTokens(res) | |
| } | |
| const tokenEffectFn = async () => { | |
| if (!code) return | |
| const res = await claimLoginCode(code) | |
| setTokens(res) | |
| } | |
| // @ts-expect-error 2345 | |
| React.useEffect(tokenEffectFn, [code]) | |
| if (loading) return <p>Loading...</p> | |
| return ( | |
| <div> | |
| {code && !tokens && ( | |
| <p>Your code is: {code}</p> | |
| )} | |
| {!code && !tokens && ( | |
| <form> | |
| <label> | |
| Code: <input name="code" type="text" /> | |
| </label> | |
| <button type="submit">Claim code</button> | |
| </form> | |
| )} | |
| {tokens && ( | |
| <pre>{JSON.stringify(tokens, null, 2)}</pre> | |
| )} | |
| </div> | |
| ) | |
| } |
| const claimLoginCode = async (code: string, clientId?: string, redirectUri?: string): Promise<any> => { | |
| setLoading(true) | |
| const codeVerifier = getCodeVerifier() | |
| const response = await fetch(`${BASE_ENDPOINT}/auth/oauth2/token`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| credentials: 'same-origin', | |
| body: JSON.stringify({ | |
| grant_type: 'authorization_code', | |
| code, | |
| code_verifier: codeVerifier, | |
| client_id: clientId || clientIdDefault, | |
| redirect_uri: redirectUri || redirectUriDefault, | |
| }) | |
| }) | |
| const tokens = await response.json() | |
| setLoading(false) | |
| return tokens | |
| } |