Tenant Authentication Setup
Configure authentication for tenant APIs with JWT, Firebase Auth, or Google OAuth.
Overview
Tenants can configure their own authentication:
| Method | Description | Use Case |
|---|---|---|
| JWT | Self-managed tokens | Custom auth systems |
| Firebase Auth | Google's auth service | Mobile/web apps |
| Google OAuth | Direct Google login | Enterprise SSO |
| Custom | Any OAuth2 provider | Third-party auth |
JWT Authentication
Tenant Configuration
json
{
"jwt": {
"secret": "{{secret:JWT_SECRET}}",
"expiresIn": "24h",
"algorithm": "HS256",
"issuer": "tenant-api"
}
}Store JWT Secret
bash
POST /tenant/secrets
Authorization: Bearer <admin-token>
x-tenant-id: my-tenant
{
"key": "JWT_SECRET",
"value": "your-256-bit-secret-key-here"
}Protected Routes
json
{
"path": "/users/profile",
"method": "get",
"requireAuth": true,
"response": {
"user": "{{auth}}"
}
}Generate Tokens
Your app generates tokens:
javascript
import jwt from 'jsonwebtoken';
const token = jwt.sign(
{
sub: 'user123',
email: 'user@example.com',
role: 'user'
},
process.env.JWT_SECRET,
{ expiresIn: '24h', algorithm: 'HS256' }
);API Calls
bash
curl -X GET https://api.example.com/users/profile \
-H "Authorization: Bearer <jwt-token>" \
-H "x-tenant-id: my-tenant"JWT Claims Access
json
{
"path": "/orders",
"method": "get",
"requireAuth": true,
"supabaseQueries": [{
"table": "orders",
"operation": "select",
"filters": [
{ "column": "user_id", "operator": "eq", "value": "{{auth.sub}}" }
]
}]
}Available claims:
{{auth.sub}}- Subject (user ID){{auth.email}}- User email{{auth.role}}- User role{{auth.tenant_id}}- Tenant ID (if in token){{auth.*}}- Any custom claim
Firebase Authentication
Configuration
json
{
"firebase": {
"projectId": "{{env.FIREBASE_PROJECT_ID}}",
"privateKey": "{{secret:FIREBASE_PRIVATE_KEY}}",
"clientEmail": "{{env.FIREBASE_CLIENT_EMAIL}}"
},
"auth": {
"provider": "firebase"
}
}Store Firebase Credentials
bash
# Store private key as secret
POST /tenant/secrets
Authorization: Bearer <admin-token>
x-tenant-id: my-tenant
{
"key": "FIREBASE_PRIVATE_KEY",
"value": "-----BEGIN PRIVATE KEY-----\n..."
}Protected Routes
json
{
"path": "/dashboard",
"method": "get",
"requireAuth": true,
"authProvider": "firebase"
}Client-Side (React)
javascript
import { getAuth, signInWithEmailAndPassword } from 'firebase/auth';
const auth = getAuth();
// Sign in
const { user } = await signInWithEmailAndPassword(auth, email, password);
// Get ID token
const token = await user.getIdToken();
// Call API
const response = await fetch('/dashboard', {
headers: {
'Authorization': `Bearer ${token}`,
'x-tenant-id': 'my-tenant'
}
});Firebase Claims Access
json
{
"path": "/user/data",
"method": "get",
"requireAuth": true,
"authProvider": "firebase",
"response": {
"userId": "{{auth.uid}}",
"email": "{{auth.email}}",
"name": "{{auth.name}}"
}
}Firebase claims:
{{auth.uid}}- Firebase user ID{{auth.email}}- User email{{auth.email_verified}}- Email verified{{auth.name}}- Display name{{auth.picture}}- Profile photo URL
Google OAuth (Direct)
Configuration
json
{
"oauth2": {
"google": {
"clientId": "{{env.GOOGLE_CLIENT_ID}}",
"clientSecret": "{{secret:GOOGLE_CLIENT_SECRET}}",
"callbackUrl": "https://api.example.com/auth/google/callback"
}
}
}OAuth Routes
json
{
"routes": [
{
"path": "/auth/google",
"method": "get",
"oauth2": {
"provider": "google",
"action": "authorize",
"scopes": ["email", "profile"]
}
},
{
"path": "/auth/google/callback",
"method": "get",
"oauth2": {
"provider": "google",
"action": "callback",
"onSuccess": {
"redirect": "https://app.example.com/dashboard"
}
}
}
]
}Token Exchange Flow
- User visits
/auth/google - Redirected to Google consent screen
- Google redirects to
/auth/google/callback - Backflow exchanges code for tokens
- Creates session/JWT and redirects to app
Google User Info
json
{
"path": "/auth/google/callback",
"method": "get",
"oauth2": {
"provider": "google",
"action": "callback"
},
"workflow": {
"steps": [
{
"id": "upsert-user",
"type": "database",
"params": {
"table": "users",
"operation": "upsert",
"data": {
"google_id": "{{oauth.user.id}}",
"email": "{{oauth.user.email}}",
"name": "{{oauth.user.name}}",
"picture": "{{oauth.user.picture}}"
},
"onConflict": ["google_id"]
}
},
{
"id": "generate-jwt",
"type": "auth",
"params": {
"action": "createToken",
"claims": {
"sub": "{{steps.upsert-user.result.id}}",
"email": "{{oauth.user.email}}"
}
}
}
]
}
}Custom OAuth2 Provider
Configuration
json
{
"oauth2": {
"okta": {
"clientId": "{{env.OKTA_CLIENT_ID}}",
"clientSecret": "{{secret:OKTA_CLIENT_SECRET}}",
"authorizeUrl": "https://your-domain.okta.com/oauth2/v1/authorize",
"tokenUrl": "https://your-domain.okta.com/oauth2/v1/token",
"userInfoUrl": "https://your-domain.okta.com/oauth2/v1/userinfo",
"callbackUrl": "https://api.example.com/auth/okta/callback",
"scopes": ["openid", "profile", "email"]
}
}
}Routes
json
{
"routes": [
{
"path": "/auth/okta",
"method": "get",
"oauth2": {
"provider": "okta",
"action": "authorize"
}
},
{
"path": "/auth/okta/callback",
"method": "get",
"oauth2": {
"provider": "okta",
"action": "callback"
}
}
]
}RBAC (Role-Based Access Control)
Configure Roles
json
{
"rbac": {
"enabled": true,
"roles": {
"admin": {
"permissions": ["*"]
},
"manager": {
"permissions": ["read:*", "write:orders", "write:products"]
},
"user": {
"permissions": ["read:own", "write:own:profile"]
}
}
}
}Protected Routes with Roles
json
{
"path": "/admin/users",
"method": "get",
"requireAuth": true,
"rbac": {
"roles": ["admin"]
}
}Permission-Based Access
json
{
"path": "/products",
"method": "post",
"requireAuth": true,
"rbac": {
"permissions": ["write:products"]
}
}Role from JWT
json
{
"path": "/dashboard",
"method": "get",
"requireAuth": true,
"rbac": {
"rolesClaim": "role",
"roles": ["admin", "manager"]
}
}JWT should include role:
json
{
"sub": "user123",
"role": "manager"
}MFA (Multi-Factor Authentication)
Enable MFA
json
{
"mfa": {
"enabled": true,
"methods": ["totp", "sms"],
"requiredFor": ["admin"]
}
}MFA Enrollment
json
{
"path": "/auth/mfa/enroll",
"method": "post",
"requireAuth": true,
"mfa": {
"action": "enroll",
"method": "totp"
}
}MFA Verification
json
{
"path": "/auth/mfa/verify",
"method": "post",
"requireAuth": true,
"mfa": {
"action": "verify"
}
}API Key Authentication
Generate API Key
json
{
"path": "/admin/api-keys",
"method": "post",
"requireAuth": true,
"rbac": { "roles": ["admin"] },
"workflow": {
"steps": [{
"id": "create-key",
"type": "auth",
"params": {
"action": "createApiKey",
"name": "{{body.name}}",
"permissions": "{{body.permissions}}",
"expiresIn": "{{body.expiresIn}}"
}
}]
}
}API Key Protected Route
json
{
"path": "/api/data",
"method": "get",
"auth": {
"type": "api_key",
"header": "X-API-Key"
}
}Usage:
bash
curl -X GET https://api.example.com/api/data \
-H "X-API-Key: bf_live_xxx"Complete Example
Tenant Config
json
{
"tenantId": "fitness-studio",
"jwt": {
"secret": "{{secret:JWT_SECRET}}",
"expiresIn": "7d"
},
"firebase": {
"projectId": "{{env.FIREBASE_PROJECT_ID}}",
"privateKey": "{{secret:FIREBASE_PRIVATE_KEY}}",
"clientEmail": "{{env.FIREBASE_CLIENT_EMAIL}}"
},
"rbac": {
"enabled": true,
"roles": {
"owner": { "permissions": ["*"] },
"instructor": { "permissions": ["read:*", "write:classes", "write:bookings"] },
"member": { "permissions": ["read:classes", "write:own:bookings"] }
}
},
"routes": [
{
"path": "/classes",
"method": "get",
"requireAuth": true
},
{
"path": "/classes",
"method": "post",
"requireAuth": true,
"rbac": { "roles": ["owner", "instructor"] }
},
{
"path": "/admin/members",
"method": "get",
"requireAuth": true,
"rbac": { "roles": ["owner"] }
}
]
}Client Integration
javascript
import { initializeApp } from 'firebase/app';
import { getAuth, signInWithEmailAndPassword } from 'firebase/auth';
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
// Sign in
const { user } = await signInWithEmailAndPassword(auth, email, password);
const token = await user.getIdToken();
// API client
const api = {
async get(path) {
return fetch(`https://api.example.com${path}`, {
headers: {
'Authorization': `Bearer ${token}`,
'x-tenant-id': 'fitness-studio'
}
}).then(r => r.json());
},
async post(path, data) {
return fetch(`https://api.example.com${path}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'x-tenant-id': 'fitness-studio',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}).then(r => r.json());
}
};
// Usage
const classes = await api.get('/classes');
await api.post('/bookings', { classId: 'yoga-101' });