c86a69f fix: race condition in signup allows multiple admin accounts
📊 Changes
1 file changed (+11 additions, -7 deletions)
View changed files
📝backend/open_webui/routers/auths.py (+11 -7)
📄 Description
Pull Request Checklist
Target branch: Verify that the pull request targets the dev branch.
Description: Provided below.
Changelog: Included below.
Documentation: N/A — internal code fix, no user-facing API changes.
Dependencies: No new dependencies.
Testing: Tested with 4 uvicorn workers and 30 concurrent signup requests. Before fix: 4 admin accounts created. After fix: only 1 admin.
Agentic AI Code: This fix has been human-reviewed and manually tested against a running Open WebUI instance.
Code review: Self-reviewed.
Design & Architecture: Minimal change — no new settings, no UI changes.
Git Hygiene: Single atomic commit.
Title Prefix:fix:
Changelog Entry
Description
Race condition in signup_handler allows multiple admin account registrations on fresh deployments when running with multiple uvicorn workers.
Fixed
TOCTOU race in signup_handler where has_users() check is not atomic with insert_new_auth(). With multiple workers, concurrent signup requests during first-user registration can all see an empty user table and each receive the admin role.
Security
Prevents unauthorized admin privilege escalation during initial setup on multi-worker deployments.
has_users=Users.has_users(db=db)# CHECKrole="admin"ifnothas_userselse...# DECIDEhashed=get_password_hash(password)# bcrypt ~100ms windowuser=Auths.insert_new_auth(...,role=role,db=db)# USE (stale check)
What could happen
On a fresh Open WebUI deployment with multiple uvicorn workers (--workers >= 2), an attacker can send concurrent signup requests during first-user registration. Each worker's has_users() returns False before any insert completes, so multiple accounts get the admin role. I tested with 4 workers and 30 concurrent requests — 4 accounts got admin, each with full admin privileges (listing users, reading config, managing the instance).
How to verify
Start Open WebUI with UVICORN_WORKERS=4 and ENABLE_SIGNUP=true
On a fresh instance (no existing users), fire 20+ concurrent POST requests to /api/v1/auths/signup with different email addresses
Check how many responses contain "role": "admin" — without this fix, multiple will
What this PR does
Reverses the signup flow: insert the user with the default role first, then atomically check if the user count is 1 (meaning this is the only user). Only then promote to admin. This closes the race window because after the insert, the count includes the just-inserted row — if two workers insert concurrently, both will see count >= 2 and neither gets promoted.
By submitting this pull request, I confirm that I have read and fully agree to the Contributor License Agreement (CLA), and I am providing my contributions under its terms.
🔄 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/21631
**Author:** [@theeggorchicken](https://github.com/theeggorchicken)
**Created:** 2/20/2026
**Status:** ✅ Merged
**Merged:** 2/21/2026
**Merged by:** [@tjbck](https://github.com/tjbck)
**Base:** `dev` ← **Head:** `fix/signup-race-condition`
---
### 📝 Commits (1)
- [`c86a69f`](https://github.com/open-webui/open-webui/commit/c86a69f6197d7197cf1709a5346673f863aebaa8) fix: race condition in signup allows multiple admin accounts
### 📊 Changes
**1 file changed** (+11 additions, -7 deletions)
<details>
<summary>View changed files</summary>
📝 `backend/open_webui/routers/auths.py` (+11 -7)
</details>
### 📄 Description
<!--
⚠️ CRITICAL CHECKS FOR CONTRIBUTORS (READ, DON'T DELETE) ⚠️
1. Target the `dev` branch. PRs targeting `main` will be automatically closed.
2. Do NOT delete the CLA section at the bottom. It is required for the bot to accept your PR.
-->
# Pull Request Checklist
- [x] **Target branch:** Verify that the pull request targets the `dev` branch.
- [x] **Description:** Provided below.
- [x] **Changelog:** Included below.
- [ ] **Documentation:** N/A — internal code fix, no user-facing API changes.
- [ ] **Dependencies:** No new dependencies.
- [x] **Testing:** Tested with 4 uvicorn workers and 30 concurrent signup requests. Before fix: 4 admin accounts created. After fix: only 1 admin.
- [x] **Agentic AI Code:** This fix has been human-reviewed and manually tested against a running Open WebUI instance.
- [x] **Code review:** Self-reviewed.
- [x] **Design & Architecture:** Minimal change — no new settings, no UI changes.
- [x] **Git Hygiene:** Single atomic commit.
- [x] **Title Prefix:** `fix:`
# Changelog Entry
### Description
Race condition in `signup_handler` allows multiple admin account registrations on fresh deployments when running with multiple uvicorn workers.
### Fixed
- TOCTOU race in `signup_handler` where `has_users()` check is not atomic with `insert_new_auth()`. With multiple workers, concurrent signup requests during first-user registration can all see an empty user table and each receive the admin role.
### Security
- Prevents unauthorized admin privilege escalation during initial setup on multi-worker deployments.
---
### Vulnerable Lines
**File:** [`backend/open_webui/routers/auths.py#L703-L709`](https://github.com/open-webui/open-webui/blob/main/backend/open_webui/routers/auths.py#L703-L709)
```python
has_users = Users.has_users(db=db) # CHECK
role = "admin" if not has_users else ... # DECIDE
hashed = get_password_hash(password) # bcrypt ~100ms window
user = Auths.insert_new_auth(..., role=role, db=db) # USE (stale check)
```
### What could happen
On a fresh Open WebUI deployment with multiple uvicorn workers (`--workers >= 2`), an attacker can send concurrent signup requests during first-user registration. Each worker's `has_users()` returns False before any insert completes, so multiple accounts get the admin role. I tested with 4 workers and 30 concurrent requests — 4 accounts got admin, each with full admin privileges (listing users, reading config, managing the instance).
### How to verify
1. Start Open WebUI with `UVICORN_WORKERS=4` and `ENABLE_SIGNUP=true`
2. On a fresh instance (no existing users), fire 20+ concurrent POST requests to `/api/v1/auths/signup` with different email addresses
3. Check how many responses contain `"role": "admin"` — without this fix, multiple will
### What this PR does
Reverses the signup flow: insert the user with the default role first, then atomically check if the user count is 1 (meaning this is the only user). Only then promote to admin. This closes the race window because after the insert, the count includes the just-inserted row — if two workers insert concurrently, both will see count >= 2 and neither gets promoted.
### Evidence
Full race condition exploit traces: https://gist.github.com/theeggorchicken/455f6c46ea73bd96396400a679c3c207
### Contributor License Agreement
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.
---
<sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
📋 Pull Request Information
Original PR: https://github.com/open-webui/open-webui/pull/21631
Author: @theeggorchicken
Created: 2/20/2026
Status: ✅ Merged
Merged: 2/21/2026
Merged by: @tjbck
Base:
dev← Head:fix/signup-race-condition📝 Commits (1)
c86a69ffix: race condition in signup allows multiple admin accounts📊 Changes
1 file changed (+11 additions, -7 deletions)
View changed files
📝
backend/open_webui/routers/auths.py(+11 -7)📄 Description
Pull Request Checklist
devbranch.fix:Changelog Entry
Description
Race condition in
signup_handlerallows multiple admin account registrations on fresh deployments when running with multiple uvicorn workers.Fixed
signup_handlerwherehas_users()check is not atomic withinsert_new_auth(). With multiple workers, concurrent signup requests during first-user registration can all see an empty user table and each receive the admin role.Security
Vulnerable Lines
File:
backend/open_webui/routers/auths.py#L703-L709What could happen
On a fresh Open WebUI deployment with multiple uvicorn workers (
--workers >= 2), an attacker can send concurrent signup requests during first-user registration. Each worker'shas_users()returns False before any insert completes, so multiple accounts get the admin role. I tested with 4 workers and 30 concurrent requests — 4 accounts got admin, each with full admin privileges (listing users, reading config, managing the instance).How to verify
UVICORN_WORKERS=4andENABLE_SIGNUP=true/api/v1/auths/signupwith different email addresses"role": "admin"— without this fix, multiple willWhat this PR does
Reverses the signup flow: insert the user with the default role first, then atomically check if the user count is 1 (meaning this is the only user). Only then promote to admin. This closes the race window because after the insert, the count includes the just-inserted row — if two workers insert concurrently, both will see count >= 2 and neither gets promoted.
Evidence
Full race condition exploit traces: https://gist.github.com/theeggorchicken/455f6c46ea73bd96396400a679c3c207
Contributor License Agreement
By submitting this pull request, I confirm that I have read and fully agree to the Contributor License Agreement (CLA), and I am providing my contributions under its terms.
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.