Skip to content

SSO Configuration Guide

This guide covers SSO configuration for org admins. It describes how to connect Microsoft Entra ID via OIDC (OAuth 2.0), configure a SAML 2.0 identity provider, rotate SCIM bearer tokens, and select the authentication method appropriate for your deployment.


Arbitex supports three authentication methods:

MethodProtocolUse case
Entra ID OIDCOAuth 2.0 / OpenID ConnectEntra ID (Azure AD) tenants — recommended for most organizations
SAML 2.0SAML 2.0Any SAML-compatible IdP (Okta, PingFederate, AD FS, Entra ID in SAML mode)
Username + passwordArbitex nativeLocal accounts not tied to an external IdP

OIDC and SAML can be active simultaneously. Users authenticate via whichever method their client initiates. Native accounts continue to work alongside SSO.


1. Client requests: GET /api/auth/oauth/google/authorize
2. Arbitex returns { authorization_url }
3. Client redirects user to authorization_url (Microsoft login page)
4. User authenticates with Entra ID; Microsoft redirects to callback URI with code
5. Client sends: POST /api/auth/oauth/google/callback { code, state }
6. Arbitex exchanges code for tokens, validates OIDC ID token
7. Arbitex provisions user account if first login (JIT provisioning)
8. Arbitex returns { access_token, refresh_token, user }

The email_verified claim in the ID token must be true. Authentication is rejected if the claim is absent or false.

JIT-provisioned users receive the USER role by default. Admin role must be assigned separately after first login.

  • Microsoft Azure subscription with Entra ID
  • Permission to create App Registrations in Entra ID
  • Arbitex platform environment variables accessible to platform operators

Step 1: Register an application in Entra ID

Section titled “Step 1: Register an application in Entra ID”
  1. Sign in to the Azure Portal.
  2. Navigate to Microsoft Entra IDApp registrationsNew registration.
  3. Configure:
    • Name: Arbitex Gateway (or your preferred display name)
    • Supported account types: Accounts in this organizational directory only (single tenant) for most deployments
    • Redirect URI: Leave blank — configure in Step 2
  4. Click Register.
  5. Note the Application (client) ID and Directory (tenant) ID from the Overview page.
  1. Navigate to AuthenticationAdd a platformWeb.
  2. Add the redirect URI:
    https://api.arbitex.ai/api/auth/oauth/google/callback
  3. Leave Implicit grant and hybrid flows checkboxes unchecked — Arbitex uses the authorization code flow.
  4. Click Save.
  1. Navigate to Certificates & secretsNew client secret.
  2. Set a description and expiry period.
  3. Click Add.
  4. Copy the secret Value immediately — it is not shown again after you navigate away.

Arbitex requires these Microsoft Graph delegated permissions:

PermissionPurpose
openidOIDC login
profileDisplay name for JIT provisioning
emailEmail address (primary user identifier)
  1. Navigate to API permissionsAdd a permissionMicrosoft GraphDelegated permissions.
  2. Add openid, profile, and email.
  3. Click Grant admin consent for [your tenant] if your tenant requires admin consent for delegated permissions.

Set these environment variables on the Arbitex Platform API:

VariableValue
GOOGLE_CLIENT_IDApplication (client) ID from Step 1
GOOGLE_CLIENT_SECRETClient secret value from Step 3
GOOGLE_REDIRECT_URIhttps://api.arbitex.ai/api/auth/oauth/google/callback

In production deployments, store these values in Azure Key Vault and inject via the Key Vault CSI driver at pod startup. Do not store them in plaintext configuration files.

Restart the Platform API after setting these variables.

Terminal window
GET https://api.arbitex.ai/api/auth/oauth/status

Response when configured:

{ "sso_enabled": true }

If sso_enabled is false, the environment variables are not being read correctly. Verify that Key Vault secrets are accessible and the pod has restarted.


SAML IdP configuration is managed via the admin API at /api/admin/saml/idp. Arbitex acts as the Service Provider (SP).

ParameterValue
ACS URLhttps://api.arbitex.ai/api/auth/saml/acs
Entity ID (SP)https://api.arbitex.ai
NameID formaturn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
BindingHTTP POST

Use these values when configuring Arbitex as a relying party in your IdP (Entra ID enterprise app, Okta app, AD FS relying party trust, or PingFederate SP connection).

FieldRequiredDescription
nameYesDisplay name for this IdP configuration
entity_idYesIdP Entity ID from IdP metadata (must be unique)
sso_urlYesIdP SSO endpoint URL (HTTP POST binding)
slo_urlNoIdP Single Logout endpoint URL
x509_certYesIdP signing certificate (PEM format, without headers)
attribute_mappingNoJSON object mapping SAML attributes to Arbitex fields
is_activeYestrue to enable this IdP for login
Terminal window
POST /api/admin/saml/idp
Authorization: Bearer <admin_token>
Content-Type: application/json
{
"name": "Entra ID SAML",
"entity_id": "https://sts.windows.net/{tenant_id}/",
"sso_url": "https://login.microsoftonline.com/{tenant_id}/saml2",
"slo_url": "https://login.microsoftonline.com/{tenant_id}/saml2",
"x509_cert": "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA...",
"attribute_mapping": {
"email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
"display_name": "http://schemas.microsoft.com/identity/claims/displayname"
},
"is_active": true
}

Response (201 Created):

{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"name": "Entra ID SAML",
"entity_id": "https://sts.windows.net/{tenant_id}/",
"sso_url": "https://login.microsoftonline.com/{tenant_id}/saml2",
"slo_url": "https://login.microsoftonline.com/{tenant_id}/saml2",
"x509_cert": "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA...",
"attribute_mapping": { ... },
"is_active": true,
"created_at": "2026-03-11T00:00:00Z",
"updated_at": "2026-03-11T00:00:00Z"
}

