Platform

Authentication

Email + OTP, social login, passkey, password flows, and JWT sessions — with Row-Level Security integration.

OrbitNest Auth handles user sign-up, sign-in, email verification, and session management out of the box. It issues standard JWTs that are recognized by every module — including Row-Level Security policies in your database.

Supported flows

  • Email + password — the classic flow. Sign-up always requires an OTP before the account is created, so typo'd or throw-away emails never generate a row.
  • Email + OTP (passwordless) — a 4-digit code is emailed; verifying it issues a session. No password stored, no password-reset flow to worry about.
  • Passkey (WebAuthn / FIDO2) — hardware-backed credentials via Credential Manager on Android and ASAuthorization on iOS. Supported for both admin users and project users; device management and revocation live in the settings page.
  • Social login (OAuth) — admins can sign in or sign up with Google, GitHub, or Apple. A verified-email match auto-links the provider to an existing account, so an admin who registered with email + password can later sign in with Google with no manual linking step. Currently admin-side only.
  • Password reset — request → email OTP → verify → set new password, all in a single round-trip via the SDK.
  • Refresh-token rotation — every refresh invalidates the previous token; replays are detected and cause an immediate revocation of all sessions for the user (defence against stolen refresh tokens).

Social login for admins

Admins can sign in or sign up with Google, GitHub, or Apple. When the provider returns a verified email that matches an existing admin, the provider is auto-linked to that account — so an admin who registered with email + password can later sign in with Google without any manual linking step. SAML / enterprise SSO is not yet shipped.

Sign up with OTP

The recommended flow: collect email, send a 4-digit code, verify it, then optionally set a password.

typescript
// 1. Request OTP
await client.auth.signUpWithEmail({
  email: 'user@example.com',
  user_metadata: { name: 'Jane Doe' },
});

// User enters the 4-digit code from their email …

// 2. Verify and create the account
const { user, session } = await client.auth.verifySignup({
  email: 'user@example.com',
  code:  '1234',
  password: 'optional-password',
});

Debug mode

In debug mode (set per-project in Studio → Settings), the OTP is always 1234. Perfect for development without blasting emails.

Sessions & tokens

A successful sign-in returns an access_token (short-lived JWT) and a refresh_token. SDKs handle token refresh automatically; for manual calls, hit the refresh endpoint when a 401 comes back.

typescript
const { access_token, refresh_token } = await client.auth.refresh(refreshToken);

JWT claims

OrbitNest JWTs include the user's sub (user ID), email, role, and any custom user_metadata. The signing secret can be rotated from API Keys → JWT Settings.

json
{
  "sub":   "9f2a1c7e-...",
  "email": "user@example.com",
  "role":  "authenticated",
  "iat":   1704499200,
  "exp":   1704502800,
  "user_metadata": { "name": "Jane Doe" }
}

Multi-factor auth (TOTP)

Let users add an authenticator app (Google Authenticator, 1Password, …) as a second factor. Enroll while signed in, then confirm with the first code.

typescript
// Show data.qr_code (a data URL) or data.secret to the user
const { data } = await client.auth.enrollMfaTotp('My phone');
await client.auth.verifyMfaEnrollment(data.factor_id, '123456');

await client.auth.listMfaFactors();
await client.auth.unenrollMfa(factorId);

Once a verified factor exists, signIn returns an MFA challenge instead of a session — complete it with verifyMfa:

typescript
import { isMfaChallenge } from '@orbitneststudio/js';

const { data } = await client.auth.signIn({ email, password });
if (isMfaChallenge(data)) {
  // codeFromApp may be a 6-digit TOTP or a recovery code
  await client.auth.verifyMfa(data.challenge_token, codeFromApp);
}

Recovery codes. Confirming enrollment returns a one-time recovery_codes array — show them to the user to store safely. Any of them works in place of the authenticator code at sign-in, and you can replace the set at any time with regenerateMfaRecoveryCodes().

typescript
const { data } = await client.auth.verifyMfaEnrollment(factorId, '123456');
data.recovery_codes;  // ['abcd-efgh', …] — shown once; tell the user to save them

// later — replace the whole set (also returned once)
const { data: fresh } = await client.auth.regenerateMfaRecoveryCodes();

SMS OTP (phone sign-in)

Connect your own Twilio account in Settings → SMS / Twilio, then users can sign in with a code texted to their phone. The user is created on first sign-in.

typescript
await client.auth.signInWithSms('+15555550123');          // sends the code
await client.auth.verifySmsOtp('+15555550123', '123456'); // verifies + signs in

RLS integration

When a user is signed in, their JWT is forwarded to Postgres. Use auth.uid() inside RLS policies to reference the current user.

sql
CREATE POLICY "users read own rows" ON private_data
  FOR SELECT USING (auth.uid() = owner_id);

Admin users vs. project users

Two distinct user systems coexist:

  • Admin users — access the Studio dashboard. Managed from the Admins page.
  • Project users — end-users of apps built on top of OrbitNest. Managed per-project in the Authentication page.

Token isolation

Admin JWTs can never authenticate against a project's client API endpoints, and project-user JWTs can never access the admin dashboard. This is enforced at the API layer.

Protected auth tables

Tables under the auth. schema (users, sessions, OTP, passkeys) are read-only from the client. Studio exposes them in the Authentication view with masked sensitive columns.