immich-app/immich · error
OAuth login failed
Error message
OAuth login failed
What it means
getProfileAndOAuthSid wraps any failure during the OAuth code exchange / token verification / profile retrieval into a single 'OAuth login failed' error, preserving the underlying cause. It is a top-level umbrella error for the whole OIDC login flow, so the original error is attached as `cause` and logged.
Solutions
- Inspect the `cause` property / server logs ('OAuth login failed' with error) for the real failure
- Verify clientId, clientSecret, and redirect URI match the provider's registered app settings
- Check token endpoint auth method configuration matches what the provider expects
- Confirm server clock is NTP-synchronized and the provider is reachable from the server
Example fix
// before
try { login(); } catch (e) { console.log(e.message); } // 'OAuth login failed'
// after
try { login(); } catch (e) { console.log(e.message, e.cause); } // surfaces root cause Defensive patterns
Strategy: try-catch
Validate before calling
// before redirecting, sanity-check config assert(clientId && clientSecret, 'OAuth client credentials missing'); assert(registeredRedirectUris.includes(redirectUri), 'redirectUri not registered with provider');
Type guard
const hasCause = (e: unknown): e is Error & { cause?: unknown } => e instanceof Error && 'cause' in e; Try / catch
try {
await oauthRepo.getProfileAndOAuthSid(tokens);
} catch (e) {
logger.error('OAuth login failed', { cause: (e as any).cause ?? e });
return redirect('/login?error=oauth');
} Prevention
- Surface e.cause in logs — the umbrella message hides the root reason
- Keep client secrets and redirect URIs in sync with the provider dashboard
- Synchronize server clocks (NTP) to avoid token expiry false positives
- Store PKCE verifier/state in a shared store when running multiple instances
When it happens
Trigger: Any exception inside the login flow: code/token exchange failure, ID token verification failure (bad issuer/audience/expiry), PKCE mismatch, client authentication rejection, or profile fetch failure during getProfileAndOAuthSid().
Common situations: Wrong client secret after rotation; redirect URI mismatch causing token exchange rejection; clock skew making tokens 'expired'; provider outage; state/PKCE verifier lost across a scaled deployment; user denied consent at the provider.
Related errors
- Failed login attempt for user
- Password login has been disabled
- Unable to register / . User does not exist and auto…
- authToken is required
- Error backchannel logout: token validation failed
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/a9f9b5db52e5d39b.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/repositories/oauth.repository.ts:128
if (typeof claims?.sid === 'string') {
sid = claims.sid;
}
}
return { profile, sid, idToken: tokens.id_token };
} catch (error: Error | any) {
if (error.message.includes('unexpected JWT alg received')) {
this.logger.warn(
[
'Algorithm mismatch. Make sure the signing algorithm is set correctly in the OAuth settings.',
'Or, that you have specified a signing key in your OAuth provider.',
].join(' '),
);
}
this.logger.error('OAuth login failed', error);
throw new Error('OAuth login failed', { cause: error });
}
}
async getProfilePicture(url: string) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to fetch picture: ${response.statusText}`);
}
return response.arrayBuffer();
}
private jwksClients: Map<string, JWTVerifyGetKey> = new Map(); // useful for caching and performnce
async validateLogoutToken(config: OAuthConfig, logoutToken: string): Promise<{ sub?: string; sid?: string } | null> {
const client = await this.getClient(config);
const algorithm = client.clientMetadata().id_token_signed_response_alg ?? 'RS256';
let keyOrGetter: Uint8Array | JWTVerifyGetKey;
View on GitHub (pinned to e55ac299a4)