The entity_id must be unique. If a configuration with the same entity_id already exists, the request returns 409 Conflict.

Step 2: Configure Entra ID as SAML IdP (optional — Entra ID-specific)

Section titled “Step 2: Configure Entra ID as SAML IdP (optional — Entra ID-specific)”

If you are configuring Entra ID in SAML mode rather than OIDC mode:

  1. In the Azure Portal, navigate to Microsoft Entra IDEnterprise applicationsNew applicationCreate your own application.
  2. Select Integrate any other application you don’t find in the gallery.
  3. Under Single sign-onSAML, configure:
    • Entity ID / Identifier: https://api.arbitex.ai
    • Reply URL (ACS URL): https://api.arbitex.ai/api/auth/saml/acs
    • Sign on URL: Leave blank for IdP-initiated flow, or set to your portal URL
  4. Download the Certificate (Base64) from the SAML Signing Certificate section.
  5. Note the Login URL and Azure AD Identifier (Entity ID) from the Setup section.
  6. Use those values in the sso_url and entity_id fields when calling POST /api/admin/saml/idp.
Terminal window
GET /api/admin/saml/idp
Authorization: Bearer <admin_token>

Response:

{
"items": [
{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"name": "Entra ID SAML",
"entity_id": "https://sts.windows.net/{tenant_id}/",
"is_active": true,
...
}
],
"total": 1
}
Terminal window
PUT /api/admin/saml/idp/{idp_id}
Authorization: Bearer <admin_token>
Content-Type: application/json
{
"x509_cert": "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A...",
"is_active": true
}

Only fields included in the request body are updated. To rotate a signing certificate, update only x509_cert. The entity_id uniqueness constraint is re-validated if entity_id is included.

Terminal window
DELETE /api/admin/saml/idp/{idp_id}
Authorization: Bearer <admin_token>

Returns 204 No Content. After deletion, users cannot authenticate via this IdP.


Arbitex supports SCIM 2.0 for automated user and group provisioning from Entra ID, Okta, and other SCIM-compatible IdPs. SCIM authentication uses a per-org bearer token separate from JWT access tokens.

  • Each org has one active SCIM token at a time, stored as a bcrypt hash.
  • The raw token value is returned once at generation time and cannot be retrieved afterwards.
  • Rotating a token invalidates the previous token immediately.
  • SCIM provisioning requests must present the current token in the Authorization: Bearer header.
Terminal window
POST /v1/orgs/{org_id}/scim/token/rotate
Authorization: Bearer <admin_token>

Replace {org_id} with the UUID of your organization.

Response (200 OK):

{
"token": "abc123xyz...",
"org_id": "550e8400-e29b-41d4-a716-446655440000",
"created_at": "2026-03-11T00:00:00Z"
}

Store the token value immediately. This is the only time the raw value is returned. The platform stores only the bcrypt hash.

After rotation:

  1. Copy the new token value.
  2. Update the SCIM provisioning configuration in your IdP (Entra ID or Okta) with the new token.
  3. Verify provisioning by triggering a test sync in your IdP.

The previous token is invalidated at the time of rotation. Any provisioning requests using the old token return 401 Unauthorized immediately.

EndpointMethodDescription
/scim/v2/UsersGETList users
/scim/v2/Users/{user_id}GETGet a single user
/scim/v2/UsersPOSTCreate a user
/scim/v2/Users/{user_id}PUTReplace user attributes
/scim/v2/Users/{user_id}PATCHPartial user update
/scim/v2/Users/{user_id}DELETEDeactivate a user (soft delete)
/scim/v2/GroupsGETList groups
/scim/v2/Groups/{group_id}GETGet a single group
/scim/v2/GroupsPOSTCreate a group
/scim/v2/Groups/{group_id}PUTReplace group attributes
/scim/v2/Groups/{group_id}PATCHPartial group update
/scim/v2/Groups/{group_id}DELETEDelete a group

SCIM DELETE for users performs a soft delete (is_active=false) rather than removing the user record, preserving audit history.

Arbitex extends the SCIM Group resource with additional attributes for policy configuration. The extension URN is urn:ietf:params:scim:schemas:extension:arbitex:2.0:Group. Your IdP can discover the schema via GET /scim/v2/Schemas.

Available extension attributes:

AttributeTypeDescription
dlpPolicystringDLP policy ID to apply to the group
modelAccessstringModel access configuration
complianceBundleslistCompliance bundle IDs to assign
quotasobjectPer-group usage quotas

OIDC, SAML, and native authentication can operate simultaneously. There is no exclusivity constraint. Users are matched to their auth method based on how they initiate the login:

  • OIDC: client calls /api/auth/oauth/google/authorize → user redirected to Microsoft
  • SAML: IdP-initiated or SP-initiated via the SAML endpoint
  • Native: client posts { email, password } to /api/auth/login

Remove the GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and GOOGLE_REDIRECT_URI environment variables and restart the Platform API. The /api/auth/oauth/status endpoint returns { "sso_enabled": false }.

Users provisioned via OIDC SSO retain their accounts. They must use native password authentication or SAML after OIDC is disabled.

Set is_active: false on the IdP configuration:

Terminal window
PUT /api/admin/saml/idp/{idp_id}
Authorization: Bearer <admin_token>
Content-Type: application/json
{ "is_active": false }

Or delete the configuration entirely (DELETE /api/admin/saml/idp/{idp_id}). Deletion is permanent.

Native authentication (username + password) is not disabled by enabling SSO. Contact your Arbitex platform operator to disable native login at the deployment level if you require SSO-only enforcement.