toeverything/AFFiNE · error · InvalidOauthResponse
invalid_oauth_response
invalid_oauth_response
Error message
Invalid OAuth response: Missing PKCE challenge for OIDC authorization request.
What it means
OIDCProvider.getAuthUrl builds the authorization URL from the state blob. When the provider requires PKCE but the state payload carries no pkce.codeChallenge/codeChallengeMethod (or the state string isn't a parseable state envelope, so parsedState is null), it refuses to construct a PKCE-less authorization request and throws InvalidOauthResponse. PKCE material is minted server-side in /oauth/preflight and embedded in state — its absence means the state didn't come from a current preflight.
Source
Thrown at packages/backend/server/src/plugins/oauth/providers/oidc.ts:252
`OIDC discovery validation failed, retrying in ${delay}ms`
);
}
private resetState() {
this.#endpoints = null;
this.#jwks = null;
}
getAuthUrl(state: string): string {
const parsedState = this.parseStatePayload(state);
const nonce = parsedState?.state ?? state;
const pkce = parsedState?.pkce;
if (
this.requiresPkce &&
(!pkce?.codeChallenge || !pkce.codeChallengeMethod)
) {
throw new InvalidOauthResponse({
reason: 'Missing PKCE challenge for OIDC authorization request',
});
}
const query: JWTPayload = {
client_id: this.config.clientId,
redirect_uri: this.url.link('/oauth/callback'),
scope: this.resolveScope(this.config.args?.scope),
response_type: 'code',
...omit(
this.config.args,
'claim_id',
'claim_email',
'claim_name',
'claim_email_verified'
),
state,
nonce,View on GitHub (pinned to b4c8548c09)
Solutions
- Always initiate login via POST /oauth/preflight and use the state/authorize URL it returns — never construct state yourself
- Invalidate cached/pending authorize URLs after upgrading the server; users mid-flow should restart login
- If the IdP mandates PKCE (e.g. config requires it), ensure the server version that created the state already supports PKCE
Defensive patterns
Strategy: validation
Validate before calling
// Only ever feed getAuthUrl the state string produced by POST /oauth/preflight
const { state, authorize_url } = await post('/oauth/preflight', {
provider: 'OIDC', client: 'web', client_nonce: crypto.randomUUID(),
});
// verify it is the envelope format this server version issues
const parsed = JSON.parse(state);
if (!parsed.state || !parsed.pkce?.codeChallenge) {
throw new Error('Preflight returned a state without PKCE — server/provider PKCE config mismatch');
}
window.location.href = authorize_url ?? provider.getAuthUrl(state); Type guard
function isPkceStateEnvelope(s: string): boolean {
try {
const p = JSON.parse(s);
return typeof p.state === 'string' && typeof p.pkce?.codeChallenge === 'string';
} catch { return false; }
} Try / catch
try {
window.location.href = provider.getAuthUrl(state);
} catch (e) {
if ((e as any).code === 'invalid_oauth_response' && /PKCE/i.test((e as any).args?.reason ?? '')) {
return restartFromPreflight(); // state predates PKCE — mint a fresh one
}
throw e;
} Prevention
- Never construct authorize URLs or state yourself; always obtain them from the current server's preflight
- After server upgrades, treat pre-existing login tabs/URLs as expired and force users to restart
- Reject cached authorize URLs on the client (bookmark/back-button) before redirecting
When it happens
Trigger: Calling getAuthUrl with a raw/legacy state string: a state from a login started before the server upgrade that added PKCE, a state string hand-crafted by a third-party client, or a stale cached authorize URL being re-used after the IdP turned on PKCE enforcement.
Common situations: Server upgraded mid-flight — users with an open login tab carry old-format state; custom clients building their own authorize URLs instead of going through preflight; browser back-button resubmitting an old authorize URL.
Related errors
- invalid_auth_state
- oauth_state_expired
- missing_oauth_query_parameter
- wrong_sign_in_method
- missing_oauth_query_parameter
AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18).
Data as JSON: /api/errors/c30703dacb99dca6.
Report an issue: GitHub.