-
-
Notifications
You must be signed in to change notification settings - Fork 0
Troubleshooting
The login-state entry lives for about 15 minutes, long enough for a provider-side MFA or consent step. If the round-trip takes longer than that, the callback lands on a different plugin instance, or the callback comes in under a different provider than the one that started the login, retry the sign-in. States are per-instance, in-memory, and provider-bound.
The login is also bound to the browser that started it by a short-lived __Host-
cookie (see Login browser binding),
so the same message appears when the callback is opened in a different browser, after
clearing cookies mid-flow, or over plain http:// — the Secure binding cookie
is only stored over HTTPS, so on an HTTP-only deployment every login fails this way.
Serve SSO over HTTPS (a TLS-terminating reverse proxy is fine) and complete the login in
the browser that started it. Two logins running at once in the same browser share the one
cookie; the later one wins and the earlier tab must be retried.
The id_token returned by the provider is validated fail-closed — signature, issuer, audience, expiry (see Security Model). A refused login usually means one of:
- the provider signs the id_token with a symmetric algorithm (HS256) or leaves it unsigned — switch the client's id_token signing algorithm to RS256 (some Auth0 and Keycloak client templates default to HS256); EdDSA/Ed25519 is not supported;
- clock drift beyond the skew allowance between the Jellyfin host and the IdP — keep both on NTP;
- the token's issuer differs from the discovery issuer (some templated or
multi-tenant setups) — set
DoNotValidateIssuerNamefor that provider; this relaxes only the issuer check; - the provider publishes no usable signing key (
jwks_uri) in its discovery document.
The full provider-side requirements are listed in the id_token requirements.
The provider sent an iss parameter in the authorization response (RFC 9207) that does
not match the issuer of the id_token — the server log shows a warning naming the RFC
9207 mix-up check. A correctly configured provider always satisfies this; if yours
legitimately presents a different iss there (some multi-tenant setups), set
DoNotValidateResponseIssuer for that provider — it relaxes only this check.
The provider is marked Require PKCE (S256) (RequirePkce), but its discovery
document does not list S256 under code_challenge_methods_supported — or the document
could not be read at all. Enable PKCE with the S256 method at the identity provider, or
clear the option to fall back to the default behavior (an [SSO Audit] warning is
logged and the login proceeds). The support check is cached for about 15 minutes, so a
provider-side change can take that long to be picked up.
Because SAML validation is fail-closed, a rejected login usually means one of:
- the response signature does not cover the consumed assertion, or is missing;
- the assertion is expired or not yet valid (check clock sync between your IdP and server);
- the assertion's
AudienceRestrictiondoes not match this service provider (set the expected audience, or opt out only if your provider cannot emit it); - the same assertion was already used (replay);
- the browser-binding cookie is missing: a login started from Jellyfin must complete
in the same browser, and the
Securecookie is only stored over HTTPS — on an HTTP-only deployment (no TLS at the browser edge) every SP-initiated login is refused. Serve SSO over HTTPS; - the response's
InResponseTono longer matches an outstanding request: the login took longer than ~15 minutes, or the challenge and the callback landed on different instances behind a load balancer without sticky sessions (a lost correlation fails closed) — run a single instance or enable session affinity; -
ValidateInResponseTois enabled and the login was started from the identity provider's dashboard (IdP-initiated) — that option refuses unsolicited responses by design; start the login from Jellyfin, or disable the option.
Check the server log for the specific [SSO entry.
A first SSO login matched an existing, unlinked Jellyfin account with the same name. This
is refused by default to prevent account takeover. If adopting that account is intended,
enable AllowExistingAccountLink for the provider, or link it via /SSOViews/linking.
The identity provider authenticated the user, but the plugin's role gate refused the login:
the account is not in an allowed role or group for that provider. This is the most common
cause of this message. Check the provider's role/group allow-list (its Roles /
EnableAuthorization settings, and any generic permission-role mapping) against the roles the
identity provider actually sends for the user. The user-facing message is deliberately uniform
— it never reveals which roles are allowed — so the server log carries the specific reason.
Reached by direct browser navigation, this now renders on a themed page with a "Return to
login" link rather than as raw text.
This is the gated legacy-link migration. Links created by a pre-upgrade build (up to and
including 9p4 4.0.0.4) were keyed on username instead of sub, and following one now needs
the same AllowExistingAccountLink opt-in as adopting a same-named account. Two symptoms,
depending on whether your Jellyfin username still matches what the identity provider sends:
- Refused (HTTP 403) — your account still bears that name; the legacy link is not followed with the flag off.
- Signed in, but to a brand-new empty account — your account was renamed since the link was created, so the old link's name no longer matches; a fresh account is created instead of your original one.
The server log carries a WARNING marking either case. Fix it with a short, supervised
maintenance window: enable AllowExistingAccountLink for the provider, have affected users
log in once, then turn it back off — or skip the name trust entirely and link each account
explicitly via the admin linking endpoint (AddCanonicalLink). An administrator account
is never self-migrated this way, flag on or off — re-link it explicitly from a break-glass,
non-SSO admin. Keep a non-SSO administrator login before you upgrade so you are never locked
out in-band. Full runbook:
Security Model
and
OpenID Connect id_token requirements.
With SSO-only login on, every account except the designated break-glass administrator must sign in through the identity provider — so an IdP outage or misconfiguration blocks everyone else by design. Two recovery doors, neither of which needs the IdP:
- Sign in as the break-glass admin with its username and password, then fix the provider or turn
the mode off (
POST <base URL>/sso/SSO-Only/Disable, admin-gated). - If the break-glass login is unavailable too: on the server, edit
<jellyfin data dir>/plugins/configurations/Jellyfin.Plugin.SSO_Auth.xml, set<DisablePasswordLogin>tofalse, and restart Jellyfin — a boot-time reconciliation restores password routing for every account the mode had moved.
Full runbook: Security Model § SSO-only login.
Make sure you copied the full publish output (all DLLs), not just SSO-Auth.dll. See
Installation.
Still stuck? Open an issue with your Jellyfin version, plugin version, provider, and the
relevant [SSO log lines (redact secrets).
Repository · Issues · Releases · Security policy — report vulnerabilities privately, never in a public issue. Pages describe what is implemented today; if the wiki disagrees with the code, the code wins.
Getting started
- Installation
- Provider Setup
- Hardening & Options Reference
- Migrating from 9p4
- Troubleshooting
- Rollback
How it works
Security
- Security Model
- Security Conformance (ASVS / RFC 9700)
- Threat Model
- SSO-Only Login — design record
- Single Logout — design record
Standards & process (internal / maintainer)