refactor: remove PKCE from OIDC flow (confidential client)
Backend holds client_secret and does the token exchange server-side, making PKCE redundant. Removes code_verifier/code_challenge from all frontend auth paths and backend exchange method. Eliminates the source of "grant request is invalid" errors from verifier mismatches. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,22 +14,6 @@ interface OidcInfo {
|
||||
additionalScopes?: string[];
|
||||
}
|
||||
|
||||
/** Generate a random code_verifier for PKCE (RFC 7636). */
|
||||
function generateCodeVerifier(): string {
|
||||
const array = new Uint8Array(32);
|
||||
crypto.getRandomValues(array);
|
||||
return btoa(String.fromCharCode(...array))
|
||||
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
||||
}
|
||||
|
||||
/** Derive the S256 code_challenge from a code_verifier. */
|
||||
async function deriveCodeChallenge(verifier: string): Promise<string> {
|
||||
const data = new TextEncoder().encode(verifier);
|
||||
const digest = await crypto.subtle.digest('SHA-256', data);
|
||||
return btoa(String.fromCharCode(...new Uint8Array(digest)))
|
||||
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
||||
}
|
||||
|
||||
const SUBTITLES = [
|
||||
"Prove you're not a mirage",
|
||||
"Only authorized cameleers beyond this dune",
|
||||
@@ -92,22 +76,16 @@ export function LoginPage() {
|
||||
if (oidc && !forceLocal && !autoRedirected.current) {
|
||||
autoRedirected.current = true;
|
||||
const redirectUri = `${window.location.origin}${config.basePath}oidc/callback`;
|
||||
const verifier = generateCodeVerifier();
|
||||
sessionStorage.setItem('oidc-code-verifier', verifier);
|
||||
deriveCodeChallenge(verifier).then((challenge) => {
|
||||
const scopes = ['openid', 'email', 'profile', ...(oidc.additionalScopes || [])];
|
||||
const params = new URLSearchParams({
|
||||
response_type: 'code',
|
||||
client_id: oidc.clientId,
|
||||
redirect_uri: redirectUri,
|
||||
scope: scopes.join(' '),
|
||||
prompt: 'none',
|
||||
code_challenge: challenge,
|
||||
code_challenge_method: 'S256',
|
||||
});
|
||||
if (oidc.resource) params.set('resource', oidc.resource);
|
||||
window.location.href = `${oidc.authorizationEndpoint}?${params}`;
|
||||
const scopes = ['openid', 'email', 'profile', ...(oidc.additionalScopes || [])];
|
||||
const params = new URLSearchParams({
|
||||
response_type: 'code',
|
||||
client_id: oidc.clientId,
|
||||
redirect_uri: redirectUri,
|
||||
scope: scopes.join(' '),
|
||||
prompt: 'none',
|
||||
});
|
||||
if (oidc.resource) params.set('resource', oidc.resource);
|
||||
window.location.href = `${oidc.authorizationEndpoint}?${params}`;
|
||||
}
|
||||
}, [oidc, forceLocal]);
|
||||
|
||||
@@ -118,21 +96,16 @@ export function LoginPage() {
|
||||
login(username, password);
|
||||
};
|
||||
|
||||
const handleOidcLogin = async () => {
|
||||
const handleOidcLogin = () => {
|
||||
if (!oidc) return;
|
||||
setOidcLoading(true);
|
||||
const redirectUri = `${window.location.origin}${config.basePath}oidc/callback`;
|
||||
const verifier = generateCodeVerifier();
|
||||
sessionStorage.setItem('oidc-code-verifier', verifier);
|
||||
const challenge = await deriveCodeChallenge(verifier);
|
||||
const scopes = ['openid', 'email', 'profile', ...(oidc.additionalScopes || [])];
|
||||
const params = new URLSearchParams({
|
||||
response_type: 'code',
|
||||
client_id: oidc.clientId,
|
||||
redirect_uri: redirectUri,
|
||||
scope: scopes.join(' '),
|
||||
code_challenge: challenge,
|
||||
code_challenge_method: 'S256',
|
||||
});
|
||||
if (oidc.resource) params.set('resource', oidc.resource);
|
||||
window.location.href = `${oidc.authorizationEndpoint}?${params}`;
|
||||
|
||||
Reference in New Issue
Block a user