{"record":{"id":"0af8fd5bc77ca008","repo":"immich-app/immich","slug":"invalid-logout-token-no-claims-found","errorCode":null,"errorMessage":"Invalid logout token: no claims found","messagePattern":"Invalid logout token: no claims found","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"server/src/services/auth.service.ts","lineNumber":110,"sourceCode":"\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    }\n  }\n\n  async changePassword(auth: AuthDto, dto: ChangePasswordDto): Promise<UserAdminResponseDto> {\n    const { password, newPassword } = dto;","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/auth.service.ts#L92-L128","documentation":"Thrown by backchannelLogout after validateLogoutToken returns without throwing but yields a falsy claims object. Per the OIDC back-channel logout spec the token must carry identifiable claims; if the validated token has no usable claims the server rejects it with 400 BadRequest 'Invalid logout token: no claims found'. This is distinct from error 75 (validation threw) and error 77 (claims present but incomplete).","triggerScenarios":"A logout_token that is structurally valid (signature/issuer ok) but whose payload decodes to null/empty claims; IdP issued a token with an empty payload section.","commonSituations":"Misconfigured IdP emitting minimal logout tokens; token corruption that drops the payload while preserving the header/signature; non-standard IdP behavior.","solutions":["Decode the logout_token JWT locally (base64 of the payload) and inspect the claims.","Have the IdP re-issue the logout token with a proper claims payload (sub/sid/events).","Confirm the IdP implements OIDC back-channel logout (RFC) rather than a custom variant."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Inspect the logout_token payload before sending/forwarding.\nfunction parseJwtPayload(token: string): any {\n  const part = token.split('.')[1];\n  return JSON.parse(Buffer.from(part, 'base64').toString('utf8'));\n}\nconst claims = parseJwtPayload(logout_token);\nif (!claims || Object.keys(claims).length === 0) {\n  throw new Error('Logout token has no claims; request a new one from the IdP.');\n}","typeGuard":"function hasClaims(claims: unknown): claims is Record<string, unknown> {\n  return !!claims && typeof claims === 'object' && Object.keys(claims as object).length > 0;\n}","tryCatchPattern":"try {\n  await api.post('/oauth/backchannel-logout', { logout_token });\n} catch (e) {\n  if (e.response?.status === 400 && /no claims/i.test(e.response?.data?.message)) {\n    requestNewLogoutToken();\n  } else throw e;\n}","preventionTips":["Decode the logout_token payload to confirm claims are present before forwarding.","Configure the IdP to emit a standards-compliant back-channel logout token.","Distinguish this from signature failures (error 75)."],"tags":["auth","oauth","logout","backchannel","token","claims"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}