Compare commits

...

24 Commits

Author SHA1 Message Date
Ersei Saggi fc58b8e80f Update upload action 2025-10-20 22:43:33 -04:00
Ersei Saggi da21ebffa3 Specify that pushed authorization may need to be disabled for Authelia 2025-10-20 12:40:02 -04:00
Ersei Saggi 0d18ebae03 Update config UI links
https://github.com/9p4/jellyfin-plugin-sso/issues/285
2025-10-20 12:30:55 -04:00
Ersei Saggi 77e2a229f2 Don't use rc libs for Jellyfin 10.11 2025-10-20 12:30:15 -04:00
Ersei Saggi 71bb9a2f14 Jellyfin 10.11.0-rc5 2025-08-14 12:43:04 -04:00
Ersei Saggi 8baa922eea Manifest should be for 4.0.0.0 / JF10.11 2025-08-12 14:57:22 -04:00
Ersei Saggi 0738ad85f7 Fix warnings 2025-08-12 14:22:31 -04:00
Ersei Saggi 8d049705c4 Update workflows to use dotnet9 2025-08-12 13:51:13 -04:00
Ersei Saggi 3459baffdf More Jellyfin 10.11 fixes 2025-08-12 13:49:09 -04:00
9p4 de6cd5d5c2 Merge pull request #265 from babbitt/punycode
Use punycode URL for redirect page - OIDC
2025-08-11 14:13:48 -04:00
9p4 299d3436ec Merge pull request #269 from kernelb00t/patch-1
Match the styling of the redirection page with Jellyfin colors and font
2025-08-11 14:12:38 -04:00
9p4 5808f34064 Merge pull request #280 from hendrik1120/patch-1
remove deprecated redirect uri from readme
2025-08-11 14:12:22 -04:00
Ersei Saggi 5ed4c8bbcc Update to net9.0 for JF 10.11 2025-08-11 14:11:52 -04:00
Hendrik Sievers f78a0f3108 remove deprecated redirect uri 2025-07-18 11:04:29 +02:00
Leo THIVILLON aa0e361265 Match the styling of the redirection page with Jellyfin colors and font
- Added a style tag
- Set color to Jellyfin's dark background and light text
- Set font to Noto Sans
2025-05-09 23:26:51 +02:00
Ersei Saggi efc997c39e Add security policy
Closes #266
2025-05-06 13:46:32 -04:00
Joe Babbitt caab66b653 use punycode URL for redirect page
Redirect page content gets encoded as LATIN1, non LATIN characters in URLs get malformed.
2025-04-29 17:22:38 -04:00
9p4 79ac628c4c Merge pull request #251 from FeikoJoosten/main
Prevent force updating username
2025-04-07 02:30:43 -04:00
Ersei Saggi fe3ca6337d We support >=10.8 2025-04-07 02:16:48 -04:00
Feiko Joosten f761dea356 Removed trailing whitespace 2025-04-05 18:59:36 +02:00
Feiko Joosten 55f58ecedd Prevent force updating username
Fixed an issue where you cannot modify the username as the code tried to find users by name instead of its id.
2025-04-04 01:58:01 +02:00
Ersei Saggi 556feba864 Update credits in README to point to Duende library 2025-03-26 13:55:40 -04:00
Ersei Saggi 1f08ccd213 Prepare for 3.5.3.0 2025-03-26 13:51:26 -04:00
Ersei Saggi f1f57723c3 Make prettier happy again 2025-03-26 13:14:36 -04:00
17 changed files with 102 additions and 50 deletions
+3 -3
View File
@@ -3,12 +3,12 @@ on:
inputs:
dotnet-version:
required: false
default: "8.0.x"
default: "9.0.x"
description: "The .NET version to setup for the build"
type: string
dotnet-target:
required: false
default: "net8.0"
default: "net9.0"
description: "The .NET target to set for JPRM"
type: string
@@ -31,7 +31,7 @@ jobs:
dotnet-target: "${{ inputs.dotnet-target }}"
- name: Upload Artifact
uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # tag=v3
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # tag=v4.6.2
with:
name: build-artifact
retention-days: 30
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: 8.0.x
dotnet-version: 9.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
+2 -2
View File
@@ -15,7 +15,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: 8.0.x
dotnet-version: 9.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build Dotnet
@@ -35,7 +35,7 @@ jobs:
version: "0.0.0.9000"
verbosity: debug
path: .
dotnet-target: "net8.0"
dotnet-target: "net9.0"
output: _dist
- name: Prepare GitHub Release assets
run: |-
+2 -2
View File
@@ -10,8 +10,8 @@ jobs:
build:
uses: ./.github/workflows/build.yml
with:
dotnet-version: "8.0.*"
dotnet-target: "net8.0"
dotnet-version: "9.0.*"
dotnet-target: "net9.0"
upload:
runs-on: ubuntu-latest
needs:
+2 -4
View File
@@ -34,7 +34,7 @@ This is 100% alpha software! PRs are welcome to improve the code.
~~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).
**[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)**
**[This is for Jellyfin >=10.8](https://github.com/9p4/jellyfin-plugin-sso/issues/3) and only on the Web UI or clients supporting [Quick Connect](https://jellyfin.org/docs/general/server/quick-connect)**
**This README reflects the branch it is currently on! Switch tags to view version-specific documentation!**
@@ -91,8 +91,6 @@ The nightly build may have new features unavailable in other builds, but **be wa
## Examples
**Note that you should add both "/r/" and "/redirect/" paths to your SSO provider's configuration!**
### Creating A Login Button On The Main Page
In 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):
@@ -311,7 +309,7 @@ Much thanks to the [Jellyfin LDAP plugin](https://github.com/jellyfin/jellyfin-p
I 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).
I use the [IdentityModel OIDC Client](https://github.com/IdentityModel/IdentityModel.OidcClient/) library for the OpenID side of things.
I use the [Duende IdentityModel OIDC Client](https://github.com/DuendeSoftware/foss) library for the OpenID side of things.
Thanks to these projects, without which I would have been pulling my hair out implementing these protocols from scratch.
+3
View File
@@ -0,0 +1,3 @@
Please email all security vulnerabilities and issues found to the email "contact at ersei dot net". If using LLMs/AI to find the issues, first verify the issue exists manually. Please do not publicly disclose security vulnerabilities until after a stable release for the fix has been released for 30 days.
The latest released version is the only supported version.
+2 -1
View File
@@ -9,7 +9,8 @@
using System;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using Jellyfin.Data;
using Jellyfin.Database.Implementations.Enums;
using MediaBrowser.Controller.Net;
using Microsoft.AspNetCore.Http;
+26 -4
View File
@@ -8,8 +8,9 @@ using System.Security.Cryptography;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Duende.IdentityModel.OidcClient;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using Jellyfin.Data;
using Jellyfin.Database.Implementations.Entities;
using Jellyfin.Database.Implementations.Enums;
using Jellyfin.Plugin.SSO_Auth.Config;
using Jellyfin.Plugin.SSO_Auth.Helpers;
using MediaBrowser.Common.Api;
@@ -772,7 +773,28 @@ public class SSOController : ControllerBase
private async Task<Guid> CreateCanonicalLinkAndUserIfNotExist(string mode, string provider, string canonicalName)
{
User user = null;
user = _userManager.GetUserByName(canonicalName);
// First try to get the user by its id in case it was already registered before
Guid userId = Guid.Empty;
try
{
userId = GetCanonicalLink(mode, provider, canonicalName);
}
catch (KeyNotFoundException)
{
userId = Guid.Empty;
}
// No userId found? Let's try and find the user by name instead
if (userId == Guid.Empty)
{
user = _userManager.GetUserByName(canonicalName);
}
else
{
user = _userManager.GetUserById(userId);
}
if (user == null)
{
_logger.LogInformation($"SSO user {canonicalName} doesn't exist, creating...");
@@ -787,7 +809,7 @@ public class SSOController : ControllerBase
UpdateCanonicalLinkConfig(links, mode, provider);
}
Guid userId = Guid.Empty;
userId = Guid.Empty;
try
{
userId = GetCanonicalLink(mode, provider, canonicalName);
+18 -11
View File
@@ -600,7 +600,10 @@
type="checkbox"
class="sso-toggle"
/>
<span>Disable Pushed Authorization (Insecure)</span>
<span
>Disable Pushed Authorization (Insecure). May be needed
for Authelia.</span
>
</label>
<div class="fieldDescription checkboxFieldDescription"></div>
</div>
@@ -651,16 +654,20 @@
</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="RoleClaim">Port Override</label>
<input is="emby-input"
id="PortOverride"
type="text"
class="sso-text" />
<div class="fieldDescription">
If the plugin is redirecting to an incorrect port, set this to
the appropiate port
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="RoleClaim"
>Port Override</label
>
<input
is="emby-input"
id="PortOverride"
type="text"
class="sso-text"
/>
<div class="fieldDescription">
If the plugin is redirecting to an incorrect port, set this
to the appropiate port
</div>
</div>
<button
+3 -3
View File
@@ -61,14 +61,14 @@
is="emby-linkbutton"
href="https://github.com/9p4/jellyfin-plugin-sso"
class="button-link"
>help page</a
>homepage</a
>
and
<a
is="emby-linkbutton"
href="https://github.com/9p4/jellyfin-plugin-sso/projects/1"
href="https://github.com/9p4/jellyfin-plugin-sso/issues"
class="button-link"
>roadmap
>issue tracker
</a>
for more information.
</p>
+1 -1
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<RootNamespace>SSO_Auth</RootNamespace>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
+5 -5
View File
@@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<RootNamespace>Jellyfin.Plugin.SSO_Auth</RootNamespace>
<AssemblyVersion>3.5.2.4</AssemblyVersion>
<FileVersion>3.5.2.4</FileVersion>
<AssemblyVersion>4.0.0.0</AssemblyVersion>
<FileVersion>4.0.0.0</FileVersion>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
@@ -30,8 +30,8 @@
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Duende.IdentityModel.OidcClient" Version="6.0.1" />
<PackageReference Include="Jellyfin.Controller" Version="10.*-*" />
<PackageReference Include="Jellyfin.Model" Version="10.*-*" />
<PackageReference Include="Jellyfin.Controller" Version="10.11.0" />
<PackageReference Include="Jellyfin.Model" Version="10.11.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.Security.Cryptography.Xml" Version="6.0.1" />
</ItemGroup>
+1 -1
View File
@@ -61,7 +61,7 @@ public class Response
/// <param name="certificateBytes">The certificate formatted as an array of bytes.</param>
public Response(byte[] certificateBytes)
{
_certificate = new X509Certificate2(certificateBytes);
_certificate = X509CertificateLoader.LoadCertificate(certificateBytes);
}
/// <summary>
+24 -5
View File
@@ -1,3 +1,5 @@
using System.Globalization;
namespace Jellyfin.Plugin.SSO_Auth;
/// <summary>
@@ -9,7 +11,16 @@ public static class WebResponse
/// The shared HTML between all of the responses.
/// </summary>
public static readonly string Base = @"<!DOCTYPE html>
<html><head><meta name='viewport' content='width=device-width, initial-scale=1'></head><body>
<html><head>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<style>
body {
background: #101010;
color: #d1cfce;
font-family: Noto Sans, Noto Sans HK, Noto Sans JP, Noto Sans KR, Noto Sans SC, Noto Sans TC, sans-serif;
}
</style>
</head><body>
<p>Logging in...</p>
<noscript>Please enable Javascript to complete the login</noscript>
<script>
@@ -404,6 +415,14 @@ const sleep = (milliseconds) => {
/// <returns>A string with the HTML to serve to the client.</returns>
public static string Generator(string data, string provider, string baseUrl, string mode, bool isLinking = false)
{
// Strip out the protocol (http:// or https://) and convert the domain to Punycode
var idnMapping = new IdnMapping();
var protocolSeparatorIndex = baseUrl.IndexOf("//");
var protocol = baseUrl.Substring(0, protocolSeparatorIndex + 2);
var domain = baseUrl.Substring(protocolSeparatorIndex + 2);
var punycodeDomain = idnMapping.GetAscii(domain);
var punycodeBaseUrl = protocol + punycodeDomain;
return Base + @"
async function link(request) {
const jfCredentialsString = localStorage.getItem(""jellyfin_credentials"");
@@ -417,7 +436,7 @@ async function link(request) {
if (jfUser == null) return;
if (jfToken == null) return;
const url = '" + $"{baseUrl}/sso/{mode}/Link/{provider}/" + @"' + jfUser;
const url = '" + $"{punycodeBaseUrl}/sso/{mode}/Link/{provider}/" + @"' + jfUser;
return new Promise(resolve => {
var xhr = new XMLHttpRequest();
@@ -442,7 +461,7 @@ async function link(request) {
async function main() {
localStorage.removeItem('jellyfin_credentials');
document.getElementById('iframe-main').src = '" + baseUrl + @"/web/index.html';
document.getElementById('iframe-main').src = '" + punycodeBaseUrl + @"/web/index.html';
var data = '" + data + @"';
while (localStorage.getItem(""_deviceId2"") == null ||
@@ -460,7 +479,7 @@ async function main() {
if (" + $"{isLinking}".ToLower() + @") await link(request);
var url = '" + baseUrl + "/sso/" + mode + "/Auth/" + provider + @"';
var url = '" + punycodeBaseUrl + "/sso/" + mode + "/Auth/" + provider + @"';
let response = await new Promise(resolve => {
var xhr = new XMLHttpRequest();
@@ -484,7 +503,7 @@ async function main() {
jfCreds['Servers'][0]['UserId'] = responseJson['User']['Id'];
localStorage.setItem('jellyfin_credentials', JSON.stringify(jfCreds));
localStorage.setItem('enableAutoLogin', 'true');
window.location.replace('" + baseUrl + @"/web/index.html');
window.location.replace('" + punycodeBaseUrl + @"/web/index.html');
}
document.addEventListener('DOMContentLoaded', function () {
+5 -3
View File
@@ -1,9 +1,9 @@
name: "SSO Authentication"
guid: "505ce9d1-d916-42fa-86ca-673ef241d7df"
imageUrl: "https://raw.githubusercontent.com/9p4/jellyfin-plugin-sso/main/img/logo.png"
version: "3.5.2.4"
targetAbi: "10.9.0.0"
framework: "net8.0"
version: "4.0.0.0"
targetAbi: "10.11.0.0"
framework: "net9.0"
owner: "9p4"
overview: "Authenticate users against an SSO provider."
description: |
@@ -15,6 +15,8 @@ artifacts:
- "Duende.IdentityModel.OidcClient.dll"
- "Duende.IdentityModel.dll"
changelog: |
4.0.0.0: Jellyfin 10.11
3.5.3.0: Allow for OID-provided avatars, various bugfixes and workarounds
3.5.2.4: Updates for Jellyfin 10.9
3.5.2.3: Improve OpenID discovery policy security rules, fix iOS login bugs related to cache
3.5.2.2: Fix linking page when using new paths
Generated
+3 -3
View File
@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1730768919,
"narHash": "sha256-8AKquNnnSaJRXZxc5YmF/WfmxiHX6MMZZasRP6RRQkE=",
"lastModified": 1760934318,
"narHash": "sha256-/oUYsC0lUCBory65VK+UHqCCsCspbL1Vgfcf1KUYqVw=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "a04d33c0c3f1a59a2c1cb0c6e34cd24500e5a1dc",
"rev": "87848bf0cc4f87717fc813a4575f07330c3e743c",
"type": "github"
},
"original": {
+1 -1
View File
@@ -5,6 +5,6 @@
let pkgs = nixpkgs.legacyPackages.x86_64-linux;
in {
devShell.x86_64-linux =
pkgs.mkShell { buildInputs = [ pkgs.nodePackages.prettier pkgs.dotnet-sdk_8 ]; };
pkgs.mkShell { buildInputs = [ pkgs.nodePackages.prettier pkgs.dotnet-sdk_9 ]; };
};
}