github ZL154/JellyfinSecurity v2.5.21

5 hours ago

v2.5.21

This release fixes the "signed in successfully, then thrown straight back to the login page" bug at its root, makes the plugin work when Jellyfin is served under a Base URL, and turns notifications into something you can actually authenticate and test. Drop-in from any 2.5.x — no config or data migration.

Highlights

  • No more bounce back to the login page after a successful 2FA or SSO sign-in. (Fixes #137, #98.) Two independent causes, both fixed. The client-side "2FA pending" flag that suppresses API calls mid-challenge was only ever cleared by the OIDC bridge, so completing a password + 2FA login left it armed and Jellyfin Web's entire post-login bootstrap was blocked on the next page load. Separately, the stored credential recorded connection mode Remote while only ever populating a manual address, so Jellyfin resolved the server address to undefined. This is why it reproduced externally (through a reverse proxy) but never on the LAN. Verified end-to-end through Cloudflare + a reverse proxy, including surviving a page reload.

  • The Setup page and 2FA challenge work behind a Base URL. (Fixes #144.) Every API call from /TwoFactorAuth/Setup was hard-coded to the origin root, so any server mounted under a path (https://host/jellyfin/) got a 404 on /Users/Me and the page reported "We couldn't verify your Jellyfin session". Nothing was ever wrong with the session — the page was asking the wrong origin. Both pages are now Base-URL aware.

  • User details load in the admin Users table. (Fixes #156.) The inline ▸ details panel was reading the step-up-gated full export, so on any server with step-up enabled it always returned 403 and printed "Failed to load details". It now reads a dedicated summary endpoint carrying only what the panel shows — device labels and dates — with no audit log, IPs, device ids, or email. The per-user Export button (which genuinely should be gated) now prompts for the step-up code instead of failing silently.

SSO / OIDC

  • Sign-in no longer breaks when an identity provider's signing certificate expires. (Fixes #142, #98.) Authentik's self-signed signing certificate expires after a year and doesn't auto-rotate, and the plugin was rejecting otherwise-valid tokens because of it. The token signature is still fully verified against the provider's published keys — only the certificate's expiry date is no longer treated as fatal.
  • OIDC sign-in failures now say what's wrong. Instead of one opaque "Sign-in token could not be verified", a failed sign-in now names the cause — expired certificate, signing-key mismatch, Client ID mismatch, clock drift, or an unsupported signing algorithm — with configuration guidance and without leaking any library internals. The message now also reliably surfaces on the login page on Jellyfin 10.11.

Notifications

  • ntfy authentication. (#143.) Add an access token (Authorization: Bearer …) or a username + password (Basic auth), so a topic with a write ACL no longer has to be opened up to anonymous writes to receive alerts.
  • Custom webhook headers. For receivers that authenticate with Authorization: or X-Api-Key: rather than the HMAC secret. Header names/values are validated against header-injection, and the dispatcher's own headers can't be overridden.
  • "Send test notification" tests every configured channel — ntfy, Gotify and the webhook — and reports each one's result, instead of refusing to run without a webhook URL and reporting a flat "Sent".
  • The security score credits any notification channel, so an ntfy-only or Gotify-only setup is no longer docked points for not having a webhook.

Fixes & hardening

  • The injected XMLHttpRequest wrapper now leaves synchronous requests completely untouched, so a sync-xhr Permissions-Policy violation raised by another plugin's jQuery is no longer attributed to this plugin's inject.js. (#149.) The plugin itself issues no synchronous XHR. The README documents an obsolete-feature-free Permissions-Policy header.
  • Removed an unused admin download helper whose unhandled promise rejection surfaced as an "Uncaught (in promise)" console error. (#149.)
  • Fixed a dead branch in the XHR interceptor that made the 2FA-pending flag impossible to clear over XHR.

Upgrade

Drop-in from any 2.5.x — no config or data migration. Sigstore-signed + SLSA build-provenance attested. All 855 translation keys complete across 8 languages. 387/387 tests pass on .NET 9 / Jellyfin 10.11.x.

Don't miss a new JellyfinSecurity release

NewReleases is sending notifications on new releases.