The release workflow was copied from another project and could not succeed on any tag: it installed the .NET 8 SDK for a net9.0 project, published a JellyfinSso.csproj that does not exist, called a missing update_manifest.py, and zipped the entire publish tree instead of the three DLLs declared in build.yaml. Replace it with a tag-driven release: - Build with the .NET 9 SDK, injecting the tag version via -p:Version, -p:AssemblyVersion and -p:FileVersion so the assembly no longer carries a hardcoded 1.0.3.0. - Package via scripts/build_plugin.py, which emits the JPRM layout Jellyfin expects: the build.yaml artifacts plus a generated meta.json. The previous zips shipped without meta.json, which only worked for repository installs because Jellyfin synthesises one from the folder name. - Update manifest.json via scripts/update_manifest.py, keeping build.yaml as the single source of truth for plugin metadata. - Commit the zip and manifest to main and attach the zip to the Gitea release. Pushing uses the injected token, falling back to a RELEASE_TOKEN secret when the built-in one lacks write access. Also fix the published download URLs. manifest.json and the README pointed at pascallinxweiler/jellyfinplugin, which 404s; the repository is jellyfinsso, so the plugin repository could not be added to Jellyfin and no version could be downloaded. The workflow now derives the URL from GITHUB_REPOSITORY. Add a build workflow so main and pull requests are compiled and packaged. Co-Authored-By: Claude <noreply@anthropic.com>
5.2 KiB
Jellyfin OIDC Auth Plugin
Log in to Jellyfin with any OpenID Connect provider (Authelia, Authentik, Keycloak, Pocket ID, Google, ...).
How it works
- User opens
https://<jellyfin>/OidcAuth/login. - Plugin redirects to your provider (authorization code flow + PKCE via
IdentityModel.OidcClient). - Provider redirects back to
https://<jellyfin>/OidcAuth/callback; the plugin exchanges the code, validates the tokens, and maps claims to a Jellyfin user (creating it if configured). - A small callback page creates a Jellyfin session (
ISessionManager.AuthenticateDirect), writes the credentials into the web client's local storage, and redirects to/web/.
Build
Requires the .NET 9 SDK and pyyaml. The script compiles the plugin and packages
it the way Jellyfin expects — the DLLs listed in build.yaml plus a generated
meta.json — into dist/oidc-auth_<version>.zip:
pip install pyyaml
python scripts/build_plugin.py
Pass --version 1.0.4.0 to override the version in build.yaml.
Release
Releases are cut by pushing an annotated tag; the tag message becomes the changelog:
git tag -a v1.0.4.0 -m "What changed in this release."
git push origin v1.0.4.0
.gitea/workflows/release.yml then builds the plugin at that tag, commits the zip
to dist/ on main together with an updated manifest.json, and attaches the zip
to the Gitea release. Jellyfin picks the new version up from the repository URL below.
Pushing needs a token with write access. Gitea's built-in GITHUB_TOKEN is used by
default; if the repository does not grant it write access, add a PAT as the
RELEASE_TOKEN secret.
Install via plugin repository (recommended)
-
In Jellyfin: Dashboard → Plugins → Repositories → +
-
Repository URL:
https://git.linxweiler.xyz/pascallinxweiler/jellyfinsso/raw/branch/main/manifest.json -
Catalog → Authentication → OIDC Auth → Install → restart Jellyfin.
Updates show up in the catalog automatically when a new version is added to manifest.json.
Install manually
Extract dist/oidc-auth_<version>.zip into a new folder in Jellyfin's plugin
directory (e.g. config/plugins/OidcAuth/), then restart Jellyfin. The zip holds:
Jellyfin.Plugin.OidcAuth.dllIdentityModel.OidcClient.dllIdentityModel.dllmeta.json
Requires Jellyfin 10.11.x.
Configure
At your OIDC provider, create a confidential web client with redirect URI:
https://<jellyfin>/OidcAuth/callback
Then in Jellyfin: Dashboard → Plugins → OIDC Auth:
| Setting | Meaning |
|---|---|
| Issuer URL | Provider base URL; discovery doc must exist at <issuer>/.well-known/openid-configuration |
| Client ID / Secret | From your provider |
| Scopes | Default openid profile email; add groups if roles come via that scope |
| Username claim | Default preferred_username, falls back to sub |
| Role claim | Default groups |
| Allowed roles | CSV; empty = every authenticated user may log in |
| Admin roles | CSV; users with one of these roles are granted admin on SSO login. Missing role does not revoke admin unless "Also revoke admin" is enabled. Empty = plugin never touches the admin flag |
| Create users on first login | On by default; new users get access to all libraries |
| Set random password for auto-created users | On by default. Without it, Jellyfin accepts a blank password for these users from any client — convenient on a LAN-only server, dangerous on an exposed one |
| Disable strict endpoint validation | Enable for Google and other providers whose endpoints are on a different host than the issuer |
Login button on the web login page
Jellyfin does not let plugins modify the login page. Add a button via Dashboard → General → Branding → Login disclaimer:
<a is="emby-linkbutton" class="raised block emby-button" href="/OidcAuth/login">Sign in with SSO</a>
TV and native clients
The plugin does not change or disable Jellyfin's normal password login, so TV apps (Android TV, Roku, Swiftfin, Kodi, ...) keep working exactly as before. For users that exist only through OIDC, three options:
- Quick Connect (recommended): enable it in Dashboard → General. On the TV choose Quick Connect, then approve the code from the web client (where you're logged in via OIDC).
- Set a password: after the first OIDC login, the user sets a password in their web profile and uses that on the TV.
- Blank password (LAN-only setups): turn off "Set random password for auto-created users" — TV login then works with username + empty password. Do not do this on a server reachable from the internet.
Notes / limitations
- The OIDC browser flow itself works for the web client (and anything that can open a browser and reuse the resulting session token). Native clients that only show the username/password form can't use this flow directly — see the TV section above.
- Behind a reverse proxy, make sure Jellyfin sees the external scheme/host (
X-Forwarded-Proto,X-Forwarded-Host— Jellyfin's "Known proxies" setting), otherwise the computed redirect URI won't match the one registered at the provider. - Password login stays enabled; this plugin adds an additional login path.