[PR #24821] fix: don't block first-admin signup on stale ENABLE_SIGNUP #131536

Open
opened 2026-05-21 17:09:19 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/open-webui/open-webui/pull/24821
Author: @Classic298
Created: 5/17/2026
Status: 🔄 Open

Base: devHead: fix/fresh-install-admin-signup


📝 Commits (1)

  • dbd0218 fix: don't block first-admin signup on stale ENABLE_SIGNUP

📊 Changes

1 file changed (+5 additions, -2 deletions)

View changed files

📝 backend/open_webui/routers/auths.py (+5 -2)

📄 Description

Symptom
On a fresh install (zero users) the frontend shows the mandatory "Create Admin Account" onboarding screen, but POST /api/v1/auths/signup returns 403 ACCESS_PROHIBITED ("You do not have permission to access this resource."). Wiping the database does not help when the config layer is backed by Redis (the value survives in the Redis/valkey volume), or when only the user table is cleared (the config row survives in Postgres). The instance is then unrecoverable through the UI.

Root cause
signup_handler() auto-sets request.app.state.config.ENABLE_SIGNUP = False immediately after the first admin is created. That value is persisted by the config layer (the Postgres config table, and Redis when REDIS_URL is set). On a later zero-user database the persisted False is read back, so ENABLE_SIGNUP resolves False even though no users exist. The old gate was:

if WEBUI_AUTH:
    if not ENABLE_SIGNUP or not ENABLE_LOGIN_FORM:
        if has_users or not ENABLE_INITIAL_ADMIN_SIGNUP:
            403

ENABLE_INITIAL_ADMIN_SIGNUP defaults to False, so with zero users the inner test (has_users or not ENABLE_INITIAL_ADMIN_SIGNUP) is True, and a stale ENABLE_SIGNUP=False trips the outer test, producing a 403 on the only UI path that can create the first admin. The frontend decides to show onboarding purely from user_count == 0, so frontend and backend disagree and the instance bricks.

Change
Split the gate by has_users. Subsequent signups (has_users True) are unchanged: still gated by ENABLE_SIGNUP and ENABLE_LOGIN_FORM. The first user (has_users False, the bootstrap admin the onboarding screen invites) is gated only by the admin-chosen ENABLE_LOGIN_FORM (the documented SSO-only hard-disable) unless ENABLE_INITIAL_ADMIN_SIGNUP is set. It is no longer gated by ENABLE_SIGNUP, which in the zero-user state is never an admin decision but the post-first-admin auto-disable leaking across a database reset.

Why this is safe (full case analysis)
For WEBUI_AUTH the gate has 16 input combinations over (has_users, ENABLE_SIGNUP, ENABLE_LOGIN_FORM, ENABLE_INITIAL_ADMIN_SIGNUP). Old and new are identical in 15 of them:

  • All 8 has_users=True cases: both reduce to "403 iff not ENABLE_SIGNUP or not ENABLE_LOGIN_FORM". Unchanged.
  • 7 of the 8 has_users=False cases: identical. The only changed case is has_users=False, ENABLE_SIGNUP=False, ENABLE_LOGIN_FORM=True, ENABLE_INITIAL_ADMIN_SIGNUP=False: old behaviour 403, new behaviour allow. The new condition is a strict subset of the old (new-403 implies old-403), so the change never newly blocks any request that previously succeeded; it only stops blocking that one bootstrap state.

That state has no legitimate deployment. With the login form enabled and zero users the onboarding form is already served, and the only operator-configurable way to keep the first signup closed (SSO-only: ENABLE_LOGIN_FORM=False, optionally with ENABLE_INITIAL_ADMIN_SIGNUP) is preserved byte for byte. ENABLE_SIGNUP=False with zero users is not an operator choice, it is the automatic post-first-admin disable, so the old behaviour there was purely a brick with no recovery path. No security control is weakened: ENABLE_LOGIN_FORM and ENABLE_INITIAL_ADMIN_SIGNUP keep their exact meaning, and the WEBUI_AUTH=False path is untouched.

This is not Redis-specific: it reproduces with Redis disabled through the Postgres config table alone (clear the user table, keep the config row).

Verification
Drove the real signup endpoint across a 10-case matrix on freshly migrated databases, including the full end-to-end first-admin creation (returns role=admin, row persisted as admin) and the preserved SSO-only, subsequent-signup and no-auth behaviours. All pass

Contributor License Agreement

Note

Deleting the CLA section will lead to immediate closure of your PR and it will not be merged in.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/open-webui/open-webui/pull/24821 **Author:** [@Classic298](https://github.com/Classic298) **Created:** 5/17/2026 **Status:** 🔄 Open **Base:** `dev` ← **Head:** `fix/fresh-install-admin-signup` --- ### 📝 Commits (1) - [`dbd0218`](https://github.com/open-webui/open-webui/commit/dbd0218e46257f7f09e32c36b503632ee8a7788f) fix: don't block first-admin signup on stale ENABLE_SIGNUP ### 📊 Changes **1 file changed** (+5 additions, -2 deletions) <details> <summary>View changed files</summary> 📝 `backend/open_webui/routers/auths.py` (+5 -2) </details> ### 📄 Description Symptom On a fresh install (zero users) the frontend shows the mandatory "Create Admin Account" onboarding screen, but POST /api/v1/auths/signup returns 403 ACCESS_PROHIBITED ("You do not have permission to access this resource."). Wiping the database does not help when the config layer is backed by Redis (the value survives in the Redis/valkey volume), or when only the user table is cleared (the config row survives in Postgres). The instance is then unrecoverable through the UI. Root cause signup_handler() auto-sets request.app.state.config.ENABLE_SIGNUP = False immediately after the first admin is created. That value is persisted by the config layer (the Postgres config table, and Redis when REDIS_URL is set). On a later zero-user database the persisted False is read back, so ENABLE_SIGNUP resolves False even though no users exist. The old gate was: if WEBUI_AUTH: if not ENABLE_SIGNUP or not ENABLE_LOGIN_FORM: if has_users or not ENABLE_INITIAL_ADMIN_SIGNUP: 403 ENABLE_INITIAL_ADMIN_SIGNUP defaults to False, so with zero users the inner test (has_users or not ENABLE_INITIAL_ADMIN_SIGNUP) is True, and a stale ENABLE_SIGNUP=False trips the outer test, producing a 403 on the only UI path that can create the first admin. The frontend decides to show onboarding purely from user_count == 0, so frontend and backend disagree and the instance bricks. Change Split the gate by has_users. Subsequent signups (has_users True) are unchanged: still gated by ENABLE_SIGNUP and ENABLE_LOGIN_FORM. The first user (has_users False, the bootstrap admin the onboarding screen invites) is gated only by the admin-chosen ENABLE_LOGIN_FORM (the documented SSO-only hard-disable) unless ENABLE_INITIAL_ADMIN_SIGNUP is set. It is no longer gated by ENABLE_SIGNUP, which in the zero-user state is never an admin decision but the post-first-admin auto-disable leaking across a database reset. Why this is safe (full case analysis) For WEBUI_AUTH the gate has 16 input combinations over (has_users, ENABLE_SIGNUP, ENABLE_LOGIN_FORM, ENABLE_INITIAL_ADMIN_SIGNUP). Old and new are identical in 15 of them: * All 8 has_users=True cases: both reduce to "403 iff not ENABLE_SIGNUP or not ENABLE_LOGIN_FORM". Unchanged. * 7 of the 8 has_users=False cases: identical. The only changed case is has_users=False, ENABLE_SIGNUP=False, ENABLE_LOGIN_FORM=True, ENABLE_INITIAL_ADMIN_SIGNUP=False: old behaviour 403, new behaviour allow. The new condition is a strict subset of the old (new-403 implies old-403), so the change never newly blocks any request that previously succeeded; it only stops blocking that one bootstrap state. That state has no legitimate deployment. With the login form enabled and zero users the onboarding form is already served, and the only operator-configurable way to keep the first signup closed (SSO-only: ENABLE_LOGIN_FORM=False, optionally with ENABLE_INITIAL_ADMIN_SIGNUP) is preserved byte for byte. ENABLE_SIGNUP=False with zero users is not an operator choice, it is the automatic post-first-admin disable, so the old behaviour there was purely a brick with no recovery path. No security control is weakened: ENABLE_LOGIN_FORM and ENABLE_INITIAL_ADMIN_SIGNUP keep their exact meaning, and the WEBUI_AUTH=False path is untouched. This is not Redis-specific: it reproduces with Redis disabled through the Postgres config table alone (clear the user table, keep the config row). Verification Drove the real signup endpoint across a 10-case matrix on freshly migrated databases, including the full end-to-end first-admin creation (returns role=admin, row persisted as admin) and the preserved SSO-only, subsequent-signup and no-auth behaviours. All pass ### Contributor License Agreement <!-- 🚨 DO NOT DELETE THE TEXT BELOW 🚨 Keep the "Contributor License Agreement" confirmation text intact. Deleting it will trigger the CLA-Bot to INVALIDATE your PR. Your PR will NOT be reviewed or merged until you check the box below confirming that you have read and agree to the terms of the CLA. --> - [x] By submitting this pull request, I confirm that I have read and fully agree to the [Contributor License Agreement (CLA)](https://github.com/open-webui/open-webui/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT), and I am providing my contributions under its terms. > [!NOTE] > Deleting the CLA section will lead to immediate closure of your PR and it will not be merged in. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-05-21 17:09:19 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#131536