Identity
Overview
Section titled “Overview”Identity is the foundation of Reactor.cloud. Every other capability—Data, Storage, Functions, Jobs, Sites, Gateway, and Connect—builds on the same user, organization, and permission model.
Identity provides email/password authentication, JWT access tokens with rotating refresh tokens, multi-tenant organizations, role-based permissions, and org-scoped API keys. The HTTP surface is GoTrue-shaped so existing client patterns from Supabase and similar platforms transfer directly.
When you send a request to any Reactor API, Identity answers three questions: who is this caller, which organization are they acting in, and what are they allowed to do?
Key features
Section titled “Key features”- Email and password auth — Sign up, sign in, password recovery, and email verification (when SMTP is configured).
- JWT access tokens — RS256-signed tokens with a one-hour TTL; verified cryptographically without a database roundtrip on the happy path.
- Rotating refresh tokens — Single-use refresh tokens with theft detection; reusing a burned token revokes the entire session.
- Multi-tenant organizations — Users belong to one or more orgs; switch context with the
X-Reactor-Orgheader (UUID or slug). - Role-based permissions — Built-in
owner,admin, andmemberroles; define custom roles per org with dotted permission strings likedata:todos:read. - API keys — Opaque service tokens for server-side integrations; revocable and auditable.
- JWKS endpoint — Standard OpenID discovery and key rotation for third-party JWT verification.
Quickstart
Section titled “Quickstart”This minimal flow creates a user, signs in, creates an organization, and reads the current profile.
# Sign upreactor auth signup user@example.com --password 'SecurePass123!'
# Sign in and export tokenexport REACTOR_TOKEN=$(reactor auth login user@example.com --password 'SecurePass123!' --json | jq -r .access_token)
# Create an organizationreactor auth orgs create acme --name "Acme Inc"
# Get current userreactor auth user getimport { createClient } from '@reactor/client';
const reactor = createClient({ url: 'https://api.reactor.cloud',});
// Sign upawait reactor.auth.signUp({ email: 'user@example.com', password: 'SecurePass123!',});
// Sign inconst { accessToken } = await reactor.auth.signInWithPassword({ email: 'user@example.com', password: 'SecurePass123!',});
// Create org and set as activeconst org = await reactor.auth.orgs.create({ slug: 'acme', name: 'Acme Inc' });reactor.auth.setOrg(org.id);
// Get current userconst user = await reactor.auth.getUser();console.log(user.email, user.orgs);# Sign upcurl -s -X POST "$REACTOR_URL/auth/v1/signup" \ -H "Content-Type: application/json" \ -d '{"email":"user@example.com","password":"SecurePass123!"}'
# Sign in (password grant)curl -s -X POST "$REACTOR_URL/auth/v1/token?grant_type=password" \ -H "Content-Type: application/json" \ -d '{"email":"user@example.com","password":"SecurePass123!"}'
export REACTOR_TOKEN="<access_token from response>"
# Create organizationcurl -s -X POST "$REACTOR_URL/auth/v1/orgs" \ -H "Authorization: Bearer $REACTOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"slug":"acme","name":"Acme Inc"}'
# Get current usercurl -s "$REACTOR_URL/auth/v1/user" \ -H "Authorization: Bearer $REACTOR_TOKEN"How-to guides
Section titled “How-to guides”Invite a teammate to your organization
Section titled “Invite a teammate to your organization”Organization owners and admins can invite members by email. When SMTP is configured, Reactor sends the invitation; otherwise you get a shareable signed link for local development.
reactor auth orgs invitations create acme \ --email teammate@example.com \ --role memberawait reactor.auth.orgs.invitations.create('acme', { email: 'teammate@example.com', role: 'member',});curl -s -X POST "$REACTOR_URL/auth/v1/orgs/acme/invitations" \ -H "Authorization: Bearer $REACTOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"email":"teammate@example.com","role":"member"}'Switch organization context
Section titled “Switch organization context”Pass the active org on every request that operates on org-scoped resources. Reactor accepts either the org UUID or its slug.
reactor auth orgs use acmereactor data query todos --limit 5reactor.auth.setOrg('acme'); // slug or UUID
const todos = await reactor.data.from('todos').select('*').limit(5);curl -s "$REACTOR_URL/data/v1/todos?limit=5" \ -H "Authorization: Bearer $REACTOR_TOKEN" \ -H "X-Reactor-Org: acme"Create a custom role with scoped permissions
Section titled “Create a custom role with scoped permissions”Define roles that grant exactly the access your app needs—no more, no less.
-- Permissions are assigned via the API; example permission strings:-- data:todos:read, data:todos:write, storage:avatars:read, functions:checkout:invokereactor auth orgs roles create acme editor \ --permissions 'data:posts:read,data:posts:write,storage:media:read'
reactor auth orgs members update-role acme user_01HZ... --role editorawait reactor.auth.orgs.roles.create('acme', { name: 'editor', permissions: ['data:posts:read', 'data:posts:write', 'storage:media:read'],});
await reactor.auth.orgs.members.updateRole('acme', userId, { role: 'editor' });curl -s -X POST "$REACTOR_URL/auth/v1/orgs/acme/roles" \ -H "Authorization: Bearer $REACTOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"editor","permissions":["data:posts:read","data:posts:write"]}'Configuration
Section titled “Configuration”Add an [identity] section to your project’s reactor.toml. Environment variables override these values at runtime.
[identity]# Required: 32-byte AES key for encrypting sensitive columns (base64-encoded)data_key = "base64-32-byte-key-here"
# JWT settingsjwt_issuer = "reactor-auth"jwt_audience = "reactor"access_ttl_secs = 3600 # 1 hourrefresh_ttl_secs = 2592000 # 30 days
# Public URL used in email links and invite URLspublic_url = "https://api.reactor.cloud"
# Optional SMTP for verification, recovery, and invite emails[identity.smtp]host = "smtp.resend.com"port = 587user = "resend"password = "re_..."from = "noreply@example.com"tls = "starttls"Limits and quotas
Section titled “Limits and quotas”| Limit | Value | Notes |
|---|---|---|
| Access token TTL | 1 hour | Configurable via access_ttl_secs |
| Refresh token TTL | 30 days | Sliding expiry on each rotation |
| Password hashing | Argon2id | 64 MiB memory, calibrated per deploy |
| JWT signing | RS256, 2048-bit RSA | Two active keys during rotation window |
| Org slug | URL-safe, unique per deploy | Accept UUID or slug in path params |
| Session revocation lag | Up to 1 hour | Access tokens valid until exp; logout revokes refresh tokens immediately |
| Rate limiting | Not in v0 | Delegate to reverse proxy or add in v0.2 |
Built-in role permissions:
| Role | Default permissions |
|---|---|
owner | * (full access) |
admin | data:*:*, storage:*:*, functions:*:*, jobs:*:*, auth:members:*, auth:roles:* |
member | data:*:read, storage:*:read, functions:*:invoke |
API and SDK links
Section titled “API and SDK links”- HTTP base path:
/auth/v1/ - OpenAPI reference: Identity API
- JavaScript SDK:
@reactor/clientauth module - Swift SDK:
ReactorAuth - CLI:
reactor auth
Key endpoints:
| Method | Path | Description |
|---|---|---|
POST | /auth/v1/signup | Create a new user |
POST | /auth/v1/token?grant_type=password | Sign in |
POST | /auth/v1/token?grant_type=refresh_token | Refresh access token |
GET | /auth/v1/user | Current user profile |
GET | /auth/v1/orgs | List user’s organizations |
POST | /auth/v1/orgs | Create organization |
GET | /auth/v1/permissions | Effective permissions for active org |
GET | /auth/v1/keys | JWKS for token verification |
Troubleshooting
Section titled “Troubleshooting”invalid_credentials on login
Section titled “invalid_credentials on login”The email or password does not match, or the account is disabled. Password recovery always returns 204 to prevent email enumeration—check your inbox (and spam) if SMTP is configured.
reactor auth recover user@example.comreactor auth doctorcurl -s -X POST "$REACTOR_URL/auth/v1/recover" \ -H "Content-Type: application/json" \ -d '{"email":"user@example.com"}'403 on Data or Storage despite valid token
Section titled “403 on Data or Storage despite valid token”Your JWT is valid but the active org lacks permission, or X-Reactor-Org points to an org you are not a member of.
- Confirm membership:
GET /auth/v1/orgs - Check effective permissions:
GET /auth/v1/permissionswithX-Reactor-Orgset - Ensure the role includes the required permission (e.g.
data:todos:write)
Refresh token reuse detected
Section titled “Refresh token reuse detected”If a refresh token is used twice, Reactor revokes the entire session as a theft-detection measure. The user must sign in again. This is expected behavior—do not store refresh tokens in multiple places.
Emails not sending
Section titled “Emails not sending”Invites and verification require SMTP. Without it, use the signed invite link returned by the API. Verify [identity.smtp] settings and run reactor auth doctor.