Skip to main content

Authentication & Platform APIs

This page covers the authentication, setup, recovery, OAuth, and SCIM APIs. These endpoints are not under the /api/v1/admin prefix and have their own authentication requirements.

Common Error Response

All endpoints return errors in this format:

{
"error": "descriptive error message"
}
StatusMeaning
400Bad request or validation error
401Missing or invalid authentication
403Insufficient permissions or setup already complete
404Resource not found
409Conflict (duplicate resource)
429Rate limit exceeded (check Retry-After header)
500Internal server error
503Service unavailable

Setup API

These endpoints are only available before initial setup is complete (i.e., when zero admin users exist). After setup, they return 403.

Prefix: /api/v1/setup

Get Setup Status

GET /api/v1/setup/status

Returns whether the instance needs initial setup.

Response:

{
"needsSetup": true,
"devMode": false
}

needsSetup is true when no admin users exist (setup required), false after the first admin has been created.

Complete Setup

POST /api/v1/setup/complete

Creates the first admin user and tenant from a license key, and marks setup as complete.

Body:

{
"email": "admin@example.com",
"password": "secure-password-here",
"displayName": "Admin User",
"licenseKey": "payload.signature",
"externalBaseUrl": "https://surfacesec.example.com"
}
FieldRequiredDescription
emailYesAdmin user email
passwordYesAdmin password (min 12 chars, must include upper, lower, digit, special)
displayNameYesAdmin display name (2-100 chars)
licenseKeyYesSigned license key in payload.signature format (obtained from portal)
externalBaseUrlYesPublicly routable HTTPS URL for this deployment (must start with http:// or https://)

Response (201 Created):

{
"tenantId": "uuid",
"clientName": "My Organization",
"tier": "enterprise",
"expiresAt": "2027-04-19T00:00:00Z",
"maxUsers": 100,
"accessToken": "eyJhbGciOiJI...",
"refreshToken": "eyJhbGciOiJI...",
"user": { "...": "AdminUser" }
}

Authentication API

Prefix: /api/v1/auth

Tenant Discovery

GET /api/v1/auth/tenant

Auto-detects and returns the tenant for single-tenant deployments. No authentication required.

Response:

{
"tenantId": "uuid",
"name": "Organization Name",
"slug": "org-slug"
}

Login

POST /api/v1/auth/login

Authenticates an admin user and returns JWT tokens.

Headers: X-Tenant-ID (required unless tenantId is in the body)

Body:

{
"email": "admin@example.com",
"password": "password123",
"tenantId": "uuid"
}
FieldRequiredDescription
emailYesAdmin user email
passwordYesPassword (min 6 characters)
tenantIdNoCan alternatively be passed via X-Tenant-ID header

Response (success):

{
"accessToken": "eyJhbGciOiJI...",
"refreshToken": "eyJhbGciOiJI...",
"expiresAt": "2026-03-12T00:00:00Z",
"user": {
"id": "uuid",
"email": "admin@example.com",
"displayName": "Admin User",
"roleType": "super_admin"
}
}

Response (MFA required):

If the user has MFA enabled, the login endpoint returns a challenge instead:

{
"mfaRequired": true,
"mfaToken": "temp-token",
"message": "MFA verification required"
}

Use mfaToken with POST /api/v1/auth/mfa/verify to complete login.

Response (password change required):

{
"mustChangePassword": true,
"tempToken": "temp-token",
"userId": "uuid",
"message": "Password change required"
}

Use the temp token with POST /api/v1/auth/change-password to set a new password.

Verify MFA

POST /api/v1/auth/mfa/verify

Completes MFA verification during login. Accepts either a TOTP code or a one-time backupCode.

Headers: X-Tenant-ID

Body:

{
"mfaToken": "mfa-token-from-login",
"code": "123456"
}
FieldRequiredDescription
mfaTokenYesToken returned from the login response (mfaToken field)
codeConditional6-digit TOTP code. Required unless backupCode is provided.
backupCodeConditionalOne of the backup codes issued during MFA setup (consumed on use).

Response: Same as successful login response (returns accessToken, refreshToken, user).

Refresh Token

POST /api/v1/auth/refresh

Exchanges a refresh token for a new access token.

Headers: X-Tenant-ID

Body:

{
"refreshToken": "eyJhbGciOiJI..."
}

Response: Same format as login response.

Logout

POST /api/v1/auth/logout

Invalidates the current session.

Headers: X-Tenant-ID

Change Password

POST /api/v1/auth/change-password

Changes user password using a temporary token (from forced password change flow).

Headers: X-Tenant-ID

Body:

{
"tempToken": "temp-token-from-login",
"newPassword": "new-secure-password",
"confirmPassword": "new-secure-password"
}
FieldRequiredDescription
tempTokenYesTemporary token from login response
newPasswordYesNew password (min 12 characters)
confirmPasswordYesMust match newPassword

SSO (OpenID Connect)

Prefix: /api/v1/auth/sso

Get SSO Status

GET /api/v1/auth/sso/status

Returns whether SSO is configured for the tenant.

SSO Login

GET /api/v1/auth/sso/login

Redirects the user to the configured Identity Provider for authentication.

SSO Callback

GET /api/v1/auth/sso/callback

Handles the OAuth 2.0 callback from the Identity Provider. Returns JWT tokens on success.

SSO Logout

POST /api/v1/auth/sso/logout

Authentication: Requires JWT Bearer token (admin auth).

Terminates the SSO session.


SAML 2.0

Prefix: /api/v1/auth/saml

SP Metadata

GET /api/v1/auth/saml/metadata

Returns SAML Service Provider metadata (XML) for configuring your Identity Provider.

SAML Login

GET /api/v1/auth/saml/login

Initiates a SP-initiated SAML SSO flow. Redirects to the configured IdP.

SAML Callback (ACS)

POST /api/v1/auth/saml/callback

Assertion Consumer Service endpoint. Receives and validates the SAML Response from the IdP. Returns JWT tokens on success.


Recovery

Admin Reset (Break-Glass)

POST /api/v1/recovery/reset

Emergency admin account reset. Protected by the RECOVERY_KEY environment variable. The endpoint is disabled (returns 404) when RECOVERY_KEY is unset. When invoked successfully, it resets the admin password to a one-time generated value, unlocks the account, disables MFA, reactivates any suspended status, and revokes all existing sessions.

Body:

{
"recoveryKey": "shared-recovery-secret",
"email": "admin@example.com"
}
FieldRequiredDescription
recoveryKeyYesMust match the server's RECOVERY_KEY env var (constant-time compare)
emailYesEmail of the admin user to recover

Response:

{
"success": true,
"tempPassword": "generated-temporary-password",
"message": "Account recovered. User must change password on next login. MFA has been disabled."
}

The returned tempPassword is shown only once. The user must change it on next login.

caution

The recovery key should be stored securely (e.g., in a secrets vault) and rotated after use. If the environment variable is compromised, an attacker can reset admin access.


OAuth 2.0 (SCIM Token Management)

These endpoints implement the OAuth 2.0 Client Credentials grant for SCIM provisioning. SCIM clients (Entra ID, Okta, etc.) use these endpoints to obtain Bearer tokens.

Prefix: /oauth

Token

POST /oauth/token

Issues an access token using client credentials.

Accepts both application/json and application/x-www-form-urlencoded.

Body (JSON):

{
"grantType": "client_credentials",
"clientId": "scim-client-id",
"clientSecret": "scim-client-secret",
"scope": "scim:read scim:write"
}

Body (form-encoded):

grant_type=client_credentials&client_id=scim-client-id&client_secret=scim-client-secret&scope=scim:read+scim:write

Response:

{
"accessToken": "generated-token",
"tokenType": "Bearer",
"expiresIn": 3600,
"scope": "scim:read scim:write"
}

Error Response:

{
"error": "invalid_client",
"errorDescription": "Client authentication failed"
}

Token Introspection

POST /oauth/introspect

Checks whether an access token is active.

Body:

{
"token": "access-token-to-check"
}

Response:

{
"active": true,
"scope": "scim:read scim:write",
"clientId": "scim-client-id",
"tokenType": "Bearer",
"exp": 1700000000
}

Token Revocation

POST /oauth/revoke

Revokes an access token.

Body:

{
"token": "access-token-to-revoke"
}

SCIM 2.0

Implements RFC 7643 (Core Schema) and RFC 7644 (Protocol) for automated user and group provisioning from identity providers.

Prefix: /scim/v2

Authentication: OAuth 2.0 Bearer token (obtained via POST /oauth/token)

Rate Limit: 100 requests per 60 seconds per client.

Service Provider Configuration

GET /scim/v2/ServiceProviderConfig

Returns the SCIM service provider configuration (supported features, authentication schemes, etc.).

Schemas

GET /scim/v2/Schemas

Returns the supported SCIM resource schemas.

Users

List Users

GET /scim/v2/Users

Query Parameters:

ParameterTypeDescription
filterstringSCIM filter expression (e.g., userName eq "jane@example.com", externalId eq "12345")
startIndexinteger1-based pagination start (default: 1)
countintegerPage size, max 1000 (default: 100)

Create User

POST /scim/v2/Users

Body: SCIM User resource (RFC 7643 format)

Response: 201 Created with the created SCIM User resource.

Get User

GET /scim/v2/Users/{id}

Update User

PUT /scim/v2/Users/{id}
PATCH /scim/v2/Users/{id}

Both PUT (full replace) and PATCH (partial update with SCIM patch operations) are supported.

Delete User

DELETE /scim/v2/Users/{id}

Response: 204 No Content

Groups

List Groups

GET /scim/v2/Groups

Query Parameters: Same as Users (filter, startIndex, count).

Create Group

POST /scim/v2/Groups

Response: 201 Created

Get Group

GET /scim/v2/Groups/{id}

Update Group

PUT /scim/v2/Groups/{id}
PATCH /scim/v2/Groups/{id}

Delete Group

DELETE /scim/v2/Groups/{id}

Response: 204 No Content