{"id":13521816,"url":"https://github.com/9p4/jellyfin-plugin-sso","last_synced_at":"2025-03-31T21:33:33.535Z","repository":{"id":36991827,"uuid":"446603494","full_name":"9p4/jellyfin-plugin-sso","owner":"9p4","description":"This plugin allows users to sign in through an SSO provider (such as Google, Microsoft, or your own provider). This enables one-click signin.","archived":false,"fork":false,"pushed_at":"2024-06-04T14:27:12.000Z","size":1698,"stargazers_count":542,"open_issues_count":23,"forks_count":25,"subscribers_count":12,"default_branch":"main","last_synced_at":"2024-08-02T06:13:03.216Z","etag":null,"topics":["jellyfin","jellyfin-plugin","single-sign-on","sso"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/9p4.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":null,"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":"a055","issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2022-01-10T22:37:10.000Z","updated_at":"2024-08-01T21:03:05.000Z","dependencies_parsed_at":"2023-01-17T11:46:37.654Z","dependency_job_id":"69673fb6-9974-4ffe-9684-5dfe182b9477","html_url":"https://github.com/9p4/jellyfin-plugin-sso","commit_stats":null,"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/9p4%2Fjellyfin-plugin-sso","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/9p4%2Fjellyfin-plugin-sso/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/9p4%2Fjellyfin-plugin-sso/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/9p4%2Fjellyfin-plugin-sso/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/9p4","download_url":"https://codeload.github.com/9p4/jellyfin-plugin-sso/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":222683211,"owners_count":17022461,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["jellyfin","jellyfin-plugin","single-sign-on","sso"],"created_at":"2024-08-01T06:00:38.289Z","updated_at":"2025-03-31T21:33:33.525Z","avatar_url":"https://github.com/9p4.png","language":"C#","funding_links":["https://liberapay.com/a055"],"categories":["C# #","🧩 Plugins"],"sub_categories":["🔐 Authentication"],"readme":"\u003ch1 align=\"center\"\u003eJellyfin SSO Plugin\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n\n\u003cimg alt=\"Logo\" src=\"https://raw.githubusercontent.com/9p4/jellyfin-plugin-sso/main/img/logo.png\"/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003ca href=\"https://github.com/9p4/jellyfin-plugin-sso\"\u003e\n\u003cimg alt=\"GPL 3.0 License\" src=\"https://img.shields.io/github/license/9p4/jellyfin-plugin-sso.svg\"/\u003e\n\u003c/a\u003e\n\u003ca href=\"https://github.com/9p4/jellyfin-plugin-sso/actions/workflows/dotnet.yml\"\u003e\n\u003cimg alt=\"GitHub Actions Build Status\" src=\"https://github.com/9p4/jellyfin-plugin-sso/actions/workflows/dotnet.yml/badge.svg\"/\u003e\n\u003c/a\u003e\n\u003ca href=\"https://github.com/9p4/jellyfin-plugin-sso/releases\"\u003e\n\u003cimg alt=\"Current Release\" src=\"https://img.shields.io/github/release/9p4/jellyfin-plugin-sso.svg\"/\u003e\n\u003c/a\u003e\n\u003ca href=\"https://github.com/9p4/jellyfin-plugin-sso/releases.atom\"\u003e\n\u003cimg alt=\"Release RSS Feed\" src=\"https://img.shields.io/badge/rss-releases-ffa500?logo=rss\" /\u003e\n\u003c/a\u003e\n\u003ca href=\"https://github.com/9p4/jellyfin-plugin-sso/commits/main.atom\"\u003e\n\u003cimg alt=\"Main Commits RSS Feed\" src=\"https://img.shields.io/badge/rss-commits-ffa500?logo=rss\" /\u003e\n\u003c/a\u003e\n\u003c/p\u003e\n\nThis plugin allows users to sign in through an SSO provider (such as Google, Microsoft, or your own provider). This enables one-click signin.\n\nhttps://user-images.githubusercontent.com/17993169/149681516-f93b43f5-fa5c-4c1f-a909-e5414878a864.mp4\n\nExisting users may link new SSO accounts, or remove existing links using self-service at `/SSOViews/linking`.\n\n## Current State:\n\nThis is 100% alpha software! PRs are welcome to improve the code.\n\n~~There is NO admin configuration! You must use the API to configure the program!~~ Added by [strazto](https://github.com/strazto) in PR [#18](https://github.com/9p4/jellyfin-plugin-sso/pull/18) and [#27](https://github.com/9p4/jellyfin-plugin-sso/pull/27).\n\n**[This is for Jellyfin 10.8](https://github.com/9p4/jellyfin-plugin-sso/issues/3) and only on the Web UI and clients supporting [Quick Connect](https://jellyfin.org/docs/general/server/quick-connect)**\n\n**This README reflects the branch it is currently on! Switch tags to view version-specific documentation!**\n\n## Tested Providers\n\n[Find provider specific documentation in providers.md](providers.md)\n\n- Authelia\n- authentik\n- Keycloak\n  - OIDC \u0026 SAML\n- Google OpenID: Works, but usernames are all numeric\n\n## Supported Protocols\n\n- [OpenID](https://openid.net/what-is-openid/)\n- [SAML](https://www.cloudflare.com/learning/access-management/what-is-saml/)\n\n## Security\n\nThis is my first time writing C# so please take all of the code written here with a grain of salt. This program should be reasonably secure since it validates all information passed from the client with either a certificate or a secret internal state.\n\n## Installing\n\nAdd the package repo [https://raw.githubusercontent.com/9p4/jellyfin-plugin-sso/manifest-release/manifest.json](https://raw.githubusercontent.com/9p4/jellyfin-plugin-sso/manifest-release/manifest.json) to your Jellyfin plugin repositories.\n\nThen, install the plugin from the plugin catalog!\n\nSee [Contributing](#contributing) for instructions on how to build from source.\n\n### (Fallback) Legacy package repo (Versions \u003c= 3.3.0)\n\nWe have transitioned to a release system that automates distribution, packaging \u0026 hosting.\nThis system is new, and if something goes wrong, you can try using the old package repository as a fallback.\n\nInstead add the **old** package repository: [https://repo.ersei.net/jellyfin/manifest.json](https://repo.ersei.net/jellyfin/manifest.json) to your jellyfin plugin repositories.\n\n### Installing cutting edge/nightly builds\n\nIf you're impatient/brave/feel like helping us test things out, you can install the nightly build of the plugin, which is automatically built against the main branch.\n\nThe nightly build can be installed from the [main plugin repo](https://raw.githubusercontent.com/9p4/jellyfin-plugin-sso/manifest-release/manifest.json), and will always have a version number of `0.0.0.9000`.\n\nThe nightly build may have new features unavailable in other builds, but **be warned**, things may change frequently in nightly builds, and things may break, and you could lose data.\n\n## Roadmap\n\n- [x] Admin page\n- [ ] Automated tests\n- [x] Add role/claims support\n- [x] Use canonical usernames instead of preferred usernames\n- [x] Add user self-service\n- [ ] Finalize RBAC access for all user properties\n\n## Examples\n\n**Note that you should add both \"/r/\" and \"/redirect/\" paths to your SSO provider's configuration!**\n\n### Creating A Login Button On The Main Page\n\nIn the Jellyfin administration UI, under \"General\", there is a \"Branding\" section. In that section, add the following code in the \"Login disclaimer\" block (replacing `PROVIDER_NAME` and the domain):\n\n```html\n\u003cform action=\"https://jellyfin.example.com/sso/OID/start/PROVIDER_NAME\"\u003e\n  \u003cbutton class=\"raised block emby-button button-submit\"\u003e\n    Sign in with SSO\n  \u003c/button\u003e\n\u003c/form\u003e\n```\n\nThen, add the following code in the \"Custom CSS code\" section:\n\n```css\na.raised.emby-button {\n  padding: 0.9em 1em;\n  color: inherit !important;\n}\n\n.disclaimerContainer {\n  display: block;\n}\n```\n\n![screenshot of the configuration page with the same code](img/custom-button.png)\n\nFor more information, refer to [issue #16](https://github.com/9p4/jellyfin-plugin-sso/issues/16).\n\n### SAML\n\nExample for adding a SAML configuration with the API using [curl](https://curl.se/):\n\n`curl -v -X POST -H \"Content-Type: application/json\" -d '{\"samlEndpoint\": \"https://keycloak.example.com/realms/test/protocol/saml\", \"samlClientId\": \"jellyfin-saml\", \"samlCertificate\": \"Very long base64 encoded string here\", \"enabled\": true, \"enableAuthorization\": true, \"enableAllFolders\": false, \"enabledFolders\": [], \"adminRoles\": [\"jellyfin-admin\"], \"roles\": [\"allowed-to-use-jellyfin\"], \"enableFolderRoles\": true, \"folderRoleMapping\": [{\"role\": \"allowed-to-watch-movies\", \"folders\": [\"cc7df17e2f3509a4b5fc1d1ff0a6c4d0\", \"f137a2dd21bbc1b99aa5c0f6bf02a805\"]}]}' \"https://myjellyfin.example.com/sso/SAML/Add/PROVIDER_NAME?api_key=API_KEY_HERE\"`\n\nMake sure that the JSON is the same as the configuration you would like.\n\nThe SAML provider must have the following configuration (I am using Keycloak, and I cannot speak for whatever you will see):\n\n- Sign Documents on\n- Sign Assertions off\n- Client Signature Required off\n- Redirect URI: [https://myjellyfin.example.com/sso/SAML/post/PROVIDER_NAME](https://myjellyfin.example.com/sso/SAML/start/PROVIDER_NAME)\n- Base URL: [https://myjellyfin.example.com](https://myjellyfin.example.com)\n- Master SAML processing URL: [https://myjellyfin.example.com/sso/SAML/start/PROVIDER_NAME](https://myjellyfin.example.com/sso/SAML/start/PROVIDER_NAME)\n\nMake sure that `clientid` is replaced with the actual client ID and `PROVIDER_NAME` is replaced with the chosen provider name!\n\n### OpenID\n\nExample for adding an OpenID configuration with the API using [curl](https://curl.se/)\n\n`curl -v -X POST -H \"Content-Type: application/json\" -d '{\"oidEndpoint\": \"https://keycloak.example.com/realms/test\", \"oidClientId\": \"jellyfin-oid\", \"oidSecret\": \"short secret here\", \"enabled\": true, \"enableAuthorization\": true, \"enableAllFolders\": false, \"enabledFolders\": [], \"adminRoles\": [\"jellyfin-admin\"], \"roles\": [\"allowed-to-use-jellyfin\"], \"enableFolderRoles\": true, \"folderRoleMapping\": [{\"role\": \"allowed-to-watch-movies\", \"folders\": [\"cc7df17e2f3509a4b5fc1d1ff0a6c4d0\", \"f137a2dd21bbc1b99aa5c0f6bf02a805\"]}], \"roleClaim\": \"realm_access\", \"oidScopes\" : [\"\"]}' \"https://myjellyfin.example.com/sso/OID/Add/PROVIDER_NAME?api_key=API_KEY_HERE\"`\n\nThe OpenID provider must have the following configuration (again, I am using Keycloak)\n\n- Access Type: Confidential\n- Standard Flow Enabled\n- Redirect URI: [https://myjellyfin.example.com/sso/OID/redirect/PROVIDER_NAME](https://myjellyfin.example.com/sso/OID/redirect/PROVIDER_NAME)\n- Base URL: [https://myjellyfin.example.com](https://myjellyfin.example.com)\n\nMake sure that `clientid` is replaced with the actual client ID and `PROVIDER_NAME` is replaced with the chosen provider name!\n\n## API Endpoints\n\nThe API is all done from a base URL of `/sso/`\n\n### SAML\n\n#### Flow\n\n- POST `SAML/start/PROVIDER_NAME`: This is the SAML POST endpoint. It accepts a form response from the SAML provider and returns HTML and JavaScript for the client to login with a given provider name.\n- GET `SAML/start/PROVIDER_NAME`: This is the SAML initiator: it will begin the authorization flow for SAML with a given provider name.\n- POST `SAML/Auth/PROVIDER_NAME`: This is the SAML client-side API: the HTML and JavaScript client will call this endpoint to receive Jellyfin credentials given a provider name. Post format is in JSON with the following keys:\n  - `deviceId`: string. Device ID.\n  - `deviceName`: string. Device name.\n  - `appName`: string. App name.\n  - `appVersion`: string. App version.\n  - `data`: string. The signed SAML XML request. Used to verify a request.\n\n#### Configuration\n\nThese all require authorization. Append an API key to the end of the request: `curl \"http://myjellyfin.example.com/sso/SAML/Get?api_key=API_KEY_HERE\"`\n\n- POST `SAML/Add/PROVIDER_NAME`: This adds or overwrites a configuration for SAML for the given provider name. It accepts JSON with the following keys and format:\n  - `samlEndpoint`: string. The SAML endpoint.\n  - `samlClientId`: string. The SAML client ID.\n  - `samlCertificate`: string. The base64 encoded SAML certificate.\n  - `enabled`: boolean. Determines if the provider is enabled or not.\n  - `enableAuthorization`: boolean: Determines if the plugin sets permissions for the user. If false, the user will start with no permissions and an administrator will add permissions. If disabled, then the permissions of users will not be modified and the Jellyfin defaults will be used instead.\n  - `enableAllFolders`: boolean. Determines if the client logging in is allowed access to all folders.\n  - `enabledFolders`: array of strings. If `enableAllFolders` is set to false, then this will be used to determine what folders the users who log in through this provider are allowed to use.\n  - `roles`: array of strings. This validates the SAML response against the `Role` attribute. If a user has any of these roles, then the user is authenticated. Leave blank to disable role checking.\n  - `adminRoles`: array of strings. This uses SAML response's `Role` attributes. If a user has any of these roles, then the user is an admin. Leave blank to disable (default is to not enable admin permissions).\n  - `enableFolderRoles`: boolean. Determines if role-based folder access should be used.\n  - `folderRoleMapping`: object in the format \"role\": string and \"folders\": array of strings. The user with this role will have access to the following folders if `enableFolderRoles` is enabled. To get the IDs of the folders, GET the `/Library/MediaFolders` URL with an API key. Look for the `Id` attribute.\n  - `enableLiveTvRoles`: boolean. Determines if role-based Live TV access should be used.\n  - `liveTvRoles`: array of strings. If `enableLiveTvRoles` is enabled, then the user's roles will be checked against these. If the user is granted permission, then the user will be able to view Live TV.\n  - `liveTvManagementRoles`: array of strings. If `enableLiveTvRoles` is enabled, then the user's roles will be checked against these. If the user is granted permission, then the user will be able to manage Live TV.\n  - `enableLiveTv`: boolean. Whether to allow Live TV by default. This applies even if `enableLiveTvRoles` is enabled.\n  - `enableLiveTvManagement`: boolean. Whether to allow Live TV management by default. This applies even if `enableLiveTvRoles` is enabled.\n  - `defaultProvider`: string. The set provider then gets assigned to the user after they have logged in. If it is not set, nothing is changed. With this, a user can login with SSO but is still able to log in via other providers later. See the `Unregister` endpoint.\n  - `schemeOverride`: string. Sets the scheme for URLs used. Can be useful if the plugin refuses to use HTTPS URLs.\n- GET `SAML/Del/PROVIDER_NAME`: This removes a configuration for SAML for a given provider name.\n- GET `SAML/Get`: Lists the configurations currently available.\n\n### OpenID\n\n#### Flow\n\n- GET `OID/redirect/PROVIDER_NAME`: This is the OpenID callback path. This will return HTML and JavaScript for the client to login with a given provider name.\n- GET `OID/start/PROVIDER_NAME`: This is the OpenID initiator: it will begin the authorization flow for OpenID with a given provider name.\n- POST `OID/Auth/PROVIDER_NAME`: This is the OpenID client-side API: the HTML and JavaScript client will call this endpoint to receive Jellyfin credentials for a given provider name. Post format is in JSON with the following keys:\n  - `deviceId`: string. Device ID.\n  - `deviceName`: string. Device name.\n  - `appName`: string. App name.\n  - `appVersion`: string. App version.\n  - `data`: string. The OpenID state. Used to verify a request.\n\n#### Configuration\n\nThese all require authorization. Append an API key to the end of the request: `curl \"http://myjellyfin.example.com/sso/OID/Get?api_key=9c6e5fae4ae145669e6b7a3942f813b7\"`\n\n- POST `OID/Add/PROVIDERNAME`: This adds or overwrites a configuration for OpenID with a given provider name. It accepts JSON with the following keys and format:\n  - `oidEndpoint`: string. The OpenID endpoint. Must have a `.well-known` path available.\n  - `oidClientId`: string. The OpenID client ID.\n  - `oidSecret`: string. The OpenID secret.\n  - `enabled`: boolean. Determines if the provider is enabled or not.\n  - `enableAuthorization`: boolean: Determines if the plugin sets permissions for the user. If false, the user will start with no permissions and an administrator will add permissions. If disabled, then the permissions of users will not be modified and the Jellyfin defaults will be used instead.\n  - `enableAllFolders`: boolean. Determines if the client logging in is allowed access to all folders.\n  - `enabledFolders`: array of strings. If `enableAllFolders` is set to false, then this will be used to determine what folders the users who log in through this provider are allowed to use.\n  - `roles`: array of strings. This validates the OpenID response against the claim set in `roleClaim`. If a user has any of these roles, then the user is authenticated. Leave blank to disable role checking. This currently only works for Keycloak (to my knowledge).\n  - `adminRoles`: array of strings. This uses the OpenID response against the claim set in `roleClaim`. If a user has any of these roles, then the user is an admin. Leave blank to disable (default is to not enable admin permissions).\n  - `enableFolderRoles`: boolean. Determines if role-based folder access should be used.\n  - `folderRoleMapping`: object in the format \"role\": string and \"folders\": array of strings. The user with this role will have access to the following folders if `enableFolderRoles` is enabled. To get the IDs of the folders, GET the `/Library/MediaFolders` URL with an API key. Look for the `Id` attribute.\n  - `enableLiveTvRoles`: boolean. Determines if role-based Live TV access should be used.\n  - `liveTvRoles`: array of strings. If `enableLiveTvRoles` is enabled, then the user's roles will be checked against these. If the user is granted permission, then the user will be able to view Live TV.\n  - `liveTvManagementRoles`: array of strings. If `enableLiveTvRoles` is enabled, then the user's roles will be checked against these. If the user is granted permission, then the user will be able to manage Live TV.\n  - `enableLiveTv`: boolean. Whether to allow Live TV by default. This applies even if `enableLiveTvRoles` is enabled.\n  - `enableLiveTvManagement`: boolean. Whether to allow Live TV management by default. This applies even if `enableLiveTvRoles` is enabled.\n  - `roleClaim`: string. This is the value in the OpenID response to check for roles. For Keycloak, it is `realm_access.roles` by default. The first element is the claim type, the subsequent values are to parse the JSON of the claim value. Use a \"\\\\.\" to denote a literal \".\". This expects a list of strings from the OIDC server.\n  - `oidScopes` : array of strings. Each contains an additional scope name to include in the OIDC request.\n    - For some OIDC providers (For example, [authelia](https://github.com/9p4/jellyfin-plugin-sso/issues/23#issuecomment-1112237616)), additional scopes may be required in order to validate group membership in role claim.\n    - Leave empty to only request the default scopes.\n  - `defaultProvider`: string. The set provider then gets assigned to the user after they have logged in. If it is not set, nothing is changed. With this, a user can login with SSO but is still able to log in via other providers later. See the `Unregister` endpoint.\n  - `defaultUsernameClaim`: string. The provider will use the claim to create the users' usernames. If not set, it fallbacks to `preferred_username`.\n  - `avatarUrlFormat`: string. The URL format for the users avatars. OIDC claims can be used by using the `@{claim_type}` syntax. If not set, the avatars won't change.\n  - `disableHttps`: boolean. Determines whether the OpenID discovery endpoint requires HTTPS.\n  - `doNotValidateEndpoints`: boolean. Determines whether the OpenID discovery process will validate endpoints. This may be required for Google.\n  - `doNotValidateIssuerName`: boolean. Determines whether the OpenID discovery process will validate the OpenID issuer name.\n  - `schemeOverride`: string. Sets the scheme for URLs used. Can be useful if the plugin refuses to use HTTPS URLs.\n- GET `OID/Del/PROVIDER_NAME`: This removes a configuration for OpenID for a given provider name.\n- GET `OID/Get`: Lists the configurations currently available.\n- GET `OID/States`: Lists currently active OpenID flows in progress.\n\n### Misc\n\n- POST `Unregister/username`: This \"unregisters\" a user from SSO. A JSON-formatted string must be posted with the new authentication provider. To reset to the default provider, use `Jellyfin.Server.Implementations.Users.DefaultAuthenticationProvider` like so: `curl -X POST -H \"Content-Type: application/json\" -d '\"Jellyfin.Server.Implementations.Users.DefaultAuthenticationProvider\"' \"https://myjellyfin.example.com/sso/Unregister/username?api_key=API_KEY`\n\n## Limitations\n\nLogging in with an SSO account that has the same username as an existing Jellyfin account will override the permissions for the user. Use caution when overriding the administrator account!\n\n~~There is no GUI to sign in. You have to make it yourself! The buttons should redirect to something like this: [https://myjellyfin.example.com/sso/SAML/start/clientid](https://myjellyfin.example.com/sso/SAML/start/clientid) replacing `clientid` with the provider client ID and `SAML` with the auth scheme (either `SAML` or `OID`).~~\n\n~~Furthermore, there is no functional admin page (yet). PRs for this are welcome. In the meantime, you have to interact with the API to add or remove configurations.~~ Added by [strazto](https://github.com/strazto) in PR [#18](https://github.com/9p4/jellyfin-plugin-sso/pull/18) and [#27](https://github.com/9p4/jellyfin-plugin-sso/pull/27).\n\nThere is also no logout callback. Logging out of Jellyfin will log you out of Jellyfin only, instead of the SSO provider as well.\n\n~~This only supports Jellyfin on its own domain (for now). This is because I'm using string concatenation for generating some URLs. A PR is welcome to patch this.~~ Fixed in [PR #1](https://github.com/9p4/jellyfin-plugin-sso/pull/1).\n\n**This only works on the web UI**. ~~The user must open the Jellyfin web UI BEFORE using the SSO program to populate some values in the localStorage.~~ Fixed by implementing a comment by [Pfuenzle](https://github.com/Pfuenzle) in [Issue #5](https://github.com/9p4/jellyfin-plugin-sso/issues/5#issuecomment-1041864820).\n\n# Contributing\n\n## Dependencies\n\nThis project uses Nix flakes to manage development dependencies. Run `nix develop` to use the same toolchain versions.\n\n## Building\n\nThis is built with .NET 6.0. Build with `dotnet publish .` for the debug release in the `SSO-Auth` directory. Copy over the `IdentityModel.OidcClient.dll`, the `IdentityModel.dll` and the `SSO-Auth.dll` files in the `/bin/Debug/net6.0/publish` directory to a new folder in your Jellyfin configuration: `config/plugins/sso`.\n\n### VSCode Workflow\n\nAn example `.vscode` configuration may be found at [strazto/jellyfin-plugin-sso-vscode](https://github.com/strazto/jellyfin-plugin-sso-vscode).\n\nFrom the root of this repo, you may clone that to `.vscode`\n\n```bash\n# From repo root\n\ngit clone https://github.com/strazto/jellyfin-plugin-sso-vscode .vscode\n```\n\n## Releasing\n\nThis plugin uses [JPRM](https://github.com/oddstr13/jellyfin-plugin-repository-manager) to build the plugin. Refer to the documentation there to install JPRM.\n\nBuild the zipped plugin with `jprm --verbosity=debug plugin build .`.\n\n### CI Releases\n\nAnything merged to the main branch will be built and published by our CI system.\n\nAnything tagged/released as a formal Github release will also be built and published by our CI system.\n\nIf you wish to use releases from your own fork, refer to\n[Installing](#installing), however, you will need to change the url to the\nmanifest file, `https://raw.githubusercontent.com/9p4/jellyfin-plugin-sso/manifest-release/manifest.json`\nso that it refers to your fork.\n\n## Credits and Thanks\n\nMuch thanks to the [Jellyfin LDAP plugin](https://github.com/jellyfin/jellyfin-plugin-ldapauth) for offering a base for me to start on my plugin.\n\nI use the [AspNet SAML](https://github.com/jitbit/AspNetSaml/) library for the SAML side of things (patched to work with Base64 on non-Windows machines).\n\nI use the [Duende IdentityModel OIDC Client](https://github.com/DuendeSoftware/foss) library for the OpenID side of things.\n\nThanks to these projects, without which I would have been pulling my hair out implementing these protocols from scratch.\n\n## Something funny about the origins of this plugin\n\nIt totally slipped my mind, but I had [requested this functionality a few years back](https://github.com/jellyfin/jellyfin/issues/2012). What goes around comes around, I guess.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F9p4%2Fjellyfin-plugin-sso","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F9p4%2Fjellyfin-plugin-sso","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F9p4%2Fjellyfin-plugin-sso/lists"}