Files
kevinveenbirkenbachandClaude Opus 5 54aced1868 fix(openwebui): stop the first MCP grant from locking a user out
ENABLE_OAUTH_ROLE_MANAGEMENT does two things and only one is obvious. Besides
mapping the administrator group onto the Open WebUI admin role, it turns
OAUTH_ALLOWED_ROLES into a sign-in gate: a user whose groups claim is non-empty
and matches neither the allowed list nor the admin list is refused outright.

Upstream defaults that list to the literals `user,admin`, and Keycloak emits
claims shaped /roles/<app>/<role>, which can never match them. A user with no
groups therefore signed in fine, and the moment they received their first `mcp`
grant their claim became non-empty, matched nothing, and they were refused. The
grant meant to give access was what removed it.

The env sets OAUTH_ALLOWED_ROLES=*. The grant itself stays the real gate, since
BYPASS_ADMIN_ACCESS_CONTROL=false keeps it binding on administrators too. Stated
plainly, this does widen a control: every authenticated platform user now passes
Open WebUI's role gate. A list matching the actual role paths would be stricter
and is not built here.

The LAM grant spec is rewritten to drive the real path rather than assert around
it. Three behaviours it now handles: LAM renders per-value add and clear controls
as anchors with an empty alt, so they cannot be found by accessible name and are
located structurally; a membership written in LAM crosses Keycloak federation and
then an Open WebUI login sync before it is observable, so the check polls instead
of reading once; and the sign-out step clears localStorage rather than only
cookies, because Open WebUI keeps its session token there and a cookie-only reset
left the old session live while the assertion passed.

Not verified: openwebui was not in the last deploy's role set, so neither the env
change nor the rewritten spec has run. The spec is also now genuinely
re-authenticating, which is more correct and will surface auth flakiness the
previous version masked.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 14:20:25 +02:00

88 lines
2.8 KiB
Django/Jinja

{% include 'roles/sys-svc-container/templates/env.j2' %}
WEBUI_SECRET_KEY={{ OPENWEBUI_SECRET_KEY | dotenv_quote }}
{% if OPENWEBUI_LITELLM_ENABLED | bool %}
OPENAI_API_BASE_URL={{ LITELLM_BASE_LOCAL_URL }}/v1
OPENAI_API_KEY={{ OPENWEBUI_LITELLM_API_KEY | dotenv_quote }}
{% endif %}
{% if OPENWEBUI_MCP_SERVERS | length > 0 %}
TOOL_SERVER_CONNECTIONS={{ OPENWEBUI_MCP_SERVERS | mcp_tool_server_connections(OPENWEBUI_MCP_GROUP_IDS) | to_json(separators=(',', ':')) | dotenv_quote }}
{% endif %}
OFFLINE_MODE={{ OPENWEBUI_OFFLINE_MODE | ternary(1, 0) }}
HF_HUB_OFFLINE={{ OPENWEBUI_HF_HUB_OFFLINE | ternary(1, 0) }}
ENABLE_PERSISTENT_CONFIG=false
{% if lookup('objstore', application_id, 'enabled') | bool %}
STORAGE_PROVIDER=s3
S3_BUCKET_NAME={{ lookup('objstore', application_id, 'bucket') }}
S3_ENDPOINT_URL={{ lookup('objstore', application_id, 'url') }}
S3_ACCESS_KEY_ID={{ lookup('objstore', application_id, 'access_key') }}
S3_SECRET_ACCESS_KEY={{ lookup('objstore', application_id, 'secret_key') }}
S3_REGION_NAME={{ lookup('objstore', application_id, 'region') }}
{% endif %}
ADMIN_EMAIL={{ lookup('users', 'administrator').email }}
SHOW_ADMIN_DETAILS=true
DEFAULT_USER_ROLE=user
ENABLE_API_KEYS={{ OPENWEBUI_ENABLE_API_KEYS | string | lower }}
{% set email = lookup('email', application_id) %}
{% if email.enabled | bool %}
SMTP_HOST={{ email.host }}
SMTP_PORT={{ email.port }}
SMTP_USERNAME={{ email.username }}
SMTP_PASSWORD={{ email.password | dotenv_quote }}
SMTP_FROM_ADDRESS={{ email['from'] }}
{% endif %}
{% if OPENWEBUI_OIDC_ENABLED | bool %}
ENABLE_OAUTH_SIGNUP=true
OAUTH_CLIENT_ID={{ OIDC.CLIENT.ID }}
OAUTH_CLIENT_SECRET={{ OIDC.CLIENT.SECRET }}
OPENID_PROVIDER_URL={{ OIDC.CLIENT.DISCOVERY_DOCUMENT }}
OPENID_REDIRECT_URI={{ OPENWEBUI_OIDC_REDIRECT_URL }}
OAUTH_PROVIDER_NAME={{ OIDC.BUTTON_TEXT }}
OAUTH_SCOPES=openid email profile {{ RBAC.GROUP.CLAIM }}
ENABLE_OAUTH_ROLE_MANAGEMENT=true
OAUTH_ROLES_CLAIM={{ RBAC.GROUP.CLAIM }}
OAUTH_ADMIN_ROLES=/{{ OPENWEBUI_OIDC_ADMIN_GROUP }}
OAUTH_ALLOWED_ROLES=*
ENABLE_OAUTH_GROUP_MANAGEMENT=true
ENABLE_OAUTH_GROUP_CREATION=true
OAUTH_GROUPS_CLAIM={{ RBAC.GROUP.CLAIM }}
BYPASS_ADMIN_ACCESS_CONTROL=false
{% endif %}
BYPASS_MODEL_ACCESS_CONTROL=true
{% if OPENWEBUI_LDAP_ENABLED | bool %}
ENABLE_LDAP=true
LDAP_SERVER_LABEL=OpenLDAP
LDAP_SERVER_HOST={{ LDAP.SERVER.DOMAIN }}
LDAP_SERVER_PORT={{ LDAP.SERVER.PORT }}
LDAP_USE_TLS={{ ('true' if (LDAP.SERVER.SECURITY | upper) in ['TLS','SSL'] else 'false') }}
LDAP_VALIDATE_CERT={{ ('true' if (LDAP.SERVER.SECURITY | upper) in ['TLS','SSL'] else 'false') }}
LDAP_APP_DN={{ LDAP.DN.ADMINISTRATOR.DATA }}
LDAP_APP_PASSWORD={{ LDAP.BIND_CREDENTIAL | dotenv_quote }}
LDAP_SEARCH_BASE={{ LDAP.DN.ROOT }}
LDAP_ATTRIBUTE_FOR_USERNAME={{ LDAP.USER.ATTRIBUTES.ID }}
LDAP_ATTRIBUTE_FOR_MAIL={{ LDAP.USER.ATTRIBUTES.MAIL }}
LDAP_SEARCH_FILTER=(objectClass=*)
{% endif %}