From a0c82c8e4cc1c4dafd6a558ce85ced66727bc867 Mon Sep 17 00:00:00 2001 From: theeggorchicken Date: Sat, 21 Feb 2026 13:37:08 -0800 Subject: [PATCH] fix: race condition in signup allows multiple admin accounts (#21631) The signup_handler function checks has_users() before inserting a new user and assigns the admin role based on that check. With multiple uvicorn workers, concurrent signup requests during first-user registration can all observe an empty user table before any insert completes, causing multiple accounts to receive the admin role. Fix: insert with the default role first, then check user count after the insert. Only promote to admin if this is the only user in the database. This eliminates the TOCTOU window between the check and the insert. --- backend/open_webui/routers/auths.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/backend/open_webui/routers/auths.py b/backend/open_webui/routers/auths.py index 8ada514cc2..9b246b297b 100644 --- a/backend/open_webui/routers/auths.py +++ b/backend/open_webui/routers/auths.py @@ -700,8 +700,9 @@ async def signup_handler( Returns the newly created UserModel. Raises HTTPException on failure. """ - has_users = Users.has_users(db=db) - role = "admin" if not has_users else request.app.state.config.DEFAULT_USER_ROLE + # Insert with default role first to avoid TOCTOU race on first signup. + # If has_users() is checked before insert, concurrent requests during + # first-user registration can all see an empty table and each get admin. hashed = get_password_hash(password) user = Auths.insert_new_auth( @@ -709,12 +710,19 @@ async def signup_handler( password=hashed, name=name, profile_image_url=profile_image_url, - role=role, + role=request.app.state.config.DEFAULT_USER_ROLE, db=db, ) if not user: raise HTTPException(500, detail=ERROR_MESSAGES.CREATE_USER_ERROR) + # Atomically check if this is the only user *after* the insert. + # Only the single user present at this point should become admin. + if Users.get_num_users(db=db) == 1: + Users.update_user_role_by_id(user.id, "admin", db=db) + user = Users.get_user_by_id(user.id, db=db) + request.app.state.config.ENABLE_SIGNUP = False + if request.app.state.config.WEBHOOK_URL: await post_webhook( request.app.state.WEBUI_NAME, @@ -727,10 +735,6 @@ async def signup_handler( }, ) - if not has_users: - # Disable signup after the first user is created - request.app.state.config.ENABLE_SIGNUP = False - apply_default_group_assignment( request.app.state.config.DEFAULT_GROUP_ID, user.id,