VYPR
High severity8.1GHSA Advisory· Published May 15, 2026· Updated May 15, 2026

CVE-2026-45675

CVE-2026-45675

Description

Open WebUI is a self-hosted artificial intelligence platform designed to operate entirely offline. Prior to 0.9.0, he LDAP and OAuth authentication flows use a TOCTOU (Time-of-Check-Time-of-Use) pattern for first-user admin role assignment. The regular signup handler (signup_handler in auths.py, line 663) was explicitly patched to prevent this race with the comment "Insert with default role first to avoid TOCTOU race", but the LDAP and OAuth code paths were never updated with the same fix. This vulnerability is fixed in 0.9.0.

Affected products

1

Patches

1
96a0b3239b1a

fix: prevent first-user admin race in LDAP and OAuth registration (#23626)

https://github.com/open-webui/open-webuiClassic298Apr 12, 2026via ghsa
2 files changed · +25 6
  • backend/open_webui/routers/auths.py+9 3 modified
    @@ -479,19 +479,25 @@ async def ldap_auth(
                 user = Users.get_user_by_email(email, db=db)
                 if not user:
                     try:
    -                    role = 'admin' if not Users.has_users(db=db) else request.app.state.config.DEFAULT_USER_ROLE
    -
    +                    # Insert with default role first to avoid TOCTOU race on
    +                    # first-user registration.  Matches signup_handler pattern.
                         user = Auths.insert_new_auth(
                             email=email,
                             password=str(uuid.uuid4()),
                             name=cn,
    -                        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 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)
    +
                         apply_default_group_assignment(
                             request.app.state.config.DEFAULT_GROUP_ID,
                             user.id,
    
  • backend/open_webui/utils/oauth.py+16 3 modified
    @@ -1109,9 +1109,12 @@ def get_user_role(self, user, user_data):
                 log.debug('Assigning the only user the admin role')
                 return 'admin'
             if not user and user_count == 0:
    -            # If there are no users, assign the role "admin", as the first user will be an admin
    -            log.debug('Assigning the first user the admin role')
    -            return 'admin'
    +            # First-user bootstrap: skip role management gating so the
    +            # instance can be initialized.  We intentionally return the
    +            # default role here (not 'admin') — admin promotion happens
    +            # race-safely *after* insert via get_num_users() == 1.
    +            log.debug('First user bootstrap: using default role (admin promotion deferred to post-insert)')
    +            return auth_manager_config.DEFAULT_USER_ROLE
     
             if auth_manager_config.ENABLE_OAUTH_ROLE_MANAGEMENT:
                 log.debug('Running OAUTH Role management')
    @@ -1577,6 +1580,16 @@ async def handle_callback(self, request, provider, response, db=None):
                             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 to avoid TOCTOU race on first-user registration.
    +                    # Matches signup_handler pattern.
    +                    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)
    +
                         if auth_manager_config.WEBHOOK_URL:
                             await post_webhook(
                                 WEBUI_NAME,
    

Vulnerability mechanics

AI mechanics synthesis has not run for this CVE yet.

References

6

News mentions

0

No linked articles in our index yet.