Frontend (React)
PKCE helpers
export function randomString(len = 64) {
const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
const buf = new Uint8Array(len);
crypto.getRandomValues(buf);
return Array.from(buf, x => charset[x % charset.length]).join('');
}
export async function sha256Base64Url(input: string) {
const data = new TextEncoder().encode(input);
const digest = await crypto.subtle.digest('SHA-256', data);
const b64 = btoa(String.fromCharCode(...new Uint8Array(digest)));
return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}Login button
const BASE_URL = 'https://<BASE_URL>';
async function loginWithGoogle() {
const state = crypto.randomUUID();
const codeVerifier = randomString(64);
localStorage.setItem('code_verifier', codeVerifier);
localStorage.setItem('oauth_state', state);
const codeChallenge = await sha256Base64Url(codeVerifier);
const res = await fetch(`${BASE_URL}/oauth/init`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({
provider: 'google',
redirect_uri: `${location.origin}/oauth/callback`,
state,
code_challenge: codeChallenge
})
});
const { url } = await res.json();
location.href = url;
}
Callback
const BASE_URL = 'https://<BASE_URL>';
export async function handleOAuthCallback() {
const p = new URLSearchParams(location.search);
const state = p.get('wallet_oauth_state');
const code = p.get('wallet_oauth_code');
const verifier = localStorage.getItem('code_verifier');
await fetch(`${BASE_URL}/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ type: 'GoogleOAuth', data: { state, code, code_verifier: verifier } })
});
const me = await fetch(`${BASE_URL}/session`, { credentials: 'include' }).then(r => r.json());
console.log(me);
}
Last updated