Add OIDC Back-Channel Logout support

Implement OIDC Back-Channel Logout 1.0 (RFC). When enabled, the IdP can
POST a signed logout_token JWT to invalidate user sessions server-side.

- Add BackchannelLogoutHandler: JWT verification via jose, jti replay
  protection with bounded cache, session destruction by sub or sid
- Add oidcSessionId column to sessions table with index for fast lookups
- Add backchannel logout route (POST /auth/openid/backchannel-logout)
- Notify connected clients via socket to redirect to login page
- Add authOpenIDBackchannelLogoutEnabled toggle in schema-driven settings UI
- Migration v2.34.0 adds oidcSessionId column and index
- Polish settings UI: auto-populate loading state, subfolder dropdown
  options, KeyValueEditor fixes, localized descriptions via descriptionKey,
  duplicate key detection, success/error toasts
- Localize backchannel logout toast (ToastSessionEndedByProvider)
- OidcAuthStrategy tests now use real class via require-cache stubbing
This commit is contained in:
Denis Arnst 2026-02-05 17:55:10 +01:00
parent 33bee70a12
commit 073eff74ef
No known key found for this signature in database
GPG key ID: D5866C58940197BF
16 changed files with 886 additions and 104 deletions

View file

@ -2,7 +2,7 @@ const groups = [
{ id: 'endpoints', label: 'Provider Endpoints', order: 1 },
{ id: 'credentials', label: 'Client Credentials', order: 2 },
{ id: 'behavior', label: 'Login Behavior', order: 3 },
{ id: 'claims', label: 'Claims & Group Mapping', order: 4 },
{ id: 'claims', label: 'Claims & Group Mapping', order: 4, descriptionKey: 'LabelOpenIDClaims' },
{ id: 'advanced', label: 'Advanced', order: 5 }
]
@ -173,7 +173,7 @@ const schema = [
group: 'claims',
order: 2,
validate: 'claimName',
description: 'Name of the claim containing group membership'
descriptionKey: 'LabelOpenIDGroupClaimDescription'
},
{
key: 'authOpenIDGroupMap',
@ -192,7 +192,7 @@ const schema = [
group: 'claims',
order: 4,
validate: 'claimName',
description: 'Claim containing per-user permissions JSON'
descriptionKey: 'LabelOpenIDAdvancedPermsClaimDescription'
},
// Advanced group
@ -216,6 +216,14 @@ const schema = [
{ value: '', label: 'None' }
],
description: 'Subfolder prefix for redirect URLs (e.g. /audiobookshelf)'
},
{
key: 'authOpenIDBackchannelLogoutEnabled',
type: 'boolean',
label: 'Back-Channel Logout',
group: 'advanced',
order: 3,
description: 'Enable OIDC Back-Channel Logout. Configure your IdP with the logout URL: {baseURL}/auth/openid/backchannel-logout'
}
]
@ -230,7 +238,7 @@ function getSchema() {
if (field.key === 'authOpenIDAdvancedPermsClaim') {
return {
...field,
description: `Claim containing per-user permissions JSON. Sample: ${User.getSampleAbsPermissions()}`
samplePermissions: User.getSampleAbsPermissions()
}
}
return field