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>
118 lines
5.2 KiB
Markdown
118 lines
5.2 KiB
Markdown
# Jellyfin OIDC Auth Plugin
|
|
|
|
Log in to Jellyfin with any OpenID Connect provider (Authelia, Authentik, Keycloak, Pocket ID, Google, ...).
|
|
|
|
## How it works
|
|
|
|
1. User opens `https://<jellyfin>/OidcAuth/login`.
|
|
2. Plugin redirects to your provider (authorization code flow + PKCE via `IdentityModel.OidcClient`).
|
|
3. 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).
|
|
4. 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`:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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)
|
|
|
|
1. In Jellyfin: Dashboard → Plugins → Repositories → **+**
|
|
2. Repository URL:
|
|
|
|
```
|
|
https://git.linxweiler.xyz/pascallinxweiler/jellyfinsso/raw/branch/main/manifest.json
|
|
```
|
|
|
|
3. 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.dll`
|
|
- `IdentityModel.OidcClient.dll`
|
|
- `IdentityModel.dll`
|
|
- `meta.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:
|
|
|
|
```html
|
|
<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:
|
|
|
|
1. **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).
|
|
2. **Set a password**: after the first OIDC login, the user sets a password in their
|
|
web profile and uses that on the TV.
|
|
3. **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.
|