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/Setupwas hard-coded to the origin root, so any server mounted under a path (https://host/jellyfin/) got a 404 on/Users/Meand 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:orX-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
XMLHttpRequestwrapper now leaves synchronous requests completely untouched, so async-xhrPermissions-Policyviolation raised by another plugin's jQuery is no longer attributed to this plugin'sinject.js. (#149.) The plugin itself issues no synchronous XHR. The README documents an obsolete-feature-freePermissions-Policyheader. - 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.