feat: add a master OAuth / OIDC enable toggle in Authentication settings (#26988)

The OAuth / OIDC section in Admin Settings > Authentication had no
enable/disable switch, unlike the LDAP section above it. Add one that
persists via the existing Save flow and actually gates OAuth sign-in,
mirroring how the LDAP toggle works.

- config: new ENABLE_OAUTH persistent config ('oauth.enable'), defaulting
  to True so existing deployments with a provider configured keep working.
- oauth: expose ENABLE_OAUTH via the OAuth runtime config and reject the
  login and callback handlers with 404 when it is disabled.
- /api/config: report no OAuth providers when disabled so the login page
  hides the OAuth buttons (and cannot auto-redirect), without clearing the
  admin's provider configuration.
- auths: expose ENABLE_OAUTH through the admin OAuth config get/update
  endpoints (OAuthConfigForm + OAUTH_CONFIG_KEYS).
- Authentication.svelte: bind the OAuth / OIDC header Switch to the
  persisted oauthConfig.ENABLE_OAUTH and collapse the section when off,
  matching the LDAP header (size, weight, alignment).
This commit is contained in:
G30 authored and GitHub committed 2026-07-26 18:43:21 -04:00
1 parent 18ca19044c
commit 71f8b6d5b4
5 files changed
+48 -5

No files matched your search

+6
View File
@@ -2429,6 +2429,11 @@ if JWT_EXPIRES_IN == '-1':
# OAuth config # OAuth config
#################################### ####################################
# Master switch for OAuth/OIDC sign-in. Defaults to enabled so existing
# deployments that already have a provider configured keep working; admins can
# turn it off to disable OAuth login without clearing their provider settings.
ENABLE_OAUTH = os.getenv('ENABLE_OAUTH', 'True').lower() == 'true'
ENABLE_OAUTH_SIGNUP = os.getenv('ENABLE_OAUTH_SIGNUP', 'False').lower() == 'true' ENABLE_OAUTH_SIGNUP = os.getenv('ENABLE_OAUTH_SIGNUP', 'False').lower() == 'true'
OAUTH_AUTO_REDIRECT = os.getenv('OAUTH_AUTO_REDIRECT', 'False').lower() == 'true' OAUTH_AUTO_REDIRECT = os.getenv('OAUTH_AUTO_REDIRECT', 'False').lower() == 'true'
@@ -3091,6 +3096,7 @@ DEFAULT_CONFIG = {
'auth.api_key.endpoint_restrictions': ENABLE_API_KEYS_ENDPOINT_RESTRICTIONS, 'auth.api_key.endpoint_restrictions': ENABLE_API_KEYS_ENDPOINT_RESTRICTIONS,
'auth.api_key.allowed_endpoints': API_KEYS_ALLOWED_ENDPOINTS, 'auth.api_key.allowed_endpoints': API_KEYS_ALLOWED_ENDPOINTS,
'auth.jwt_expiry': JWT_EXPIRES_IN, 'auth.jwt_expiry': JWT_EXPIRES_IN,
'oauth.enable': ENABLE_OAUTH,
'oauth.enable_signup': ENABLE_OAUTH_SIGNUP, 'oauth.enable_signup': ENABLE_OAUTH_SIGNUP,
'oauth.auto_redirect': OAUTH_AUTO_REDIRECT, 'oauth.auto_redirect': OAUTH_AUTO_REDIRECT,
'oauth.refresh_token.include_scope': OAUTH_REFRESH_TOKEN_INCLUDE_SCOPE, 'oauth.refresh_token.include_scope': OAUTH_REFRESH_TOKEN_INCLUDE_SCOPE,
+8 -1
View File
@@ -1998,6 +1998,7 @@ async def get_app_config(request: Request):
license_metadata = getattr(app.state, 'LICENSE_METADATA', None) license_metadata = getattr(app.state, 'LICENSE_METADATA', None)
user_count = await Users.get_num_users() if license_metadata else None user_count = await Users.get_num_users() if license_metadata else None
config = await Config.get_many( config = await Config.get_many(
'oauth.enable',
'oauth.auto_redirect', 'oauth.auto_redirect',
'ldap.enable', 'ldap.enable',
'ui.enable_signup', 'ui.enable_signup',
@@ -2052,7 +2053,13 @@ async def get_app_config(request: Request):
'version': VERSION, 'version': VERSION,
'default_locale': str(DEFAULT_LOCALE), 'default_locale': str(DEFAULT_LOCALE),
'oauth': { 'oauth': {
'providers': {name: config.get('name', name) for name, config in OAUTH_PROVIDERS.items()}, # Hide providers (and thus the login buttons / auto-redirect) when OAuth
# is disabled, without clearing the admin's provider configuration.
'providers': (
{name: provider.get('name', name) for name, provider in OAUTH_PROVIDERS.items()}
if config.get('oauth.enable', True)
else {}
),
'auto_redirect': config.get('oauth.auto_redirect'), 'auto_redirect': config.get('oauth.auto_redirect'),
}, },
'features': { 'features': {
+2
View File
@@ -1304,6 +1304,7 @@ class OAuthConfigForm(BaseModel):
"""All OAuth/OIDC settings exposed to the admin panel.""" """All OAuth/OIDC settings exposed to the admin panel."""
# General OAuth # General OAuth
ENABLE_OAUTH: bool | None = None
ENABLE_OAUTH_SIGNUP: bool | None = None ENABLE_OAUTH_SIGNUP: bool | None = None
OAUTH_MERGE_ACCOUNTS_BY_EMAIL: bool | None = None OAUTH_MERGE_ACCOUNTS_BY_EMAIL: bool | None = None
OAUTH_AUTO_REDIRECT: bool | None = None OAUTH_AUTO_REDIRECT: bool | None = None
@@ -1359,6 +1360,7 @@ OAUTH_COMMA_LIST_FIELDS = {
OAUTH_CONFIG_KEYS = { OAUTH_CONFIG_KEYS = {
'ENABLE_OAUTH': 'oauth.enable',
'ENABLE_OAUTH_SIGNUP': 'oauth.enable_signup', 'ENABLE_OAUTH_SIGNUP': 'oauth.enable_signup',
'OAUTH_MERGE_ACCOUNTS_BY_EMAIL': 'oauth.merge_accounts_by_email', 'OAUTH_MERGE_ACCOUNTS_BY_EMAIL': 'oauth.merge_accounts_by_email',
'OAUTH_AUTO_REDIRECT': 'oauth.auto_redirect', 'OAUTH_AUTO_REDIRECT': 'oauth.auto_redirect',
+6
View File
@@ -35,6 +35,7 @@ from mcp.shared.auth import (
from open_webui.config import ( from open_webui.config import (
DEFAULT_USER_ROLE, DEFAULT_USER_ROLE,
ENABLE_OAUTH_GROUP_CREATION, ENABLE_OAUTH_GROUP_CREATION,
ENABLE_OAUTH,
ENABLE_OAUTH_GROUP_MANAGEMENT, ENABLE_OAUTH_GROUP_MANAGEMENT,
ENABLE_OAUTH_ROLE_MANAGEMENT, ENABLE_OAUTH_ROLE_MANAGEMENT,
ENABLE_OAUTH_SIGNUP, ENABLE_OAUTH_SIGNUP,
@@ -120,6 +121,7 @@ OAUTH_RESOURCE_PARAMETER_MODES = {'auto', 'include', 'omit'}
OAUTH_RUNTIME_CONFIG = { OAUTH_RUNTIME_CONFIG = {
'DEFAULT_USER_ROLE': ('ui.default_user_role', DEFAULT_USER_ROLE), 'DEFAULT_USER_ROLE': ('ui.default_user_role', DEFAULT_USER_ROLE),
'ENABLE_OAUTH': ('oauth.enable', ENABLE_OAUTH),
'ENABLE_OAUTH_SIGNUP': ('oauth.enable_signup', ENABLE_OAUTH_SIGNUP), 'ENABLE_OAUTH_SIGNUP': ('oauth.enable_signup', ENABLE_OAUTH_SIGNUP),
'OAUTH_REFRESH_TOKEN_INCLUDE_SCOPE': ( 'OAUTH_REFRESH_TOKEN_INCLUDE_SCOPE': (
'oauth.refresh_token.include_scope', 'oauth.refresh_token.include_scope',
@@ -1670,6 +1672,8 @@ class OAuthManager:
async def handle_login(self, request, provider): async def handle_login(self, request, provider):
auth_config = await get_oauth_runtime_config() auth_config = await get_oauth_runtime_config()
if not auth_config.ENABLE_OAUTH:
raise HTTPException(404)
if provider not in OAUTH_PROVIDERS: if provider not in OAUTH_PROVIDERS:
raise HTTPException(404) raise HTTPException(404)
# If the provider has a custom redirect URL, use that, otherwise automatically generate one # If the provider has a custom redirect URL, use that, otherwise automatically generate one
@@ -1690,6 +1694,8 @@ class OAuthManager:
async def handle_callback(self, request, provider, response, db=None): async def handle_callback(self, request, provider, response, db=None):
auth_config = await get_oauth_runtime_config() auth_config = await get_oauth_runtime_config()
if not auth_config.ENABLE_OAUTH:
raise HTTPException(404)
if provider not in OAUTH_PROVIDERS: if provider not in OAUTH_PROVIDERS:
raise HTTPException(404) raise HTTPException(404)
@@ -523,6 +523,15 @@
{#if oauthConfig} {#if oauthConfig}
<AdminSettingSection title={$i18n.t('OAuth / OIDC')}> <AdminSettingSection title={$i18n.t('OAuth / OIDC')}>
<AdminSettingRow
label={$i18n.t('OAuth / OIDC')}
description={$i18n.t('Allow users to authenticate with an OAuth / OIDC provider.')}
let:labelId
>
<Switch bind:state={oauthConfig.ENABLE_OAUTH} ariaLabelledbyId={labelId} />
</AdminSettingRow>
{#if oauthConfig.ENABLE_OAUTH}
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2"> <div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
<AdminSettingField <AdminSettingField
label={$i18n.t('Provider Name')} label={$i18n.t('Provider Name')}
@@ -636,7 +645,11 @@
label={$i18n.t('Sub Claim')} label={$i18n.t('Sub Claim')}
description={$i18n.t('Claim used as the stable user identifier.')} description={$i18n.t('Claim used as the stable user identifier.')}
> >
<input class={inputClass} placeholder="sub" bind:value={oauthConfig.OAUTH_SUB_CLAIM} /> <input
class={inputClass}
placeholder="sub"
bind:value={oauthConfig.OAUTH_SUB_CLAIM}
/>
</AdminSettingField> </AdminSettingField>
</div> </div>
@@ -661,7 +674,9 @@
<AdminSettingRow <AdminSettingRow
label={$i18n.t('Auto Redirect')} label={$i18n.t('Auto Redirect')}
description={$i18n.t('Send users directly to the OAuth provider from the sign-in page.')} description={$i18n.t(
'Send users directly to the OAuth provider from the sign-in page.'
)}
let:labelId let:labelId
> >
<Switch bind:state={oauthConfig.OAUTH_AUTO_REDIRECT} ariaLabelledbyId={labelId} /> <Switch bind:state={oauthConfig.OAUTH_AUTO_REDIRECT} ariaLabelledbyId={labelId} />
@@ -779,7 +794,10 @@
description={$i18n.t('Refresh the account email from OAuth on sign-in.')} description={$i18n.t('Refresh the account email from OAuth on sign-in.')}
let:labelId let:labelId
> >
<Switch bind:state={oauthConfig.OAUTH_UPDATE_EMAIL_ON_LOGIN} ariaLabelledbyId={labelId} /> <Switch
bind:state={oauthConfig.OAUTH_UPDATE_EMAIL_ON_LOGIN}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow> </AdminSettingRow>
<AdminSettingRow <AdminSettingRow
@@ -787,7 +805,10 @@
description={$i18n.t('Refresh the account name from OAuth on sign-in.')} description={$i18n.t('Refresh the account name from OAuth on sign-in.')}
let:labelId let:labelId
> >
<Switch bind:state={oauthConfig.OAUTH_UPDATE_NAME_ON_LOGIN} ariaLabelledbyId={labelId} /> <Switch
bind:state={oauthConfig.OAUTH_UPDATE_NAME_ON_LOGIN}
ariaLabelledbyId={labelId}
/>
</AdminSettingRow> </AdminSettingRow>
<AdminSettingRow <AdminSettingRow
@@ -800,6 +821,7 @@
ariaLabelledbyId={labelId} ariaLabelledbyId={labelId}
/> />
</AdminSettingRow> </AdminSettingRow>
{/if}
</AdminSettingSection> </AdminSettingSection>
{/if} {/if}
</div> </div>