Files
jellyfinsso/Jellyfin.Plugin.OidcAuth/Configuration/configPage.html
Pascal Linxweiler 2380e97e79 feat: initial Jellyfin OIDC auth plugin
OpenID Connect SSO for Jellyfin 10.10 (net8.0). Authorization code flow
with PKCE via IdentityModel.OidcClient, automatic user creation with
random password, role based admin/access mapping, plugin repo manifest.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 13:07:54 +02:00

121 lines
6.5 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<title>OIDC Auth</title>
</head>
<body>
<div id="OidcAuthConfigPage" data-role="page" class="page type-interior pluginConfigurationPage" data-require="emby-input,emby-button,emby-checkbox">
<div data-role="content">
<div class="content-primary">
<form id="OidcAuthConfigForm">
<div class="verticalSection verticalSection-extrabottompadding">
<div class="sectionTitleContainer flex align-items-center">
<h2 class="sectionTitle">OIDC Auth</h2>
</div>
<div class="inputContainer">
<input is="emby-input" id="OidIssuer" type="url" label="Issuer URL" />
<div class="fieldDescription">Base URL of your OIDC provider, e.g. https://auth.example.com. The discovery document is loaded from &lt;issuer&gt;/.well-known/openid-configuration.</div>
</div>
<div class="inputContainer">
<input is="emby-input" id="OidClientId" type="text" label="Client ID" />
</div>
<div class="inputContainer">
<input is="emby-input" id="OidClientSecret" type="password" label="Client Secret" autocomplete="new-password" />
</div>
<div class="inputContainer">
<input is="emby-input" id="OidScopes" type="text" label="Scopes" />
<div class="fieldDescription">Space separated. Default: openid profile email. Add e.g. "groups" if your provider needs it for the role claim.</div>
</div>
<div class="inputContainer">
<input is="emby-input" id="UsernameClaim" type="text" label="Username claim" />
<div class="fieldDescription">Claim used as Jellyfin username. Default: preferred_username. Falls back to "sub".</div>
</div>
<div class="inputContainer">
<input is="emby-input" id="RoleClaim" type="text" label="Role claim" />
<div class="fieldDescription">Claim containing groups/roles. Default: groups.</div>
</div>
<div class="inputContainer">
<input is="emby-input" id="AllowedRoles" type="text" label="Allowed roles" />
<div class="fieldDescription">Comma separated. Only users with one of these roles may log in. Empty = everyone.</div>
</div>
<div class="inputContainer">
<input is="emby-input" id="AdminRoles" type="text" label="Admin roles" />
<div class="fieldDescription">Comma separated. Users with one of these roles become Jellyfin administrators. Empty = never touch admin flag.</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label class="emby-checkbox-label">
<input is="emby-checkbox" id="CreateUsersIfMissing" type="checkbox" />
<span>Create users on first login</span>
</label>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label class="emby-checkbox-label">
<input is="emby-checkbox" id="SetRandomPasswordOnCreate" type="checkbox" />
<span>Set random password for auto-created users (recommended — without it, anyone can log in as them with a blank password)</span>
</label>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label class="emby-checkbox-label">
<input is="emby-checkbox" id="DisableEndpointValidation" type="checkbox" />
<span>Disable strict endpoint validation (needed for Google and other providers whose endpoints are on a different host than the issuer)</span>
</label>
</div>
<div>
<button is="emby-button" type="submit" class="raised button-submit block emby-button">
<span>Save</span>
</button>
</div>
<div class="fieldDescription" style="margin-top: 1em;">
Login URL: <code>&lt;your jellyfin address&gt;/OidcAuth/login</code>
register <code>&lt;your jellyfin address&gt;/OidcAuth/callback</code> as redirect URI at your provider.
</div>
</div>
</form>
</div>
</div>
<script type="text/javascript">
(function () {
var pluginId = '96badb44-9940-4ff0-befc-5f987159152c';
var textFields = ['OidIssuer', 'OidClientId', 'OidClientSecret', 'OidScopes', 'UsernameClaim', 'RoleClaim', 'AllowedRoles', 'AdminRoles'];
var boolFields = ['CreateUsersIfMissing', 'SetRandomPasswordOnCreate', 'DisableEndpointValidation'];
document.querySelector('#OidcAuthConfigPage').addEventListener('pageshow', function () {
Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(pluginId).then(function (config) {
textFields.forEach(function (f) { document.querySelector('#' + f).value = config[f] || ''; });
boolFields.forEach(function (f) { document.querySelector('#' + f).checked = !!config[f]; });
Dashboard.hideLoadingMsg();
});
});
document.querySelector('#OidcAuthConfigForm').addEventListener('submit', function (e) {
e.preventDefault();
Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(pluginId).then(function (config) {
textFields.forEach(function (f) { config[f] = document.querySelector('#' + f).value; });
boolFields.forEach(function (f) { config[f] = document.querySelector('#' + f).checked; });
ApiClient.updatePluginConfiguration(pluginId, config).then(function (result) {
Dashboard.processPluginConfigurationUpdateResult(result);
});
});
return false;
});
})();
</script>
</div>
</body>
</html>