immich-app/immich · error · BadRequestException
Error backchannel logout: token validation failed
Error message
Error backchannel logout: token validation failed
What it means
Thrown by backchannelLogout when oauthRepository.validateLogoutToken throws while verifying the logout_token. Validation covers signature, issuer, audience, token-type (must not contain a nonce), and the 'events' claim per the OIDC back-channel logout spec. Any failure is caught, logged at error level (including the underlying error object), and re-raised as 400 BadRequest 'Error backchannel logout: token validation failed'.
Source
Thrown at server/src/services/auth.service.ts:106
successful: true,
redirectUri: await this.getLogoutEndpoint(authType, oauthBearerToken),
};
}
async backchannelLogout(dto: OAuthBackchannelLogoutDto): Promise<void> {
const { oauth } = await this.getConfig({ withCache: false });
if (!oauth.enabled) {
throw new BadRequestException('Received backchannel logout request but OAuth is not enabled');
}
let claims;
try {
claims = await this.oauthRepository.validateLogoutToken(oauth, dto.logout_token);
} catch (error: Error | any) {
this.logger.error(`Error backchannel logout: ${error.message}`);
this.logger.error(error);
throw new BadRequestException('Error backchannel logout: token validation failed');
}
if (!claims) {
throw new BadRequestException('Invalid logout token: no claims found');
}
if (!claims.sub && !claims.sid) {
throw new BadRequestException('Invalid logout token: it must contain either a sub or a sid claim');
}
const deletedSessionIds = await this.sessionRepository.invalidateOAuth({
oauthSid: claims.sid,
oauthId: claims.sub,
});
for (const sessionId of deletedSessionIds) {
await this.eventRepository.emit('SessionDelete', { sessionId });
}View on GitHub (pinned to 199723261c)
Solutions
- Inspect the server error log (the full error object is logged) to see which validation step failed.
- Confirm the IdP's issuer and audience match the server's oauth config exactly.
- Ensure signing keys (JWKS) are reachable and up to date on the server side.
- Verify server/IdP clocks are in sync (NTP).
Defensive patterns
Strategy: try-catch
Validate before calling
// No purely client-side validation can replace server token verification;
// verify issuer/audience config matches the IdP before relying on back-channel logout.
const { data: oauth } = await api.get('/oauth/config');
if (!oauth.enabled || !oauth.issuer) {
throw new Error('OAuth/issuer not configured; back-channel logout cannot validate.');
} Try / catch
try {
await api.post('/oauth/backchannel-logout', { logout_token });
} catch (e) {
if (e.response?.status === 400 && /token validation failed/i.test(e.response?.data?.message)) {
// check server logs: full error object is logged there
logValidationFailure(e);
} else throw e;
} Prevention
- Keep JWKS/signing keys reachable and current on the server.
- Match issuer and audience exactly between IdP and server config.
- Sync server and IdP clocks via NTP.
When it happens
Trigger: IdP sends a logout_token with a bad/expired signature; issuer or audience in the token does not match the server's OAuth config; token is malformed JSON/JWT; clock skew between IdP and server; wrong signing key rotation.
Common situations: IdP signing keys rotated but not fetched by the server; issuer URL mismatch (trailing slash, http vs https); token replay after expiry; network MITM altering the token.
Related errors
- Invalid logout token: no claims found
- Invalid logout token: it must contain either a sub or a sid
- Received backchannel logout request but OAuth is not enabled
- password is required
- Password login has been disabled
AI-assisted analysis of immich-app/immich@199723261c (2026-08-12).
Data as JSON: /api/errors/a1636adccf3112d4.
Report an issue: GitHub.