Authentication

Required configuration

BETTER_AUTH_SECRET=<openssl rand -hex 32>
BETTER_AUTH_URL=https://sim.yourdomain.com
NEXT_PUBLIC_APP_URL=https://sim.yourdomain.com

BETTER_AUTH_URL and NEXT_PUBLIC_APP_URL must be your exact public origin — correct scheme, no trailing slash. Leaving either as localhost in a deployed instance breaks sign-in, and the failure looks like a redirect loop rather than a configuration error.

BETTER_AUTH_SECRET must be identical on the app and realtime services. They share sessions through the database; a mismatch means realtime rejects every authenticated socket connection.

If users reach Sim from more than one origin — an apex and www, or an alias domain — list the extras:

TRUSTED_ORIGINS=https://www.example.com,https://app.example.com

Email and password

Enabled by default. Users sign up with an email address and password.

EMAIL_VERIFICATION_ENABLED=true

Requires a configured email provider — see Email. Without one the mailer no-ops silently, so users can never verify and never sign in. Do not enable this before email works.

Social login

Three providers are supported for signing in to Sim itself.

ProviderVariablesCallback URL
GoogleGOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECREThttps://<host>/api/auth/callback/google
GitHubGITHUB_CLIENT_ID / GITHUB_CLIENT_SECREThttps://<host>/api/auth/callback/github
MicrosoftMICROSOFT_CLIENT_ID / MICROSOFT_CLIENT_SECREThttps://<host>/api/auth/callback/microsoft

A provider appears on the login page once its credentials are set. Microsoft additionally requires both variables to be present before it is registered at all.

Turn one off without removing its credentials — useful when the same Google or Microsoft app powers integrations but you do not want it as a login method:

DISABLE_GOOGLE_AUTH=true
DISABLE_GITHUB_AUTH=true
DISABLE_MICROSOFT_AUTH=true

GOOGLE_CLIENT_ID and MICROSOFT_CLIENT_ID are shared with the integration connectors. One app registration can serve both login and integrations — register that provider's own login callback — /api/auth/callback/google for Google, /api/auth/callback/microsoft for Microsoft — alongside the connector callbacks (/api/auth/oauth2/callback/google-email and friends) on the same OAuth client. See Integrations & OAuth.

SSO (SAML and OIDC)

SAML and OIDC single sign-on is an enterprise feature, available on self-hosted deployments through configuration rather than billing:

ENTERPRISE_ENABLED=true
NEXT_PUBLIC_ENTERPRISE_ENABLED=true

Or enable just SSO:

SSO_ENABLED=true
NEXT_PUBLIC_SSO_ENABLED=true

Providers are then registered in the app under Settings → Organization → SSO. A provider can be scoped to an organization or registered without one. Most other enterprise features do read their settings from the organization that owns a workspace, so a deployment using them still needs an organization model — set INSTANCE_ORG_NAME to place every user in one shared organization, or provision organizations through the Admin API.

See the SSO guide for identity-provider setup and the self-hosted enterprise guide for the organization patterns.

Sign in with Sim

Your deployment acts as an OAuth 2.0 authorization server using authorization code with PKCE. OAuth sign-in is available whenever authentication is enabled; see CLI authentication.

Apply database migrations before deploying a new app version. When upgrading from a version without the OAuth token-family lifecycle, drain older app instances before accepting OAuth traffic so every instance enforces the same refresh and revocation rules.

DISABLE_AUTH=true disables OAuth sign-in and discovery because authorization requires a real Better Auth user session. Older servers without OAuth support return 404 from /.well-known/oauth-authorization-server, and the CLI falls back to the pairing-code handoff.

Access tokens are opaque and last an hour; refresh tokens rotate on every use. Each login has a fixed thirty-day lifetime that refreshing does not extend. Token validation checks current grants, so revoking a grant under Settings → General → Authorized apps stops the app on its very next request. Scheduled OAuth token cleanup runs independently of sign-in activity.

Organization admins can restrict OAuth App Access under Credentials & Access in permission groups. Workspace requests use the group governing that workspace. Authorizing apps, issuing and refreshing tokens, and account-level billing and audit reads use the organization's default group. CLI Access also applies to the Sim CLI. Members can still review and revoke existing grants when OAuth app access is restricted.

Registering an app

Dynamic client registration is switched off, so clients are created by an operator. Both db:migrate and the development db:push command install the OAuth lifecycle triggers and register the Sim CLI. Repeating either command preserves existing grants and client customizations. Register other apps with:

DATABASE_URL= \
BETTER_AUTH_SECRET=… \
OAUTH_CLIENT_ID=my-app \
OAUTH_CLIENT_NAME="My App" \
OAUTH_REDIRECT_URIS=https://my-app.example/callback \
OAUTH_SCOPES=api:read \
bun run apps/sim/scripts/create-oauth-client.ts

Add OAUTH_CLIENT_PUBLIC=true for a native or CLI app that cannot keep a secret; it then authenticates with PKCE alone. The provider exposes offline_access, api:read, and api:write for API authorization; it does not expose OpenID Connect identity scopes or issue ID tokens. A confidential client's secret is printed once and cannot be read back. Confidential clients use client_secret_basic; the registration command prints the token authentication method alongside the client ID.

Redirect URIs must be https, or http on a loopback address, and are matched exactly — except a loopback URI, where any port matches, because a native app cannot know its port in advance.

Controlling who can sign up

VariableEffect
DISABLE_REGISTRATION=trueBlocks all new accounts — email/password, email OTP, and social sign-in. Only existing accounts can sign in, including to accept a workspace invitation. SSO is unaffected
DISABLE_EMAIL_SIGNUP=trueBlocks new email/password registrations; existing email login keeps working
EMAIL_PASSWORD_SIGNUP_ENABLED=falseDisables the whole email path — sign-in, signup, and email OTP — for existing users too. Use DISABLE_EMAIL_SIGNUP to block only new accounts
ALLOWED_LOGIN_DOMAINSComma-separated domain allowlist, e.g. acme.com,acme.co.uk. Gates email sign-in as well as signup
ALLOWED_LOGIN_EMAILSComma-separated address allowlist, applied the same way
BLOCKED_SIGNUP_DOMAINSComma-separated domain blocklist
BLOCKED_EMAILSComma-separated address blocklist. Bans the address from the platform outright — signup, sign-in, and workflow executions
SIGNUP_MX_VALIDATION_ENABLED=trueReject domains with no MX record or a denylisted mail backend
BLOCKED_EMAIL_MX_HOSTSMX-host substrings to block; used only with the above

ALLOWED_LOGIN_DOMAINS, ALLOWED_LOGIN_EMAILS, and SIGNUP_MX_VALIDATION_ENABLED gate the email/password path only — and SIGNUP_MX_VALIDATION_ENABLED narrower still, running on email signup alone, not on email sign-in or email OTP. A first-time sign-in through Google, GitHub, or Microsoft creates an account through the social provider and is not filtered by them. To restrict who may sign in through a social provider, disable the ones you have not vetted (DISABLE_GOOGLE_AUTH, DISABLE_GITHUB_AUTH, DISABLE_MICROSOFT_AUTH) or restrict membership at the identity provider and use SSO.

DISABLE_REGISTRATION and BLOCKED_SIGNUP_DOMAINS apply to every path, social included.

For a company deployment, the usual pairing is domain-restricted signup plus SSO:

ALLOWED_LOGIN_DOMAINS=acme.com
DISABLE_EMAIL_SIGNUP=true
SSO_ENABLED=true
NEXT_PUBLIC_SSO_ENABLED=true

Both SSO flags are needed: the server-side one grants access, and the NEXT_PUBLIC_ one makes the login page render the SSO entry point.

Behind a load balancer

Tell Better Auth which forwarding hops to trust when resolving the client IP:

AUTH_TRUSTED_PROXIES=10.0.0.0/24,192.0.2.10

Better Auth walks X-Forwarded-For right to left, skips these hops, and uses the first untrusted address as the client IP for session records and its own IP-based checks. Use your proxies' actual addresses — a broad private range that also covers client traffic defeats the purpose. See Security.

Disabling authentication entirely

DISABLE_AUTH=true

Bypasses authentication and creates an anonymous session for every request.

Everyone who can reach the instance becomes a fully privileged user — including anything that can reach it through an SSRF bug elsewhere on your network. Use this only for a single-user instance on a private network, never behind an internet-facing ingress.

Other controls

VariableEffect
DISABLE_INVITATIONS=true / NEXT_PUBLIC_DISABLE_INVITATIONS=trueDisable workspace invitations globally
DISABLE_PUBLIC_API=true / NEXT_PUBLIC_DISABLE_PUBLIC_API=trueDisable the public API globally
ADMIN_API_KEYEnables the Admin API for GitOps operations and organization provisioning

The NEXT_PUBLIC_ twin controls what the UI shows; the server-side variable enforces it. Set both.