---
title: Authentication
description: Configure sign-in for the chat app and the Console with email and password, Google, Microsoft, GitHub, OIDC, LDAP or a trusted proxy header, and manage accounts in the Console.
canonical_url: https://hubzoid.com/docs/deploy/authentication
last_updated: 2026-09-27
---

# Authentication

Configure sign-in for the chat app and the Console with email and password, Google, Microsoft, GitHub, OIDC, LDAP or a trusted proxy header, and manage accounts in the Console.

Sign-in is handled by the chat app (Open WebUI) and configured with environment variables. Hubzoid adds no second login. The Console at `/portal/` reuses the same session, validates it on the server, and then checks Hubzoid's own permissions. Choose a mode, add its lines, restart.

## When to use this

Turn sign-in on for anything beyond a single person on `localhost`. With sign-in off, anyone who can reach the port is in, and `hubzoid doctor` fails `auth.chat_signin` when the public port listens on every interface. Sign-in decides who gets in. What each person may do once inside is set by [access administration](https://hubzoid.com/docs/deploy/access-administration).

## Where the settings go

| Deployment | Put the sign-in variables in |
| --- | --- |
| One hub (`hubzoid run`) | The hub's `.env` |
| Several separate hubs | Each hub's `.env`. Every hub has its own user list in `<hub>/.openwebui-data/webui.db`. |
| Gateway (`hubzoid gateway`) | The environment the `hubzoid gateway` process starts with. A sign-in setting missing there is still taken from a hub's `.env` and named at start, so move it to the gateway's environment. |

## Modes

| Mode | Use it for |
| --- | --- |
| No sign-in (default) | Local development by one person |
| Email and password | Small teams without an identity provider |
| Google, Microsoft or GitHub | Teams with a workspace or consumer identity |
| Generic OIDC (Okta, Auth0, Keycloak, authentik and others) | An enterprise identity provider |
| LDAP or Active Directory | On-premises directories |
| Trusted header | Sign-in already handled by a proxy such as oauth2-proxy or Cloudflare Access |

## The baseline for every mode

```bash title=".env"
WEBUI_AUTH=true
WEBUI_SECRET_KEY=<openssl rand -hex 32>
WEBUI_URL=https://hub.example.com
ENABLE_SIGNUP=false
DEFAULT_USER_ROLE=user
```

Hubzoid checks two things before it starts the chat app:

- `WEBUI_AUTH=true` requires a `WEBUI_SECRET_KEY`. Hubzoid refuses to start without one, or with Open WebUI's public fallback value, because that key signs every session.
- When any of `GOOGLE_CLIENT_ID`, `MICROSOFT_CLIENT_ID`, `GITHUB_CLIENT_ID` or `OAUTH_CLIENT_ID` is set, `WEBUI_URL` must be set too. OAuth callbacks are built from it.

Keep `WEBUI_SECRET_KEY` stable. Changing it signs everyone out, including after a restore.

Public sign-up is closed by default. Hubzoid starts the chat app with `ENABLE_SIGNUP` and `ENABLE_OAUTH_SIGNUP` off unless you set them, so nobody can register themselves by email or through a provider. Administrators create accounts in the Console instead.

## Email and password

Add a one-time administrator to the baseline:

```bash title=".env"
WEBUI_ADMIN_EMAIL=you@example.com
WEBUI_ADMIN_PASSWORD=<temporary password>
```

On a fresh database the chat app creates this account as its administrator, with no public signup window. Start the hub and sign in at `/` with it once, which also makes it the [first Hubzoid administrator](https://hubzoid.com/docs/deploy/authentication#the-first-administrator-and-the-console). Then delete both lines and restart. Open **Admin Console** from the chat sidebar and add the rest of the team with **Add user**, which needs the service account described in [people and accounts](https://hubzoid.com/docs/deploy/authentication#people-and-accounts).

In a gateway, `HUBZOID_GATEWAY_ADMIN_EMAIL` and `HUBZOID_GATEWAY_ADMIN_PASSWORD` do the same job on a fresh data directory and stay in place, because the gateway and the Console use the account on every start. See [provisioning](https://hubzoid.com/docs/deploy/gateway#provisioning-each-hub).

## Google, Microsoft and GitHub

### Google

In the Google Cloud console, configure the OAuth consent screen (**Internal** if every sign-in comes from your Google Workspace), then create an OAuth client ID of type **Web application** with:

- authorized JavaScript origin `https://hub.example.com`
- authorized redirect URI `https://hub.example.com/oauth/google/callback`

```bash title=".env"
GOOGLE_CLIENT_ID=<id>.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=<secret>
OAUTH_MERGE_ACCOUNTS_BY_EMAIL=true
OAUTH_ALLOWED_DOMAINS=example.com
```

Sign-up stays closed, so Google never creates an account. Create each person in the Console with **Add user**, choosing **Google sign-in only** or a password. `OAUTH_MERGE_ACCOUNTS_BY_EMAIL=true` lets the chat app attach their Google sign-in to that account by email. Without it, the Console does not offer **Google sign-in only**. Keep these settings in the environment, because the Console reads them from there, and restart a gateway after changing them.

`OAUTH_ALLOWED_DOMAINS` rejects sign-ins from any other email domain and takes a comma-separated list. List only domains you control, because the chat app links accounts by email without checking the provider's `email_verified` claim.

To let people register themselves with Google instead, set `ENABLE_OAUTH_SIGNUP=true` and `DEFAULT_USER_ROLE=pending`, then approve each new account under **People** in the Console.

### Microsoft

Register an app in Microsoft Entra ID with the redirect URI `https://hub.example.com/oauth/microsoft/callback`.

```bash title=".env"
ENABLE_OAUTH_SIGNUP=true
MICROSOFT_CLIENT_ID=<id>
MICROSOFT_CLIENT_SECRET=<secret>
MICROSOFT_CLIENT_TENANT_ID=<tenant id, or common>
MICROSOFT_OAUTH_SCOPE=openid email profile offline_access
```

### GitHub

Create a GitHub OAuth app with the callback URL `https://hub.example.com/oauth/github/callback`.

```bash title=".env"
ENABLE_OAUTH_SIGNUP=true
GITHUB_CLIENT_ID=<id>
GITHUB_CLIENT_SECRET=<secret>
GITHUB_CLIENT_SCOPE=user:email
```

Add these to the baseline. The redirect path is exact, with no trailing slash. `ENABLE_OAUTH_SIGNUP=true` opens registration to anyone the provider signs in, so pair it with `DEFAULT_USER_ROLE=pending` if new accounts should wait for an administrator's approval.

## Generic OIDC

Any provider that publishes a `.well-known/openid-configuration` document works. Register `https://hub.example.com/oauth/oidc/callback` as the redirect URI.

```bash title=".env"
ENABLE_OAUTH_SIGNUP=true
OAUTH_CLIENT_ID=<id>
OAUTH_CLIENT_SECRET=<secret>
OPENID_PROVIDER_URL=https://idp.example.com/.well-known/openid-configuration
OAUTH_PROVIDER_NAME=Okta
OAUTH_SCOPES=openid email profile
```

`OAUTH_PROVIDER_NAME` is the label on the sign-in button. When your provider sends roles or groups as claims, the chat app can sync them on every sign-in:

```bash title=".env"
ENABLE_OAUTH_ROLE_MANAGEMENT=true
OAUTH_ROLES_CLAIM=roles
OAUTH_ALLOWED_ROLES=user,admin
OAUTH_ADMIN_ROLES=admin

ENABLE_OAUTH_GROUP_MANAGEMENT=true
OAUTH_GROUPS_CLAIM=groups
```

Synced groups are chat app groups. They grant restricted tools only on hubs that still use group-based access. Hubs on managed grants take permissions from the Console.

## LDAP and Active Directory

```bash title=".env"
ENABLE_LDAP=true
LDAP_SERVER_HOST=ldap.example.com
LDAP_SERVER_PORT=636
LDAP_USE_TLS=true
LDAP_SEARCH_BASE=dc=example,dc=com
LDAP_APP_DN=cn=svc-hubzoid,ou=users,dc=example,dc=com
LDAP_APP_PASSWORD=<password>
LDAP_ATTRIBUTE_FOR_USERNAME=sAMAccountName
LDAP_SEARCH_FILTER=(objectClass=person)
```

The rest of Hubzoid behaves the same as with any other mode. Open WebUI's LDAP documentation covers directory-specific options.

## Trusted header

When a proxy such as oauth2-proxy, Cloudflare Access or Authelia has already signed the person in, the chat app can take their identity from headers the proxy sets:

```bash title=".env"
WEBUI_AUTH=true
WEBUI_AUTH_TRUSTED_EMAIL_HEADER=X-Forwarded-Email
WEBUI_AUTH_TRUSTED_NAME_HEADER=X-Forwarded-User
WEBUI_AUTH_TRUSTED_GROUPS_HEADER=X-Forwarded-Groups
WEBUI_AUTH_TRUSTED_ROLE_HEADER=X-Forwarded-Role
```

> **Caution: The proxy must own these headers**
>
> The chat app does no check of its own on these headers, and the Hubzoid edge forwards them. Your proxy must remove any client-sent copy before it sets its own, and nothing but the proxy may reach the Hubzoid port. Keep the edge on `--host 127.0.0.1` (the default) with the proxy on the same machine.

The edge does drop client-sent `X-Hubzoid-*` and `X-OpenWebUI-*` headers. Hubzoid receives the person's identity from the chat app, never from the browser.

## Redirect URIs

| Provider | Redirect URI |
| --- | --- |
| Google | `https://<host>/oauth/google/callback` |
| Microsoft | `https://<host>/oauth/microsoft/callback` |
| GitHub | `https://<host>/oauth/github/callback` |
| Generic OIDC | `https://<host>/oauth/oidc/callback` |

`<host>` must match `WEBUI_URL`. The edge keeps the incoming `Host` header and, when `WEBUI_URL` starts with `https://`, reports HTTPS as the forwarded scheme if your proxy did not, so the chat app builds the redirect from the public address.

## The first administrator and the Console

A chat app administrator is not automatically a Hubzoid administrator. Hubzoid provisions one configured owner: the account in `HUBZOID_GATEWAY_ADMIN_EMAIL`, else `WEBUI_ADMIN_EMAIL`, which must be a chat app administrator. The first time that account signs in and opens the chat or `/portal/`, it receives organization administration and **Use this agent** in each hub, once. A later sign-in never restores access you revoke. With sign-in off, the one local account `admin@localhost` is the owner.

For a headless setup or recovery, grant the first administrator from the server instead:

```bash
hubzoid access bootstrap --admin you@example.com ./my-hub
```

Administrators then see **Admin Console** above their profile in the chat sidebar. The Console accepts a session the chat app issued, validated on the server, and on its management API (`/portal/api`) also the person's own chat app API key. It ignores identity headers sent by a browser, and people without Manage access cannot open it. See [access administration](https://hubzoid.com/docs/deploy/access-administration#the-first-administrator).

## People and accounts

The chat app keeps credentials and sessions. The Console creates and changes accounts through the chat app's admin API, as the service account in `HUBZOID_GATEWAY_ADMIN_EMAIL` and `HUBZOID_GATEWAY_ADMIN_PASSWORD`, which must be a chat app administrator. Set both in the gateway's environment, or in a single hub's `.env`.

- **Add user**, on an agent's Access page or under **People**, creates a new account with its first access: name, email and a password you type or generate. The password is shown once for you to share. Nothing is emailed. **Google sign-in only** creates the account with no password when Google sign-in and `OAUTH_MERGE_ACCOUNTS_BY_EMAIL=true` are configured. An email that already has an account changes nothing, and the Console links to that user instead.
- Organization administrators approve pending sign-ups, reset passwords and set a user's **Role** from their details under **People**. **Administrator** sets Hubzoid organization administration and the chat app's admin role together. Accounts created with **Add user** are never pending.
- To offboard someone, an organization administrator opens them under **People** and selects **Delete user** from the **…** menu, typing their email to confirm. It removes every grant, then deletes the chat account and its chats. Activity history, usage records and published artifacts are kept, and the person's API keys stop working with the account. The last administrator cannot be deleted.

The Console no longer blocks or reactivates people. Someone blocked in an earlier release stays blocked until an organization administrator unblocks them through the management API.

Hubzoid binds each email to the verified account ID on sign-in, API-key use and migration. If a different account later appears with the same email, Hubzoid removes that email's direct grants, blocks its agent access and records the event, so a reused email never inherits someone else's access. See [people and activity](https://hubzoid.com/docs/console/people-and-activity).

## Sharing sign-in across hubs

| Option | Setup | Result |
| --- | --- | --- |
| Separate hubs, separate sign-in | Each hub's `.env` has its own sign-in block | Strong isolation. Each hub has its own user list and URL. |
| Separate hubs, shared SSO | The same provider client in every hub's `.env`, with each hub's redirect URI registered | One sign-in button everywhere. Adding someone to one hub still does not add them to the others. |
| Gateway | One sign-in block in the gateway's environment | One user list and one sign-in for every hub. Visibility per agent comes from grants and model access. |

## Settings Hubzoid applies to the chat app

Hubzoid starts the chat app with these values unless you set them yourself:

| Setting | Default | Effect |
| --- | --- | --- |
| `ENABLE_PERSISTENT_CONFIG`, `ENABLE_OAUTH_PERSISTENT_CONFIG` | `False` | Edited environment values always win at the next restart |
| `ENABLE_SIGNUP`, `ENABLE_OAUTH_SIGNUP` | `False` | No public registration by email or through a provider |
| `ENABLE_ADMIN_CHAT_ACCESS`, `ENABLE_ADMIN_EXPORT` | `False` | Administrators cannot open or export other people's chats. Set either to `true` to allow it. |

If you turn persistent configuration on, a value saved in the chat app's admin settings can win over these defaults, so also turn off **Enable New Sign Ups** there.

## Common problems

| Symptom | Fix |
| --- | --- |
| The provider says the redirect URI does not match | Register the exact path from the table above, with no trailing slash, on the host in `WEBUI_URL`. |
| The provider redirects to `localhost` | `WEBUI_URL` is unset or wrong. Hubzoid refuses to start with OAuth set and no `WEBUI_URL`. |
| New users sign in but see no chat | They are awaiting approval. An organization administrator approves them under **People** in the Console. |
| **Add user** says account management isn't set up | Set `HUBZOID_GATEWAY_ADMIN_EMAIL` and `HUBZOID_GATEWAY_ADMIN_PASSWORD` for a chat app administrator, then restart. |
| **Google sign-in only** is disabled in **Add user** | Set `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` and `OAUTH_MERGE_ACCOUNTS_BY_EMAIL=true` in the environment, keep `ENABLE_OAUTH_PERSISTENT_CONFIG` off, and restart. |
| Someone else became the chat app administrator first | Stop the hub, delete `<hub>/.openwebui-data/webui.db`, set `WEBUI_ADMIN_EMAIL` and `WEBUI_ADMIN_PASSWORD`, and start again. This removes every account and chat in that hub. |
| A signed-in administrator cannot open the Console | They need Hubzoid's `manage_access` permission. An organization administrator sets their **Role** to **Administrator** under **People**, or run `hubzoid grant <email> manage_access --org <hub>` on the server. |

## Next steps

- [Access administration](https://hubzoid.com/docs/deploy/access-administration): Administrators, grants and moving hubs to managed access.
- [Identity and access](https://hubzoid.com/docs/concepts/identity-and-access): How identity flows from sign-in to every tool call.
- [Security model](https://hubzoid.com/docs/deploy/security-model): Sign-in in the context of the whole deployment.
