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 | |
} |