Authentication
Set up authentication for your NNO platform using Better Auth with per-platform auth Workers.
Set Up Authentication
Every NNO platform gets a dedicated auth Worker backed by its own D1 database. Users, sessions, and tenant memberships are fully isolated between platforms — no shared tables, no cross-platform tokens.
How it works
NNO uses Better Auth as the authentication framework. When you provision a platform, the Provisioning service automatically:
- Creates an auth Worker (
auth.svc.default.<platformId>.nno.app) - Provisions a D1 database (
<platformId>-default-auth-db) - Runs the Better Auth migrations to create user, session, organization, and permission tables
You do not configure this manually — it is part of the platform bootstrap flow.
Connect your console to the auth Worker
Set VITE_AUTH_URL in your console environment to your platform's auth Worker URL:
# apps/console/.env
VITE_AUTH_URL=https://auth.svc.default.<platformId>.nno.app
VITE_PLATFORM_ID=<platformId>Replace <platformId> with your 10-character platform ID (for example, k3m9p2xw7q). The console shell reads this URL to perform session checks and token exchanges.
For local development, the Vite dev server proxies auth requests to avoid cross-origin cookie issues:
# apps/console/.env (local)
VITE_AUTH_API_URL=/api/auth # proxied to http://localhost:8787See Managing Environments for the full environment file structure.
Session management and cookies
After sign-in, Better Auth sets a session cookie scoped to .<platformId>.nno.app. All apps on your platform share this cookie domain, so a user authenticated at console.app.default.<platformId>.nno.app is also authenticated at any other app on *.<platformId>.nno.app.
Locally, the cookie is scoped to localhost and works across all localhost ports without extra configuration.
Role model
NNO uses a two-tier role system on top of Better Auth:
| Scope | Roles | Description |
|---|---|---|
| Platform | platform-admin, user | Set on the user record via the Admin plugin |
| Tenant | owner, admin, member | Set on the org membership via the Organization plugin |
A user can hold different roles in different tenants. Platform admins can manage all tenants, view billing, and activate features. Tenant owners can invite and remove members within their tenant.
Check the active session in your feature components via the SDK:
import { useNnoSession } from "@neutrino-io/sdk";
function MyComponent() {
const { user } = useNnoSession();
const isAdmin = user.role === "platform-admin";
}Social auth (optional)
Better Auth supports OAuth providers (Google, GitHub, etc.) via the Social Login plugin. To enable a provider, add its credentials to your auth Worker's secrets:
wrangler secret put GOOGLE_CLIENT_ID
wrangler secret put GOOGLE_CLIENT_SECRETThen configure the provider in your auth Worker's createAuthApp() call. See the Better Auth social providers docs for the full list of supported providers.
Two-factor authentication
The Better Auth Two-Factor plugin is included in the auth Worker template. Users can enroll TOTP authenticators from their profile settings page. The NNO console shell renders the 2FA enrollment flow automatically via @neutrino-io/ui-auth — no additional setup required.
Verify the setup
After provisioning, confirm the auth Worker is responding:
curl https://auth.svc.default.<platformId>.nno.app/api/auth/session
# {"session": null} — expected when unauthenticatedIf you receive a connection error, check that the platform bootstrap job completed successfully in the NNO Portal under Platform > Provisioning.
Next steps
- Deploy a stack — provision backend resources for your platform
- DNS setup — configure custom domains and cookie domain
- API reference — IAM service endpoints