{"record":{"id":"a1636adccf3112d4","repo":"immich-app/immich","slug":"error-backchannel-logout-token-validation-failed","errorCode":null,"errorMessage":"Error backchannel logout: token validation failed","messagePattern":"Error backchannel logout: token validation failed","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/services/auth.service.ts","lineNumber":106,"sourceCode":"      successful: true,\n      redirectUri: await this.getLogoutEndpoint(authType, oauthBearerToken),\n    };\n  }\n\n  async backchannelLogout(dto: OAuthBackchannelLogoutDto): Promise<void> {\n    const { oauth } = await this.getConfig({ withCache: false });\n    if (!oauth.enabled) {\n      throw new BadRequestException('Received backchannel logout request but OAuth is not enabled');\n    }\n\n    let claims;\n    try {\n      claims = await this.oauthRepository.validateLogoutToken(oauth, dto.logout_token);\n    } catch (error: Error | any) {\n      this.logger.error(`Error backchannel logout: ${error.message}`);\n      this.logger.error(error);\n\n      throw new BadRequestException('Error backchannel logout: token validation failed');\n    }\n\n    if (!claims) {\n      throw new BadRequestException('Invalid logout token: no claims found');\n    }\n\n    if (!claims.sub && !claims.sid) {\n      throw new BadRequestException('Invalid logout token: it must contain either a sub or a sid claim');\n    }\n\n    const deletedSessionIds = await this.sessionRepository.invalidateOAuth({\n      oauthSid: claims.sid,\n      oauthId: claims.sub,\n    });\n\n    for (const sessionId of deletedSessionIds) {\n      await this.eventRepository.emit('SessionDelete', { sessionId });\n    }","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/auth.service.ts#L88-L124","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// No purely client-side validation can replace server token verification;\n// verify issuer/audience config matches the IdP before relying on back-channel logout.\nconst { data: oauth } = await api.get('/oauth/config');\nif (!oauth.enabled || !oauth.issuer) {\n  throw new Error('OAuth/issuer not configured; back-channel logout cannot validate.');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await api.post('/oauth/backchannel-logout', { logout_token });\n} catch (e) {\n  if (e.response?.status === 400 && /token validation failed/i.test(e.response?.data?.message)) {\n    // check server logs: full error object is logged there\n    logValidationFailure(e);\n  } else throw e;\n}","preventionTips":["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."],"tags":["auth","oauth","logout","backchannel","token","validation"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}