Auth
Overview of server authentication and policy configuration.
This guide is for server operators. It explains the core auth model and the policy env vars that control signup/login enforcement.
For provider-specific setup guides, see:
Choosing an auth setup (private vs public servers)
What you should configure depends mostly on who can reach your server.
Private server (Tailscale/VPN/LAN only)
If your server is only reachable from a private network (for example via Tailscale), you often don’t need provider-based auth:
- Recommended: keep anonymous signup enabled (default) and rely on network access control.
- Make sure your server is not reachable from the public internet (firewall / security group / reverse proxy).
Example config:
AUTH_ANONYMOUS_SIGNUP_ENABLED=true
AUTH_SIGNUP_PROVIDERS=
AUTH_REQUIRED_LOGIN_PROVIDERS=Optional (private servers): you can also enable GitHub/OIDC in addition to anonymous signup, to offer multiple signup options:
AUTH_ANONYMOUS_SIGNUP_ENABLED=true
AUTH_SIGNUP_PROVIDERS=github
AUTH_REQUIRED_LOGIN_PROVIDERS=Public server (reachable from the internet)
If your server is publicly reachable, leaving anonymous signup enabled makes it easy for anyone to create an account. In that setup, it’s usually better to require an identity provider:
- Recommended: disable anonymous signup
- Recommended: require a provider for ongoing access (
AUTH_REQUIRED_LOGIN_PROVIDERS) - Optional: restrict eligibility with allowlists (for example “must be in GitHub org X”)
Example (GitHub):
AUTH_ANONYMOUS_SIGNUP_ENABLED=false
AUTH_SIGNUP_PROVIDERS=github
AUTH_REQUIRED_LOGIN_PROVIDERS=githubThen follow the provider setup guide:
- GitHub: GitHub auth
- OIDC: OIDC auth
Standard authentication (device-key accounts)
Happier uses a device-key account model (each account is tied to a device secret).
- Default behavior: users can create accounts via device-key signup (
POST /v1/auth). - You can optionally require external providers (GitHub and/or OIDC) for signup and/or ongoing access.
- External providers are not “password login”: if a user is trying to access an existing Happier account from a new device/browser, they still need to restore the account (for example via Add your phone / Restore account), then connect providers from settings.
Keyed vs keyless (how providers show up in the UI)
The same OAuth provider (for example GitHub or an OIDC provider) can appear in the UI in two different roles:
- Keyed provisioning (“Continue with GitHub”): creates a device-key account.
- Users must back up a secret key and restore on new devices/browsers.
- This is the standard Happier model and works on E2EE-only servers.
- Keyless login (“Sign in with GitHub”): signs in to a keyless/plain account (no device keys).
- Sign-in can be seamless across devices, but it is intended for enterprise deployments and usually paired with plaintext storage.
Server operators control which role(s) are enabled via /v1/features (policy + feature env vars). Clients should not guess.
Auth methods (discovery)
Clients should treat GET /v1/features as the source of truth for “what auth flows are available”.
Happier advertises auth entrypoints via:
capabilities.auth.methods[](preferred; method + action + mode)- plus legacy surfaces like
capabilities.auth.signup.methods[]for older clients
Each method can advertise multiple actions:
login(sign in to an existing account)provision(create an account)connect(link an identity provider to an already-authenticated account)
And each action declares its account key mode:
keyed: device-key account (default Happier model)keyless: no device keys (enterprise mode; typically paired with plaintext storage)
Keyless authentication (enterprise)
Some deployments want accounts to exist without device keys (for example: company-managed identity, server-side indexing/search, and simpler multi-device behavior).
Happier currently supports keyless login via:
- mTLS auth
- keyless OAuth login (GitHub/OIDC) when enabled by feature env vars
Keyless account auto-provisioning is intentionally guarded:
- enable keyless accounts:
HAPPIER_FEATURE_E2EE__KEYLESS_ACCOUNTS_ENABLED=1 - allow plaintext storage (required for keyless accounts):
HAPPIER_FEATURE_ENCRYPTION__STORAGE_POLICY=optional|plaintext_only - for OAuth keyless login:
- enable:
HAPPIER_FEATURE_AUTH_OAUTH__KEYLESS_ENABLED=1 - allowlist providers:
HAPPIER_FEATURE_AUTH_OAUTH__KEYLESS_PROVIDERS=github,okta,... - allow first-login account creation:
HAPPIER_FEATURE_AUTH_OAUTH__KEYLESS_AUTO_PROVISION=1
- enable:
- for mTLS keyless login: see mTLS auth
Recommended hardening for plaintext/keyless deployments:
- seal plaintext account settings at rest:
HAPPIER_FEATURE_ENCRYPTION__PLAIN_ACCOUNT_SETTINGS_AT_REST=server_sealed(default) - seal plaintext account connected-service credentials at rest:
HAPPIER_FEATURE_ENCRYPTION__PLAIN_ACCOUNT_CREDENTIALS_AT_REST=server_sealed(default)
Note: HAPPIER_FEATURE_ENCRYPTION__DEFAULT_ACCOUNT_MODE controls only the default mode for keyed accounts and new sessions when the storage policy is optional. It does not gate keyless auto-provisioning.
OAuth keyless provisioning choice (optional encryption servers)
When the server storage policy is optional, new users coming back from OAuth (GitHub/OIDC) may be prompted to choose:
- End-to-end encrypted (E2EE): creates a device-key account; users must back up a secret key to restore on new devices.
- Not encrypted (plaintext): creates a keyless/plain account; sign-in is seamless across devices, but the server can read stored content.
When the provider is enabled for both modes (keyed provisioning + keyless auto-provisioning), the client should show the choice. When only one mode is available, the client should auto-select it (no extra screen).
On plaintext_only servers, clients should automatically choose plaintext. On required_e2ee servers, clients should not offer plaintext/keyless choices.
Operator recipes (recommended)
E2EE-only (public server) + GitHub required
HAPPIER_FEATURE_ENCRYPTION__STORAGE_POLICY=required_e2ee
AUTH_ANONYMOUS_SIGNUP_ENABLED=false
AUTH_SIGNUP_PROVIDERS=github
AUTH_REQUIRED_LOGIN_PROVIDERS=githubKeyless-only (enterprise) + plaintext-only + GitHub required
HAPPIER_FEATURE_ENCRYPTION__STORAGE_POLICY=plaintext_only
HAPPIER_FEATURE_E2EE__KEYLESS_ACCOUNTS_ENABLED=1
AUTH_ANONYMOUS_SIGNUP_ENABLED=false
AUTH_SIGNUP_PROVIDERS=
AUTH_REQUIRED_LOGIN_PROVIDERS=github
HAPPIER_FEATURE_AUTH_OAUTH__KEYLESS_ENABLED=1
HAPPIER_FEATURE_AUTH_OAUTH__KEYLESS_PROVIDERS=github
HAPPIER_FEATURE_AUTH_OAUTH__KEYLESS_AUTO_PROVISION=1Optional encryption (enterprise) + OAuth provisioning choice (GitHub)
HAPPIER_FEATURE_ENCRYPTION__STORAGE_POLICY=optional
HAPPIER_FEATURE_E2EE__KEYLESS_ACCOUNTS_ENABLED=1
AUTH_ANONYMOUS_SIGNUP_ENABLED=false
AUTH_SIGNUP_PROVIDERS=github
AUTH_REQUIRED_LOGIN_PROVIDERS=github
HAPPIER_FEATURE_AUTH_OAUTH__KEYLESS_ENABLED=1
HAPPIER_FEATURE_AUTH_OAUTH__KEYLESS_PROVIDERS=github
HAPPIER_FEATURE_AUTH_OAUTH__KEYLESS_AUTO_PROVISION=1Optional: allow users to switch existing accounts (e2ee ↔ plain) when storage policy is optional:
HAPPIER_FEATURE_ENCRYPTION__ALLOW_ACCOUNT_OPTOUT=1Upgrade notes (operator guidance)
- Clients first is usually safe for keyed accounts (device-key model), because older servers still accept the core
/v1/authlogin flow. - Do not enable
plaintext_onlyuntil you have clients that support plaintext storage (older clients will fail because encrypted writes are rejected). - If you enable keyless OAuth auto-provisioning, ensure your clients are new enough to understand the keyless flows advertised by
/v1/features(otherwise users may see missing buttons or failed sign-in attempts).
OAuth return URL (required for GitHub/OIDC self-hosting)
If you enable any external OAuth providers (GitHub and/or OIDC), the server must know where to redirect the browser back to the client app after OAuth completes.
- If you use the hosted web app, you don’t need to set anything (default is
https://app.happier.dev). - If you self-host the web app (or you’re using a local dev UI), you must configure one of:
HAPPIER_WEBAPP_URL(most common for web)HAPPIER_WEBAPP_OAUTH_RETURN_URL_BASE(often used for mobile deep links or custom paths)
Otherwise, users will be redirected to the wrong place (or the redirect may be rejected as unsafe).
Auth policy (env vars)
Use these env vars to control who can create accounts and whether provider identity is enforced for ongoing access.
Signup / login policy
AUTH_ANONYMOUS_SIGNUP_ENABLED(defaulttrue)- If
false, anonymous account creation is disabled (POST /v1/authwill return403 signup-disabledfor new users).
- If
AUTH_SIGNUP_PROVIDERS(CSV, default empty)- Enables provider-based signup (e.g.
github,okta).
- Enables provider-based signup (e.g.
AUTH_REQUIRED_LOGIN_PROVIDERS(CSV, default empty)- Enforces providers for ongoing access (e.g.
github,okta).
- Enforces providers for ongoing access (e.g.
What enforcement applies to
When AUTH_REQUIRED_LOGIN_PROVIDERS is set, eligibility is enforced for:
- Authenticated HTTP routes (via the
authenticatepre-handler) - Socket.IO handshake (
/v1/updates)
If a required provider is missing:
- Requests are rejected with
403 provider-required.
Offboarding / eligibility checks
If you use allowlists (GitHub org allowlists or OIDC allow rules), the server can re-check eligibility periodically and cache results on the identity record.
AUTH_OFFBOARDING_ENABLED- Defaults to
truewhen any membership-based allowlists are configured (GitHub org allowlist and/or OIDC allow rules); otherwisefalse.
- Defaults to
AUTH_OFFBOARDING_INTERVAL_SECONDS(default86400, clamp 60–86400)AUTH_OFFBOARDING_STRICT(defaultfalse)- When
true, the server fails closed if it cannot re-check eligibility due to upstream downtime (enterprise lock-down mode).
- When
Common configurations (examples)
Default (anonymous signup)
AUTH_ANONYMOUS_SIGNUP_ENABLED=true
AUTH_SIGNUP_PROVIDERS=
AUTH_REQUIRED_LOGIN_PROVIDERS=GitHub-only signup + GitHub required for login
See GitHub auth for full setup steps.
OIDC-only signup (example: Okta) + OIDC required for login
See OIDC auth for provider configuration.