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 — just register both sets of redirect URIs. 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 → Enterprise → Single Sign-On. 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.

Controlling who can sign up

VariableEffect
DISABLE_REGISTRATION=trueBlocks email/password registration
DISABLE_EMAIL_SIGNUP=trueBlocks new email/password registrations; existing email login keeps working
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
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

These controls gate the email/password path. A first-time sign-in through Google, GitHub, or Microsoft creates an account through the social provider and is not filtered by them. If you need a hard boundary, disable the social providers you have not vetted (DISABLE_GOOGLE_AUTH, DISABLE_GITHUB_AUTH, DISABLE_MICROSOFT_AUTH) or restrict membership at the identity provider and use SSO.

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.

Common Questions

BETTER_AUTH_URL or NEXT_PUBLIC_APP_URL does not match the origin users are browsing. Both must be the exact public URL including scheme, with no trailing slash. This is the single most common authentication misconfiguration.
BETTER_AUTH_SECRET differs between the app and realtime services. They share sessions through the database, so the secret must be byte-identical on both.
Yes. GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET power both. Register the login callback (/api/auth/callback/google) alongside the connector callbacks (/api/auth/oauth2/callback/google-email and friends) on the same OAuth client.
The server-side variable enforces the behavior; the NEXT_PUBLIC_ variable tells the browser UI what to render. Setting only one produces a UI that disagrees with the server, so set both.

On this page