Authentication
Fabric supports native email/password authentication with social login, API keys for service-to-service communication, and JWT validation. The first matching mechanism in the authentication chain is used.
Native Auth (Email/Password)
Section titled “Native Auth (Email/Password)”Native auth is the recommended mode for consumer-facing applications. It handles signup, login, email verification, password reset, and brute force protection directly in Fabric.
Enable it by setting OAUTH_SIGNING_SECRET in your environment.
Signup
Section titled “Signup”const auth = await fabric.auth.signup("alice@example.com", "SecurePass1!");// auth.access_token, auth.refresh_token, auth.user.idauth = fabric.signup("alice@example.com", "SecurePass1!")# Tokens auto-stored on client — subsequent calls are authenticatedcurl -X POST https://gofabric.dev/v1/auth/signup \ -H 'content-type: application/json' \ -d '{"email":"alice@example.com","password":"SecurePass1!"}'Password requirements: 8+ characters, uppercase, lowercase, digit, special character.
New users get a personal organization (Owner role) automatically.
const auth = await fabric.auth.login("alice@example.com", "SecurePass1!");auth = fabric.login("alice@example.com", "SecurePass1!")curl -X POST https://gofabric.dev/v1/auth/login \ -H 'content-type: application/json' \ -d '{"email":"alice@example.com","password":"SecurePass1!"}'Brute force protection: After 5 failed attempts, the account is locked for 15 minutes. Returns 429 with Retry-After header.
Token Refresh
Section titled “Token Refresh”Access tokens expire after 2 hours. Use the refresh token to get a new pair:
const auth = await fabric.auth.refresh(refreshToken);auth = fabric.refresh_token(refresh_token)curl -X POST https://gofabric.dev/v1/auth/token/refresh \ -H 'content-type: application/json' \ -d '{"refresh_token":"eyJ..."}'Refresh tokens are single-use — each refresh issues a new pair and blacklists the old refresh token.
Change Password
Section titled “Change Password”await fabric.auth.changePassword("OldPass1!", "NewPass1!");fabric.change_password("OldPass1!", "NewPass1!")curl -X POST https://gofabric.dev/v1/auth/change-password \ -H 'Authorization: Bearer eyJ...' \ -H 'content-type: application/json' \ -d '{"current_password":"OldPass1!","new_password":"NewPass1!"}'Changing your password invalidates all existing sessions (logout everywhere).
Logout All Sessions
Section titled “Logout All Sessions”await fabric.auth.logoutAll();fabric.logout_all()curl -X POST https://gofabric.dev/v1/auth/logout-all \ -H 'Authorization: Bearer eyJ...'Social Login (Consumer-Driven)
Section titled “Social Login (Consumer-Driven)”The recommended approach for consumer apps: your app handles the OAuth flow (Google Sign-In, NextAuth, etc.) using your own OAuth credentials, then sends the verified profile to Fabric.
// 1. Your app handles Google Sign-In with YOUR OAuth appconst googleUser = await googleSignIn();
// 2. Send the verified profile to Fabricconst auth = await fabric.auth.socialLogin({ provider: "google", provider_user_id: googleUser.sub, email: googleUser.email, display_name: googleUser.name, avatar_url: googleUser.picture,});// auth.access_token, auth.user.id# 1. Your app handles Google Sign-Ingoogle_user = google_sign_in()
# 2. Send the verified profile to Fabricauth = fabric.social_login( provider="google", provider_user_id=google_user["sub"], email=google_user["email"], display_name=google_user["name"],)# Tokens auto-stored — subsequent calls authenticatedcurl -X POST https://gofabric.dev/v1/auth/social-login \ -H 'content-type: application/json' \ -d '{ "provider": "google", "provider_user_id": "118234567890", "email": "alice@gmail.com", "display_name": "Alice" }'Account linking logic:
- If this
(provider, provider_user_id)is already linked → returns the existing user - If the email matches an existing user → links the social connection and returns the user
- Otherwise → creates a new user with a personal organization
Why consumer-driven? Each consumer app registers their own Google/GitHub/Apple OAuth app with their own branding and redirect URLs. Fabric doesn’t need any social provider secrets — it just stores the user and social connection link.
Managing Social Connections
Section titled “Managing Social Connections”// List connected providersconst connections = await fabric.me.socialConnections();
// Disconnect a provider (fails if it's the last auth method)await fabric.me.disconnectSocial("google");# List connected providersconnections = fabric.get_my_social_connections()
# Disconnect a providerfabric.disconnect_social("google")# Listcurl -H 'Authorization: Bearer eyJ...' https://gofabric.dev/v1/me/social-connections
# Disconnectcurl -X DELETE -H 'Authorization: Bearer eyJ...' \ https://gofabric.dev/v1/me/social-connections/googleLockout prevention: You cannot disconnect your last auth method. If you only have a Google connection and no password, disconnecting Google is rejected with 409. Set a password first or connect another provider.
Authentication Chain
Section titled “Authentication Chain”Fabric resolves authentication in priority order:
1. API Key (Authorization: Bearer fab_*)
Section titled “1. API Key (Authorization: Bearer fab_*)”API keys are the primary mechanism for service-to-service communication.
const fabric = new FabricClient({ apiKey: "fab_your_api_key_here" });fabric = FabricClient(api_key="fab_your_api_key_here")curl -H 'Authorization: Bearer fab_your_api_key_here' \ https://gofabric.dev/v1/meKeys use the fab_ prefix, are hashed at rest, and can be org-scoped with configurable expiry and permission scopes.
2. JWT (Authorization: Bearer <jwt>)
Section titled “2. JWT (Authorization: Bearer <jwt>)”JWT tokens issued by Fabric native auth are validated automatically.
3. Dev Header (X-Principal-Id: <uuid>)
Section titled “3. Dev Header (X-Principal-Id: <uuid>)”For development only. Set FABRIC_ENV=production to disable.
curl -H 'X-Principal-Id: 550e8400-e29b-41d4-a716-446655440000' \ https://gofabric.dev/v1/me4. Anonymous
Section titled “4. Anonymous”Requests without credentials are anonymous. Only public endpoints (/healthz, /readyz, /openapi.json, auth endpoints) are accessible.
Admin User Management
Section titled “Admin User Management”Admin endpoints for managing users programmatically:
// List usersconst { items } = await fabric.admin.listUsers({ limit: 20 });
// Unlock a locked accountawait fabric.admin.unlockUser(userId);
// Force-verify emailawait fabric.admin.verifyUser(userId);
// Force logout all sessionsawait fabric.admin.forceLogoutUser(userId);
// Delete userawait fabric.admin.deleteUser(userId);# List usersusers = fabric.admin_list_users(limit=20)
# Unlock a locked accountfabric.admin_unlock_user(user_id)
# Force-verify emailfabric.admin_verify_user(user_id)
# Force logout all sessionsfabric.admin_force_logout_user(user_id)
# Delete userfabric.admin_delete_user(user_id)# List userscurl -H 'Authorization: Bearer fab_xxx' https://gofabric.dev/v1/admin/users
# Unlockcurl -X POST -H 'Authorization: Bearer fab_xxx' \ https://gofabric.dev/v1/admin/users/<id>/unlock
# Verify emailcurl -X POST -H 'Authorization: Bearer fab_xxx' \ https://gofabric.dev/v1/admin/users/<id>/verify
# Deletecurl -X DELETE -H 'Authorization: Bearer fab_xxx' \ https://gofabric.dev/v1/admin/users/<id>CLI User Management
Section titled “CLI User Management”# Create a user (--skip-verification for dev)fabric user create --email alice@dev.local --password 'Test1234!' --skip-verification
# List usersfabric user list
# Unlock a locked accountfabric user unlock --email alice@dev.local
# Verify email manuallyfabric user verify --email alice@dev.local
# Reset password (admin — no old password needed)fabric user reset-password --email alice@dev.local --new-password 'NewPass1!'
# Delete userfabric user delete --email alice@dev.localAPI Keys
Section titled “API Keys”Creating API Keys
Section titled “Creating API Keys”const key = await fabric.createApiKey({ name: "my-service-key", organizationId: "<org-id>", scopes: ["jobs:write", "providers:execute"],});console.log("Store this key:", key.raw_key); // shown only oncekey = fabric.create_api_key( name="my-service-key", organization_id="<org-id>", scopes=["jobs:write", "providers:execute"],)print("Store this key:", key["raw_key"]) # shown only oncecurl -X POST https://gofabric.dev/v1/api-keys \ -H 'Authorization: Bearer fab_xxx' \ -H 'content-type: application/json' \ -d '{"name":"my-service-key","organization_id":"<org-id>","scopes":["jobs:write"]}'Scopes
Section titled “Scopes”| Scope | Description |
|---|---|
jobs:write | Create and manage jobs |
jobs:read | Read job status and history |
providers:execute | Execute provider requests |
workflows:write | Create and manage workflows |
workflows:read | Read workflow definitions and runs |
| (empty list) | Full access (no restrictions) |
Authorization (RBAC)
Section titled “Authorization (RBAC)”| Role | Level | Capabilities |
|---|---|---|
| Owner | Highest | Full access, can manage other owners |
| Admin | High | Manage teams, invitations, most resources |
| Member | Standard | Create and manage own resources |
| Viewer | Lowest | Read-only access |
Roles are scoped to organizations and optionally to teams. Clients must not compute permissions themselves — Fabric is the enforcement authority.
Auth Webhook Events (Consumer Email)
Section titled “Auth Webhook Events (Consumer Email)”Fabric emits webhook events for all auth actions. Consumer apps can subscribe to these events to send their own branded emails instead of using Fabric’s built-in Resend templates.
| Event | Payload | Use case |
|---|---|---|
auth.email_verification_required | principal_id, email, verify_token, verify_url | Send your own verification email |
auth.password_reset_requested | principal_id, email, reset_token, reset_url | Send your own password reset email |
auth.social_login | principal_id, email, provider, provider_user_id | Welcome email for social signups |
auth.password_changed | principal_id | Notify user of password change |
auth.account_locked | email | Alert user of lockout (5 failed attempts) |
auth.signup | principal_id, email | Welcome email for new signups |
Example: Custom Verification Email
Section titled “Example: Custom Verification Email”Subscribe to auth.email_verification_required via a webhook, then send your own email with the verify_token:
// Your webhook handler receives:{ "event": "auth.email_verification_required", "payload": { "principal_id": "550e8400-...", "email": "alice@example.com", "verify_token": "a1b2c3d4...", "verify_url": "https://fabric.example.com/v1/auth/verify?token=a1b2c3d4...&type=signup" }}
// Build your own verify URL pointing to YOUR frontend:const myVerifyUrl = `https://app.socialite.com/verify?token=${payload.verify_token}`;
// Your frontend calls Fabric's verify endpoint:// GET https://fabric.example.com/v1/auth/verify?token=a1b2c3d4...&type=signupDual delivery: If RESEND_API_KEY is set, Fabric also sends its own verification/reset emails as a fallback. To use only your own emails, leave RESEND_API_KEY unset and handle the webhook events instead.
Configuration
Section titled “Configuration”| Variable | Required | Description |
|---|---|---|
OAUTH_SIGNING_SECRET | For native auth | Secret for signing JWT tokens. Generate with openssl rand -hex 32 |
FABRIC_ENV | No | Set to production to disable dev headers |
RESEND_API_KEY | No | Built-in email for verification/reset (optional if using webhooks) |
RESEND_FROM_EMAIL | No | Sender email for built-in auth emails |
PUBLIC_BASE_URL | No | Base URL for verification/reset links in built-in emails |
AUTH_SITE_URL | No | Frontend redirect after email verification |
AUTH_CALLBACK_REDIRECT_URL | No | Frontend redirect after social login |