Skip to content

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 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.

const auth = await fabric.auth.signup("alice@example.com", "SecurePass1!");
// auth.access_token, auth.refresh_token, auth.user.id

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!");

Brute force protection: After 5 failed attempts, the account is locked for 15 minutes. Returns 429 with Retry-After header.

Access tokens expire after 2 hours. Use the refresh token to get a new pair:

const auth = await fabric.auth.refresh(refreshToken);

Refresh tokens are single-use — each refresh issues a new pair and blacklists the old refresh token.

await fabric.auth.changePassword("OldPass1!", "NewPass1!");

Changing your password invalidates all existing sessions (logout everywhere).

await fabric.auth.logoutAll();

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 app
const googleUser = await googleSignIn();
// 2. Send the verified profile to Fabric
const 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

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.

// List connected providers
const connections = await fabric.me.socialConnections();
// Disconnect a provider (fails if it's the last auth method)
await fabric.me.disconnectSocial("google");

Lockout 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.

Fabric resolves authentication in priority order:

API keys are the primary mechanism for service-to-service communication.

const fabric = new FabricClient({ apiKey: "fab_your_api_key_here" });

Keys use the fab_ prefix, are hashed at rest, and can be org-scoped with configurable expiry and permission scopes.

JWT tokens issued by Fabric native auth are validated automatically.

For development only. Set FABRIC_ENV=production to disable.

Terminal window
curl -H 'X-Principal-Id: 550e8400-e29b-41d4-a716-446655440000' \
https://gofabric.dev/v1/me

Requests without credentials are anonymous. Only public endpoints (/healthz, /readyz, /openapi.json, auth endpoints) are accessible.

Admin endpoints for managing users programmatically:

// List users
const { items } = await fabric.admin.listUsers({ limit: 20 });
// Unlock a locked account
await fabric.admin.unlockUser(userId);
// Force-verify email
await fabric.admin.verifyUser(userId);
// Force logout all sessions
await fabric.admin.forceLogoutUser(userId);
// Delete user
await fabric.admin.deleteUser(userId);
Terminal window
# Create a user (--skip-verification for dev)
fabric user create --email alice@dev.local --password 'Test1234!' --skip-verification
# List users
fabric user list
# Unlock a locked account
fabric user unlock --email alice@dev.local
# Verify email manually
fabric 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 user
fabric user delete --email alice@dev.local
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 once
ScopeDescription
jobs:writeCreate and manage jobs
jobs:readRead job status and history
providers:executeExecute provider requests
workflows:writeCreate and manage workflows
workflows:readRead workflow definitions and runs
(empty list)Full access (no restrictions)
RoleLevelCapabilities
OwnerHighestFull access, can manage other owners
AdminHighManage teams, invitations, most resources
MemberStandardCreate and manage own resources
ViewerLowestRead-only access

Roles are scoped to organizations and optionally to teams. Clients must not compute permissions themselves — Fabric is the enforcement authority.

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.

EventPayloadUse case
auth.email_verification_requiredprincipal_id, email, verify_token, verify_urlSend your own verification email
auth.password_reset_requestedprincipal_id, email, reset_token, reset_urlSend your own password reset email
auth.social_loginprincipal_id, email, provider, provider_user_idWelcome email for social signups
auth.password_changedprincipal_idNotify user of password change
auth.account_lockedemailAlert user of lockout (5 failed attempts)
auth.signupprincipal_id, emailWelcome email for new signups

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=signup

Dual 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.

VariableRequiredDescription
OAUTH_SIGNING_SECRETFor native authSecret for signing JWT tokens. Generate with openssl rand -hex 32
FABRIC_ENVNoSet to production to disable dev headers
RESEND_API_KEYNoBuilt-in email for verification/reset (optional if using webhooks)
RESEND_FROM_EMAILNoSender email for built-in auth emails
PUBLIC_BASE_URLNoBase URL for verification/reset links in built-in emails
AUTH_SITE_URLNoFrontend redirect after email verification
AUTH_CALLBACK_REDIRECT_URLNoFrontend redirect after social login