{"record":{"id":"86e6c2ede882730f","repo":"immich-app/immich","slug":"invalid-logout-token-it-must-contain-either-a-sub","errorCode":null,"errorMessage":"Invalid logout token: it must contain either a sub or a sid claim","messagePattern":"Invalid logout token: it must contain either a sub or a sid claim","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"server/src/services/auth.service.ts","lineNumber":115,"sourceCode":"      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;\n    const user = await this.userRepository.getForChangePassword(auth.user.id);\n    const isValid = this.validateSecret(password, user.password);\n    if (!isValid) {\n      throw new BadRequestException('Wrong password');","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/immich-app/immich/blob/f48d4b332127ad365ba256108799ca8f571d2dd5/server/src/services/auth.service.ts#L97-L133","documentation":"backchannelLogout validates OIDC backchannel logout tokens before invalidating the local session. Per the OIDC Back-Channel Logout spec, a logout token must identify the session via a `sub` (user) or `sid` (session ID) claim. If the token's claims carry neither, the token cannot be tied to any session and the service rejects it as a bad request rather than invalidating sessions blindly.","triggerScenarios":"An identity provider posts a backchannel logout request whose verified token payload lacks both `sub` and `sid` claims (e.g. an IdP sending only `events` or `iat`/`jti`).","commonSituations":"IdP misconfiguration or a non-conformant/custom IdP that emits logout tokens without standard claims; intermediate proxies or token-mapping middleware stripping claims; IdP version upgrades changing the token shape.","solutions":["Fix the identity provider's logout token configuration so it includes the `sid` (and preferably `sub`) claim in backchannel logout tokens.","Check that no middleware or claim mapping on the Immich side strips or renames the `sub`/`sid` claims before backchannelLogout runs.","Verify the IdP actually supports OIDC Back-Channel Logout; if it only supports front-channel logout, disable backchannel logout instead of sending malformed tokens."],"exampleFix":"// IdP logout token claims (before)\n{ \"iss\": \"https://idp\", \"aud\": \"client\", \"iat\": 1700000000, \"jti\": \"abc\" }\n// after\n{ \"iss\": \"https://idp\", \"aud\": \"client\", \"iat\": 1700000000, \"jti\": \"abc\", \"sid\": \"session-123\", \"sub\": \"user-42\" }","handlingStrategy":"validation","validationCode":"if (!tokenClaims || (!tokenClaims.sub && !tokenClaims.sid)) {\n  throw new Error('Logout token missing sub/sid claim; check IdP backchannel logout config');\n}","typeGuard":"function hasLogoutSubject(c: Record<string, unknown> | undefined): c is { sub: string } | { sid: string } & Record<string, unknown> {\n  return !!c && (typeof c.sub === 'string' || typeof c.sid === 'string');\n}","tryCatchPattern":null,"preventionTips":["Confirm the IdP includes sid/sub in backchannel logout tokens during provider setup.","Write an integration test posting a spec-conformant logout token to the endpoint.","Log unknown token claim shapes when integrating a new IdP."],"tags":["oauth","oidc","logout","validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"f48d4b332127ad365ba256108799ca8f571d2dd5","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}