Skip to content

Migrating from 9p4

iderex edited this page Jul 20, 2026 · 8 revisions

Note

This plugin is at the Beta stage of its maturity ladder (see the Roadmap). As with any auth change, do the migration described here first on a test instance with a config backup, then roll it out once you have confirmed sign-in works. The archived upstream receives no security updates anymore, so switching is the right move.

Migrating from 9p4/jellyfin-plugin-sso

This project continues the archived 9p4/jellyfin-plugin-sso from its last release (4.0.0.4). The short version: it is an in-place upgrade, not a move.

Why it is an upgrade, not a migration

Both versions share the same plugin identity:

  • Plugin GUID 505ce9d1-d916-42fa-86ca-673ef241d7df
  • Plugin name SSO Authentication
  • Configuration type Jellyfin.Plugin.SSO_Auth (same XML file)

Jellyfin therefore treats them as the same plugin. Swapping the binaries keeps the existing configuration file (<jellyfin data dir>/plugins/configurations/Jellyfin.Plugin.SSO_Auth.xml), and with it:

  • every configured OpenID and SAML provider (endpoints, client ids, certificates),
  • secrets (OidSecret, signing material),
  • all linked accounts (CanonicalLinks),
  • role/folder/Live-TV mappings.

New configuration fields added by this project are additive with safe defaults (false), so a config written by 4.0.0.4 loads unchanged. Nothing has to be re-entered.

How to switch

  1. Back up the config file named above (a plain file copy is enough).
  2. Stop Jellyfin.
  3. Install this plugin over the old one. The recommended path is the plugin catalog: add the stable repository URL under Dashboard → Plugins → Repositories and update SSO Authentication from the catalog — the GUID is unchanged, so it upgrades in place. See Installation. Building from source and dropping the binaries into <jellyfin data dir>/plugins/SSO-Auth*/ also works.
  4. Start Jellyfin and test a login for each configured provider.

What behaves differently after the switch

This project hardens the login path fail-closed. A provider that "worked" on 4.0.0.4 because nothing was checked can now be rejected — that is the hardening doing its job, and the fix is on the identity-provider side. If a login stops working, check these in order:

OpenID Connect

  • The id_token is now fully validated (signature against the discovery JWKS, issuer, audience, expiry). It must be signed with an asymmetric algorithm — RS256/384/512, PS256/384/512 or ES256/384/512. Tokens signed with HS256 or unsigned tokens are rejected; switch the IdP client's id_token signing algorithm to RS256. EdDSA is not supported.
  • A sub claim is required and must be stable. Account links are now keyed on sub instead of the username. Existing username-keyed links migrate to sub, but because the username is IdP-mutable that migration is gated behind AllowExistingAccountLink — so enable that flag for the provider before your users log in again (a short supervised window), or link accounts with the admin endpoint. With the flag off, an OpenID login whose name still matches its account is refused until you migrate it. Keep a non-SSO admin with a password login so you cannot lock yourself out. Full runbook, break-glass, and the takeover-window caveat: the Security Model page and Provider Setup. A user renamed at the IdP before the switch cannot be matched to their old link and gets a fresh account (the same outcome a rename already had on 4.0.0.4).
  • Account links are now bound to the issuer that created them. If you later repoint a provider's endpoint at a different identity provider, the plugin drops that provider's accumulated links (the same way it already drops the stored client secret on an endpoint change) rather than risk a colliding sub signing a new user into an old account. A straight 9p4 upgrade with the endpoint left unchanged is unaffected; existing links are simply stamped with the current issuer on next login.
  • Clocks matter: expiry is checked with a 5-minute skew. Keep Jellyfin and the IdP on NTP.
  • Details and per-provider notes: Provider Setup.

SAML

  • Responses signed with SHA-1 are rejected — reconfigure the IdP to sign with RSA/ECDSA-SHA-256 or stronger.
  • A response carrying a DOCTYPE/DTD is rejected outright (XXE defense); well-formed SAML never has one.
  • An assertion without a parseable NotOnOrAfter, or failing its AudienceRestriction, is rejected instead of silently accepted. Replayed assertions are rejected (one-time use).

Optional new hardening (off by default)

Nothing else changes unless you opt in. Two new per-provider SAML switches (ValidateRecipient, ValidateInResponseTo) bind responses to this server and to solicited logins — see Provider Setup before enabling them (ValidateInResponseTo deliberately disables IdP-initiated SSO).

Rolling back

Straightforward: stop Jellyfin and put the 9p4 4.0.0.4 binaries back. The GUID is the same and the new config fields are additive, so the old version simply ignores them — the configuration, including account links, stays intact. Two caveats:

  • OpenID account links that were already migrated to sub keying are not consulted by the old username-keyed code; affected users are re-linked by name exactly as they were before the switch.
  • If a provider's client secret or SAML signing key has been re-saved since upgrading, it is now stored encrypted (ssoenc:v1:) — 4.0.0.4 cannot read that format and behaves as if the secret were unset. Re-enter the secret/key on 4.0.0.4 after rolling back, or restore a pre-upgrade plaintext config backup. Details: Security Model § Secrets encrypted at rest.

If something breaks

Check the Jellyfin log first — the plugin logs why a login was rejected (weak algorithm, missing sub, expired assertion, audience mismatch) with remediation hints, then see Troubleshooting. If you hit something undocumented, please open an issue.

Clone this wiki locally