diff --git a/modules/services/open-webui.nix b/modules/services/open-webui.nix index 1b2bbb5..595b1a4 100644 --- a/modules/services/open-webui.nix +++ b/modules/services/open-webui.nix @@ -4,6 +4,9 @@ let contracts = pkgs.callPackage ../contracts {}; shblib = pkgs.callPackage ../../lib {}; + + roleClaim = "openwebui_groups"; + oauthScopes = [ "openid" "email" "profile" "groups" "${roleClaim}" ]; in { options.shb.open-webui = { @@ -191,21 +194,11 @@ in ${cfg.ldap.adminGroup} = {}; }; - shb.authelia.oidcClients = [ - { - client_id = cfg.sso.clientID; - client_secret.source = cfg.sso.sharedSecretForAuthelia.result.path; - scopes = [ "openid" "email" "profile" ]; - authorization_policy = cfg.sso.authorization_policy; - redirect_uris = [ - "https://${cfg.subdomain}.${cfg.domain}/oauth/oidc/callback" - ]; - } - ]; services.open-webui = { package = pkgs.open-webui.overrideAttrs (finalAttrs: { patches = [ ../../patches/0001-selfhostblocks-never-onboard.patch + ../../patches/0002-selfhostblocks-do-not-allow-unauthorized-roles.patch ]; }); environment = { @@ -217,13 +210,43 @@ in OAUTH_CLIENT_ID = cfg.sso.clientID; OPENID_PROVIDER_URL = "${cfg.sso.authEndpoint}/.well-known/openid-configuration"; OAUTH_PROVIDER_NAME = "Single Sign-On"; - OAUTH_SCOPES = "openid email profile"; - OAUTH_ALLOWED_ROLES = cfg.ldap.userGroup; - OAUTH_ADMIN_ROLES = cfg.ldap.adminGroup; + OAUTH_USERNAME_CLAIM = "preferred_username"; ENABLE_OAUTH_ROLE_MANAGEMENT = "True"; + OAUTH_ALLOWED_ROLES = "user,admin"; + OAUTH_ADMIN_ROLES = "admin"; + OAUTH_ROLES_CLAIM = roleClaim; + OAUTH_SCOPES = lib.concatStringsSep " " oauthScopes; }; }; + shb.authelia.extraDefinitions = { + user_attributes.${roleClaim}.expression = + ''"${cfg.ldap.adminGroup}" in groups ? ["admin"] : ("${cfg.ldap.userGroup}" in groups ? ["user"] : [""])''; + }; + shb.authelia.extraOidcClaimsPolicies.${roleClaim} = { + custom_claims = { + "${roleClaim}" = {}; + }; + }; + shb.authelia.extraOidcScopes."${roleClaim}" = { + claims = [ "${roleClaim}" ]; + }; + + shb.authelia.oidcClients = [ + { + client_id = cfg.sso.clientID; + client_name = "Open WebUI"; + client_secret.source = cfg.sso.sharedSecretForAuthelia.result.path; + claims_policy = "${roleClaim}"; + public = false; + authorization_policy = cfg.sso.authorization_policy; + redirect_uris = [ + "https://${cfg.subdomain}.${cfg.domain}/oauth/oidc/callback" + ]; + scopes = oauthScopes; + } + ]; + systemd.services.open-webui.serviceConfig.EnvironmentFile = "/run/open-webui/secrets.env"; systemd.tmpfiles.rules = [ "d '/run/open-webui' 0750 root root - -" diff --git a/modules/services/open-webui/docs/default.md b/modules/services/open-webui/docs/default.md index 0b82ef4..1a5c251 100644 --- a/modules/services/open-webui/docs/default.md +++ b/modules/services/open-webui/docs/default.md @@ -11,12 +11,13 @@ This service sets up [Open WebUI][] which provides a frontend to various LLMs. ## Features {#services-open-webui-features} - Telemetry disabled. -- Skip onboarding. +- Skip onboarding through custom patch. - Declarative [LDAP](#services-open-webui-options-shb.open-webui.ldap) Configuration. Needed LDAP groups are created automatically. - Declarative [SSO](#services-open-webui-options-shb.open-webui.sso) Configuration. When SSO is enabled, login with user and password is disabled. Registration is enabled through SSO. + Correct error message for unauthorized user through custom patch. - Access through [subdomain](#services-open-webui-options-shb.open-webui.subdomain) using reverse proxy. - Access through [HTTPS](#services-open-webui-options-shb.open-webui.ssl) using reverse proxy. - [Backup](#services-open-webui-options-shb.open-webui.sso) through the [backup block](./blocks-backup.html). diff --git a/patches/0002-selfhostblocks-do-not-allow-unauthorized-roles.patch b/patches/0002-selfhostblocks-do-not-allow-unauthorized-roles.patch new file mode 100644 index 0000000..84c473d --- /dev/null +++ b/patches/0002-selfhostblocks-do-not-allow-unauthorized-roles.patch @@ -0,0 +1,48 @@ +From fed4cfab1f66e6a2a46dbdd20ad52aee664f06b1 Mon Sep 17 00:00:00 2001 +From: ibizaman +Date: Thu, 9 Oct 2025 01:37:43 +0200 +Subject: [PATCH] selfhostblocks: do not allow unauthorized roles + +--- + backend/open_webui/constants.py | 2 +- + backend/open_webui/utils/oauth.py | 5 +++-- + 2 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/backend/open_webui/constants.py b/backend/open_webui/constants.py +index 59ee6aaac..5a42f1805 100644 +--- a/backend/open_webui/constants.py ++++ b/backend/open_webui/constants.py +@@ -51,7 +51,7 @@ class ERROR_MESSAGES(str, Enum): + + EXISTING_USERS = "You can't turn off authentication because there are existing users. If you want to disable WEBUI_AUTH, make sure your web interface doesn't have any existing users and is a fresh installation." + +- UNAUTHORIZED = "401 Unauthorized" ++ UNAUTHORIZED = "Unauthorized" + ACCESS_PROHIBITED = "You do not have permission to access this resource. Please contact your administrator for assistance." + ACTION_PROHIBITED = ( + "The requested action has been restricted as a security measure." +diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py +index 9090c38ce..3c68dead4 100644 +--- a/backend/open_webui/utils/oauth.py ++++ b/backend/open_webui/utils/oauth.py +@@ -336,8 +336,7 @@ class OAuthManager: + oauth_allowed_roles = auth_manager_config.OAUTH_ALLOWED_ROLES + oauth_admin_roles = auth_manager_config.OAUTH_ADMIN_ROLES + oauth_roles = [] +- # Default/fallback role if no matching roles are found +- role = auth_manager_config.DEFAULT_USER_ROLE ++ role = None + + # Next block extracts the roles from the user data, accepting nested claims of any depth + if oauth_claim and oauth_allowed_roles and oauth_admin_roles: +@@ -373,6 +372,8 @@ class OAuthManager: + log.debug("Assigned user the admin role") + role = "admin" + break ++ if role is None: ++ raise HTTPException(403, detail=ERROR_MESSAGES.UNAUTHORIZED) + else: + if not user: + # If role management is disabled, use the default role for new users +-- +2.50.1