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

  1. Inspect the server error log (the full error object is logged) to see which validation step failed.
  2. Confirm the IdP's issuer and audience match the server's oauth config exactly.
  3. Ensure signing keys (JWKS) are reachable and up to date on the server side.
  4. 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

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


AI-assisted analysis of immich-app/immich@199723261c (2026-08-12). Data as JSON: /api/errors/a1636adccf3112d4. Report an issue: GitHub.