A self-hosted, multi-tenant identity provider (IdP) with a built-in OpenID Connect / OAuth 2.0 authorization server (not a pass-through gateway/proxy).
Base URL: http://localhost:3200/api
All responses are wrapped in:
- Success:
{ "success": true, "data": ... } - Error:
{ "success": false, "error": "...", "message": "...", "statusCode": ... }
Tenant header: x-tenant-id: <uuid> (optional; auto-detected from the user's first tenant if not sent)
User registration. Creates a new tenant or joins an existing one.
Auth: Public | Rate limit: 5/min
// Request
{
"email": "user@example.com",
"password": "SecureP4ssw0rd!",
"name": "John Doe",
"tenantName": "My Company", // optional: name of the new tenant
"tenantSlug": "my-company" // optional: if omitted, generated from the email
}// Response 201
{ "message": "Registration successful. Please check your email to verify your account." }Login with email and password.
Auth: Public | Rate limit: 5/min
// Request
{ "email": "user@example.com", "password": "SecureP4ssw0rd!" }
// Response 200 (without MFA)
{
"accessToken": "eyJ...",
"refreshToken": "a1b2c3...",
"expiresIn": "15m",
"tokenType": "Bearer"
}
// Response 200 (with MFA)
{
"mfaRequired": true,
"mfaChallengeToken": "eyJ..."
}Rotate refresh token (single-use).
Auth: Public | Rate limit: 5/min
// Request
{ "refreshToken": "a1b2c3..." }
// Response 200
{ "accessToken": "eyJ...", "refreshToken": "d4e5f6...", "expiresIn": "15m", "tokenType": "Bearer" }Revoke the current session.
Auth: Bearer token
// Response 200
{ "message": "Logged out successfully" }Verify email with a token (sent by email upon registration).
Auth: Public
// Request
{ "token": "abc123..." }
// Response 200
{ "message": "Email verified successfully" }Request a reset link. Always returns the same message (anti-enumeration).
Auth: Public | Rate limit: 3/min
// Request
{ "email": "user@example.com" }
// Response 200
{ "message": "If an account with that email exists, a password reset link has been sent." }Reset password with a token.
Auth: Public | Rate limit: 3/min
// Request
{ "token": "abc123...", "newPassword": "NewSecureP4ss!" }
// Response 200
{ "message": "Password reset successful" }Request magic link login.
Auth: Public | Rate limit: 3/min
// Request
{ "email": "user@example.com" }
// Response 200
{ "message": "If an account with that email exists, a magic link has been sent." }Verify magic link and obtain tokens.
Auth: Public
// Request
{ "token": "abc123..." }
// Response 200
{ "accessToken": "eyJ...", "refreshToken": "...", "expiresIn": "15m", "tokenType": "Bearer" }Start TOTP setup. Returns QR code and manual secret.
Auth: Bearer token
// Response 200
{
"setupToken": "abc123...",
"qrCodeUrl": "data:image/png;base64,...",
"manualEntry": "JBSWY3DPEHPK3PXP"
}Verify the first TOTP code. Persists the secret and generates recovery codes.
Auth: Bearer token
// Request
{ "setupToken": "abc123...", "code": "123456" }
// Response 200
{ "recoveryCodes": ["ABC123DEF4", "GHI567JKL8", ...] } // 8 codesVerify TOTP during the login challenge.
Auth: Public (uses mfaChallengeToken)
// Request
{ "mfaChallengeToken": "eyJ...", "code": "123456" }
// Response 200
{ "accessToken": "eyJ...", "refreshToken": "...", "expiresIn": "15m", "tokenType": "Bearer" }Disable TOTP.
Auth: Bearer token
// Response 200
{ "message": "TOTP disabled successfully" }Use a recovery code during the login challenge.
Auth: Public (uses mfaChallengeToken)
// Request
{ "mfaChallengeToken": "eyJ...", "code": "ABC123DEF4" }
// Response 200
{ "accessToken": "...", "refreshToken": "...", "remainingRecoveryCodes": 7 }Check how many recovery codes remain.
Auth: Bearer token
// Response 200
{ "remaining": 7 }Regenerate the 8 recovery codes (invalidates the previous ones).
Auth: Bearer token
// Response 200
{ "recoveryCodes": ["NEW1CODE23", "NEW4CODE56", ...] }List the user's active sessions.
Auth: Bearer token
// Response 200
[
{
"id": "uuid",
"userAgent": "Mozilla/5.0...",
"ipAddress": "192.168.1.1",
"deviceName": null,
"lastActiveAt": "2026-03-26T10:00:00Z",
"createdAt": "2026-03-25T08:00:00Z",
"device": { "browser": "Chrome", "os": "Linux" }
}
]Revoke a specific session.
Auth: Bearer token
// Response 200
{ "message": "Session revoked" }Revoke all other sessions.
Auth: Bearer token
// Response 200
{ "message": "All other sessions revoked", "revokedCount": 3 }Current user's profile.
Auth: Bearer token
// Response 200
{
"id": "uuid",
"email": "user@example.com",
"name": "John Doe",
"avatarUrl": null,
"status": "ACTIVE",
"emailVerifiedAt": "2026-03-25T...",
"lastLoginAt": "2026-03-26T...",
"createdAt": "2026-03-25T...",
"mfaEnabled": true,
"oauthAccounts": [{ "provider": "GOOGLE", "email": "user@gmail.com" }]
}Update name or avatar.
Auth: Bearer token
// Request
{ "name": "Jane Doe", "avatarUrl": "https://..." }Auth: Bearer token
// Request
{ "currentPassword": "OldP4ss!", "newPassword": "NewP4ss!" }
// Response 200
{ "message": "Password changed successfully" }Request account deletion (30-day grace period).
Auth: Bearer token
// Response 200
{ "message": "Account scheduled for deletion. You have 30 days to cancel." }Cancel pending deletion.
Auth: Bearer token
List the tenant's users. Paginated.
Auth: Bearer token | Roles: OWNER, ADMIN
GET /users?page=1&limit=20
// Response 200
{
"items": [...],
"total": 50,
"page": 1,
"limit": 20,
"totalPages": 3
}Deactivate a user in the tenant.
Auth: Bearer token | Roles: OWNER, ADMIN
Reactivate a user in the tenant.
Auth: Bearer token | Roles: OWNER, ADMIN
Create a new tenant. The creator is assigned as OWNER.
Auth: Bearer token
// Request
{ "name": "My Company", "slug": "my-company", "domain": "mycompany.com" }List the user's tenants.
Auth: Bearer token
// Response 200
[
{ "id": "uuid", "name": "My Company", "slug": "my-company", "role": "OWNER", "joinedAt": "..." }
]Tenant details.
Auth: Bearer token | Roles: OWNER, ADMIN
Update tenant settings.
Auth: Bearer token | Roles: OWNER
// Request
{ "name": "My Company Inc.", "mfaRequired": true }List the tenant's roles with their permissions.
Auth: Bearer token | Roles: OWNER, ADMIN
Create a custom role.
Auth: Bearer token | Roles: OWNER, ADMIN
// Request
{
"name": "EDITOR",
"description": "Can edit content",
"permissions": [
{ "action": "read", "subject": "User" },
{ "action": "create", "subject": "Content" },
{ "action": "update", "subject": "Content" }
]
}Update a role (system roles cannot be renamed).
Auth: Bearer token | Roles: OWNER, ADMIN
Delete a custom role (not system roles, not roles currently in use).
Auth: Bearer token | Roles: OWNER
Assign a role to a user in the tenant.
Auth: Bearer token | Roles: OWNER, ADMIN
// Request
{ "userId": "uuid", "roleId": "uuid" }List linked providers.
Auth: Bearer token
Unlink a provider (not allowed if it's the only auth method).
Auth: Bearer token
Public keys for token verification (no /api prefix).
Auth: Public
// Response 200
{
"keys": [
{
"kty": "RSA",
"kid": "auth-gateway-key-1",
"use": "sig",
"alg": "RS256",
"n": "...",
"e": "AQAB"
}
]
}System status (no /api prefix).
Auth: Public
// Response 200
{
"status": "ok",
"info": {
"database": { "status": "up" },
"redis": { "status": "up" }
},
"error": {},
"details": {
"database": { "status": "up" },
"redis": { "status": "up" }
}
}// Response 503 (unhealthy)
{
"status": "error",
"info": { "database": { "status": "up" } },
"error": { "redis": { "status": "down", "message": "connect ECONNREFUSED" } },
"details": {
"database": { "status": "up" },
"redis": { "status": "down", "message": "connect ECONNREFUSED" }
}
}