Authentication
Configure sign-in, sessions, OAuth, OIDC/SSO, and device flow.
ObjectOS uses the ObjectStack authentication plugin, powered by Better Auth. Authentication is project-local: each project has its own identity tables and session scope.
What is supported
Depending on the packaged application and enabled settings, ObjectOS can support:
- email/password sign-in;
- session management;
- password reset and email verification;
- social OAuth providers such as Google, GitHub, Microsoft, and Apple;
- enterprise OIDC/SSO such as Okta, Entra ID, Keycloak, and Ping;
- two-factor authentication;
- passkeys/WebAuthn;
- magic links;
- phone number sign-in with SMS OTP (opt-in);
- CLI/browser device flow.
Who may sign up (ObjectStack 17.3+)
Self-registration is closed by default. Who may create their own account is one setting, Setup → Authentication → Audience:
| Audience | Who may sign up |
|---|---|
invite_only (default) | Only an address holding a pending invitation |
email_domain | Addresses on the domains you allow — exact match, case-insensitive; subdomains need their own entry |
open | Anyone |
The audience judges every self-serve path: email/password sign-up, a
first sign-in through a social provider, magic link, OTP and phone. A
refused sign-up answers 403 SELF_REGISTRATION_CLOSED (or
403 EMAIL_DOMAIN_NOT_ALLOWED under email_domain). Two rules come with
opening it:
email_domainandopenboth need a self-registration permission set — the set each self-registrant receives (member_defaultis fine;admin_full_accessis refused).- Both turn email verification on:
email_domainalways,openunless the deployment's own configuration turns it off.
The Allow self-service registration toggle under Email and password
does not override the audience: with the audience at invite_only,
sign-up stays closed even while that toggle is on.
Creating people yourself is never gated by the audience — invitations, Create User, bulk import and enterprise SSO work under every setting (see Users). The very first account on a fresh install is exempt too.
Upgrading from 17.2 or earlier: an undeclared audience used to mean open email/password sign-up. It now means
invite_only, and nothing fails at upgrade — a deployment that relied on open sign-up simply stops accepting it. Chooseopen(with a self-registration permission set) to keep it.
The audience is a different decision from the membership policy
(Setup → Authentication → Membership, or OS_AUTH_MEMBERSHIP_POLICY
— see
Environment Variables).
The audience decides whether someone may become a user at all; the
membership policy decides which organization a new user joins.
Phone sign-in & SMS (ObjectStack 14.3+)
Enable phone-number authentication via the auth.plugins.phoneNumber
setting. Once on:
POST /sign-in/phone-numberaccepts phone + password;- SMS OTP covers sign-in verification and self-service password reset, with per-number cooldowns, rolling-hour caps, and per-IP rate limits;
sys_usergains uniquephone_numberandphone_number_verifiedcolumns; phone-only accounts get a placeholder email.
SMS delivery is provided by @objectstack/plugin-sms with Aliyun SMS,
Twilio, or a dev log fallback, configured through the sms settings
namespace. Message bodies containing OTP codes are never persisted or
logged. Notifications can also target the sms channel via
notify(channels: ['sms']).
Admin user management (ObjectStack 14.3+)
Platform admins can provision accounts without the email invite flow:
POST /api/v1/auth/admin/create-usercreates a user directly, with an optional one-time password (returned once, never persisted). Themust_change_passwordflag forces rotation at first sign-in (403PASSWORD_EXPIREDuntil changed).POST /api/v1/auth/admin/import-usersbulk-imports rows / CSV / XLSX (max 500 rows per request) with dry-run and upsert-by-email-or-phone modes; existing users never get their passwords reset. Since 16.0 the defaultpasswordPolicyis'auto': rows with a deliverable channel (real email + wired email service, or phone + SMS invite path) are invited, and only unreachable rows get a temporary password (returned once,must_change_password). Callers that relied on the old identity-only default must now passpasswordPolicy: 'none'explicitly; per-row outcomes are onrows[].deliverywith asummary.deliverybreakdown.
Required secret
Set:
OS_AUTH_SECRET=replace-with-a-strong-random-secretObjectOS derives a stable per-project secret from this value and the project environment id. This means:
- sessions survive container restarts;
- tokens from one project cannot be reused on another project;
- rotating
OS_AUTH_SECRETinvalidates sessions.
See Environment variables for the
full list of auth-related settings, including the legacy AUTH_SECRET
alias.
Session isolation
In multi-project deployments, cookies are scoped to the project hostname. ObjectOS intentionally avoids broad root-domain cookies for project sessions. This prevents a session from leaking across customer projects.
Social login
Configure provider credentials through environment variables or system settings, depending on how the application package exposes auth configuration.
Provider callback URLs depend on the provider type. ObjectStack exposes two distinct callback paths and they are not interchangeable:
| Provider type | Callback path |
|---|---|
| Built-in social (Google, GitHub, Microsoft, Apple, …) | /api/v1/auth/callback/<provider> |
| Generic OIDC / OAuth2 (Okta, Entra ID, Keycloak, Ping, …) | /api/v1/auth/oauth2/callback/<provider> |
Examples:
https://crm.example.com/api/v1/auth/callback/google
https://crm.example.com/api/v1/auth/callback/microsoft
https://crm.example.com/api/v1/auth/oauth2/callback/okta
https://crm.example.com/api/v1/auth/oauth2/callback/entraConfigure the matching redirect URI in the identity provider's application registration before enabling the provider in ObjectOS.
Enterprise OIDC/SSO
OIDC providers are registered as generic OAuth2 providers and use the
/api/v1/auth/oauth2/callback/<provider> path. A typical configuration
requires:
| Field | Description |
|---|---|
| Provider id | Stable id such as okta or entra (used in the callback URL) |
| Display name | Button label shown to users |
| Discovery URL | .well-known/openid-configuration endpoint |
| Client id | Application client id from the identity provider |
| Client secret | Secret stored in environment or encrypted settings |
| Scopes | Usually openid email profile |
For customer deployments, prefer OIDC discovery URLs over manually configured authorization/token/userinfo endpoints.
Platform SSO
In cloud-connected deployments, ObjectOS can use the control-plane login as a platform SSO provider. A builder who is already signed in to the control plane can be provisioned into a project runtime without creating a separate project-local account.
This requires the control plane and ObjectOS to share the same
OS_AUTH_SECRET base secret. Disable platform SSO only when the customer
wants every project to own a completely separate login boundary.
Operational checks
Before production:
- confirm expired tokens return
401; - confirm the sign-up audience matches who should be able to create an account;
- confirm logout revokes the active session;
- confirm password reset revokes other sessions if required by policy;
- confirm callback URLs match the public project domain;
- confirm trusted origins include only approved domains;
- confirm
OS_AUTH_SECRETis stored in a secret manager, not in source.
Next steps
Authentication establishes who a user is. To control what they can access once signed in, see Permissions. For non-browser clients and machine-to-machine access, see API access.