Skip to content

SSO Integration Guide

This guide walks through configuring single sign-on (SSO) for Arbitex from end to end, covering both identity provider setup and platform configuration. Two providers are covered:

  • Google OAuth 2.0 / OIDC — suitable for organizations using Google Workspace or personal Google accounts
  • Microsoft Entra ID (Azure AD) — suitable for enterprise organizations; supports both OIDC and SAML 2.0

Both providers use JIT (just-in-time) user provisioning: users are created in Arbitex automatically on their first login, with no pre-staging required.


Method Best for Protocol
Google OAuth Google Workspace orgs, developer teams OIDC
Entra ID OIDC Microsoft 365 orgs, single tenant OIDC
Entra ID SAML Enterprise with existing SAML IdP federation, Okta bridging SAML 2.0

SAML and OIDC SSO are independent — you can configure both simultaneously and use each for different user populations. API-key-authenticated requests are unaffected by SSO configuration.


sequenceDiagram
    participant User
    participant Arbitex as Arbitex Platform<br/>(api.arbitex.ai)
    participant Google as Google OIDC

    User->>Arbitex: GET /api/auth/oauth/google/authorize
    Note over Arbitex: Generate state token (stored in Redis, 10-min TTL)
    Arbitex-->>User: { authorization_url }
    User->>Google: Redirect to authorization URL
    Google->>User: Consent screen (first login only)
    User->>Arbitex: POST /api/auth/oauth/google/callback { code, state }
    Note over Arbitex: Validate state (single-use, CSRF protection)
    Arbitex->>Google: Exchange code → id_token + access_token
    Note over Arbitex: Validate OIDC ID token<br/>Verify email_verified = true
    Note over Arbitex: JIT-provision user if new
    Arbitex-->>User: { access_token, refresh_token, user }

CSRF protection is fail-closed: if Redis is unavailable, the OAuth flow is rejected (503) rather than allowing a potentially unsafe login.

  • Google Cloud project (free tier is sufficient)
  • Google Workspace admin access is not required for basic Google OAuth
  • Arbitex platform admin credentials

If you don’t already have a Google Cloud project for Arbitex:

  1. Go to console.cloud.google.com.
  2. Click the project dropdown at the top → New Project.
  3. Name the project (e.g., Arbitex SSO) and click Create.

Step 2: Enable the Google Identity service

Section titled “Step 2: Enable the Google Identity service”
  1. In the Cloud Console, navigate to APIs & ServicesLibrary.
  2. Search for Google Identity or OAuth 2.0.
  3. The OAuth 2.0 service is enabled by default on new projects — no manual enablement is needed.
Section titled “Step 3: Configure the OAuth consent screen”

This screen is shown to users on their first Google login. It must be configured before creating OAuth credentials.

  1. Navigate to APIs & ServicesOAuth consent screen.
  2. Select the user type:
    • Internal — only users in your Google Workspace organization can sign in. Recommended for enterprise deployments.
    • External — any Google account can sign in. Required if you don’t have Google Workspace or want to allow personal accounts.
  3. Fill in the required fields:
    • App name: Arbitex Gateway (or your preferred display name)
    • User support email: your admin email
    • Developer contact information: your admin email
  4. Click Save and Continue.
  5. On the Scopes step, add the following scopes:
    • openid
    • email
    • profile
  6. Click Save and Continue through the remaining steps.

Key configuration screen — Scopes step:

Add three scopes: openid, email, profile. These are the only scopes Arbitex requires. Do not add additional scopes — the platform does not request or use them.

  1. Navigate to APIs & ServicesCredentialsCreate CredentialsOAuth client ID.
  2. Select Web application as the application type.
  3. Set the name (e.g., Arbitex Platform).
  4. Under Authorized redirect URIs, add:
    https://api.arbitex.ai/api/auth/oauth/google/callback
    For local development, also add:
    http://localhost:8100/api/auth/oauth/google/callback
  5. Click Create.
  6. A dialog shows your Client ID and Client Secret. Copy both — the secret is not displayed again after you close the dialog.

Key configuration screen — OAuth client creation:

The redirect URI must exactly match what Arbitex sends in the authorization request. A mismatch produces redirect_uri_mismatch from Google. The URI is case-sensitive.

Set the following environment variables on the Arbitex Platform API:

Variable Value Notes
GOOGLE_CLIENT_ID Client ID from Step 4 Public identifier
GOOGLE_CLIENT_SECRET Client Secret from Step 4 Treat as a credential
GOOGLE_REDIRECT_URI https://api.arbitex.ai/api/auth/oauth/google/callback Must match Step 4

In Kubernetes deployments, store these in Azure Key Vault or a Kubernetes secret — never in plaintext in ConfigMaps or source code. Restart the Platform API pod after setting the variables.

Check SSO status:

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

Expected response:

{ "sso_enabled": true }

Initiate a test login:

Terminal window
GET https://api.arbitex.ai/api/auth/oauth/google/authorize
{
"authorization_url": "https://accounts.google.com/o/oauth2/v2/auth?..."
}

Open the authorization_url in a browser, sign in with a Google account, and verify the callback returns:

{
"access_token": "eyJ...",
"refresh_token": "eyJ...",
"token_type": "bearer",
"user": { "email": "[email protected]", ... }
}
Field Source
Email email claim — must be email_verified: true
Username name claim, or email prefix if name is absent
Role USER (default) — admin role must be assigned separately
Password Not set — authentication is Google-only

If the username conflicts with an existing account, a UUID suffix is appended automatically.


Arbitex supports two Entra ID integration paths:

Path When to use
OIDC Simpler setup; single-tenant organizations
SAML 2.0 Enterprise federation, existing SAML infrastructure, Okta bridging, group claims

Both support JIT provisioning and group mapping.

Entra ID OIDC uses the same Arbitex OAuth endpoints as Google but points at Microsoft’s OIDC discovery document.

  1. Sign in to portal.azure.com.
  2. Navigate to Microsoft Entra IDApp registrationsNew registration.
  3. Configure:
    • Name: Arbitex Gateway (or your preferred name)
    • Supported account types: Accounts in this organizational directory only (single tenant — recommended) or Accounts in any organizational directory (multi-tenant)
    • Redirect URI: leave blank for now (added in Step 2)
  4. Click Register. Note the Application (client) ID and Directory (tenant) ID.
  1. In the app registration, go to AuthenticationAdd a platformWeb.
  2. Add:
    https://api.arbitex.ai/api/auth/oauth/google/callback
  3. Leave Implicit grant and hybrid flows unchecked — Arbitex uses the authorization code flow with PKCE.
  4. Click Save.
  1. Go to Certificates & secretsNew client secret.
  2. Set a description and expiry period (12–24 months recommended).
  3. Click Add. Copy the Value immediately — it is not shown again.

Add delegated permissions under Microsoft Graph:

Permission Type Purpose
openid Delegated OIDC login
profile Delegated Display name
email Delegated Email address

Click Grant admin consent for [your tenant] if your tenant requires admin consent for delegated permissions.

Set the same three environment variables as Google OAuth, but the client ID and secret are from Entra:

Variable Value
GOOGLE_CLIENT_ID Application (client) ID from Step 1
GOOGLE_CLIENT_SECRET Client secret value from Step 3
GOOGLE_REDIRECT_URI https://api.arbitex.ai/api/auth/oauth/google/callback

The platform uses GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET for both Google and Entra OIDC — the variable names are historical. The OIDC discovery document is auto-detected based on the token issuer in the ID token.


SAML is recommended when you need group claims, IdP-initiated login, or integration with an existing enterprise SAML federation.

Terminal window
GET https://api.arbitex.ai/api/auth/saml/metadata

The metadata XML contains:

  • Entity ID: arbitex
  • ACS URL: https://api.arbitex.ai/api/auth/saml/acs (POST binding)
  • SLO URL: https://api.arbitex.ai/api/auth/saml/slo (redirect binding)
  • NameID format: urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress

Download or copy this XML — you’ll upload it to Entra in the next step.

Step 2: Create an Enterprise Application in Entra

Section titled “Step 2: Create an Enterprise Application in Entra”
  1. Navigate to Microsoft Entra IDEnterprise applicationsNew application.
  2. Select Create your own application.
  3. Choose Integrate any other application you don’t find in the gallery (Non-gallery).
  4. Name it Arbitex Gateway and click Create.
  1. In the new application, go to Single sign-onSAML.
  2. Click Upload metadata file and upload the Arbitex SP metadata XML from Step 1.
    • Alternatively, configure manually:
      • Identifier (Entity ID): arbitex
      • Reply URL (ACS URL): https://api.arbitex.ai/api/auth/saml/acs
  3. Under Attributes & Claims, configure the attribute statements:
Claim name Value
emailaddress user.mail
name user.displayname
groups user.groups (optional — add if you want group mapping)

The full SAML attribute URIs that Arbitex expects by default:

Arbitex field Attribute URI
email http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
username http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
groups http://schemas.microsoft.com/ws/2008/06/identity/claims/groups

Key configuration screen — Attributes & Claims:

The emailaddress claim is required. The name and groups claims are optional. If name is absent, Arbitex derives the username from the email prefix. If groups is absent, group mapping is skipped.

  1. In the SAML Certificates section, download the Certificate (Base64) — you’ll need this in Step 4.
  2. Note the Login URL (SSO URL) and Microsoft Entra Identifier (entity ID / issuer).

Use the Arbitex admin API to register the IdP:

Terminal window
POST https://api.arbitex.ai/api/v1/admin/saml/idp
Authorization: Bearer arb_live_your-admin-key
Content-Type: application/json
{
"name": "Microsoft Entra ID",
"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": "<base64-cert-without-pem-headers>",
"is_active": true,
"attribute_mapping": {
"email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
"username": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
"groups": "http://schemas.microsoft.com/ws/2008/06/identity/claims/groups"
}
}

Replace {tenant_id} with your Azure Directory (tenant) ID.

To prepare the x509_cert value from the downloaded .cer file:

Terminal window
# Strip PEM headers and collapse to a single line
cat arbitex-entra.cer | grep -v "^-----" | tr -d '\n'

Response (201 Created):

{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Microsoft Entra ID",
"entity_id": "https://sts.windows.net/{tenant_id}/",
"is_active": true,
"created_at": "2026-03-12T00:00:00Z"
}

Note the id — this is your IdP UUID used in login URLs.

Go back to the Entra SSO configuration and set the Sign on URL to:

https://api.arbitex.ai/api/auth/saml/login?idp_id={your-idp-uuid}

This enables SP-initiated login from the Arbitex portal.

In Enterprise applicationsArbitex GatewayUsers and groups, assign the users or groups that should be able to sign in.

Open in a browser:

https://api.arbitex.ai/api/auth/saml/login?idp_id={your-idp-uuid}

After authentication, verify the response includes access_token and the correct user email.

When the SAML assertion includes a groups attribute, Arbitex synchronizes group memberships:

  1. The SAML group value (Entra Object ID, e.g. a1b2c3d4-...) is matched against Group.name and Group.entra_ad_group_id in Arbitex.
  2. If a match is found, the user is added to the group.
  3. Group memberships from SAML are additive — existing memberships are not removed.

Configure Arbitex groups to match Entra Object IDs:

Terminal window
PUT https://api.arbitex.ai/api/v1/admin/groups/{group_id}
{
"entra_ad_group_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

The GOOGLE_CLIENT_ID environment variable is not set or not readable by the platform pod. Verify:

  1. The secret exists in Key Vault and the pod has CSI driver access.
  2. The pod restarted after the secret was set (environment variables are read at startup).

503 Redis required for OAuth CSRF protection

Section titled “503 Redis required for OAuth CSRF protection”

REDIS_URL is not configured. Google OAuth requires Redis for CSRF state storage. Set REDIS_URL and restart the platform.

400 Invalid or expired OAuth state parameter

Section titled “400 Invalid or expired OAuth state parameter”

The state parameter is missing or expired (10-minute TTL). Common causes:

  • The user took more than 10 minutes to complete the consent screen
  • Multiple tabs completing the flow simultaneously (state is single-use)
  • Redis was flushed between the authorize and callback calls

Retry the login flow.

Google requires email_verified: true in the OIDC ID token. Personal Gmail accounts are always verified. Google Workspace accounts with custom domains should also be verified. If you see this error, check whether the Google account has a verified email in the Google Account settings.

The redirect URI sent by Arbitex does not match the URI registered in the Google Cloud Console. Verify GOOGLE_REDIRECT_URI exactly matches the authorized redirect URI (including scheme, host, path — case-sensitive).


SAML Response signature validation failed. Common causes:

  • Wrong x509_cert in the Arbitex IdP configuration — update with the certificate from the Entra SAML Certificates section
  • Certificate rotated in Entra but not updated in Arbitex (see Certificate rotation)
  • Clock skew between Arbitex and Entra servers — SAML responses are valid for ~5 minutes

400 No active IdP configuration found for issuer

Section titled “400 No active IdP configuration found for issuer”

The Issuer in the SAML Response does not match the entity_id in Arbitex. The Entra issuer is https://sts.windows.net/{tenant_id}/. Verify this exactly matches the entity_id you registered.

The email attribute is missing. Verify the Attributes & Claims configuration in Entra sends the emailaddress claim using the URI http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress.

Users see “You are not authorized to access this application” (Entra error)

Section titled “Users see “You are not authorized to access this application” (Entra error)”

The user is not assigned to the Arbitex Enterprise Application. Go to Users and groups in the Entra app and assign the user or their group.


Arbitex acts as the SAML Service Provider. The following endpoints are used for SAML integration:

Endpoint URL Purpose
SP metadata GET https://api.arbitex.ai/api/auth/saml/metadata SP metadata XML for IdP configuration
SP-initiated login GET https://api.arbitex.ai/api/auth/saml/login?idp_id={uuid} Initiates SP-initiated SSO flow
Assertion Consumer Service POST https://api.arbitex.ai/api/auth/saml/acs Receives and processes SAML Response from IdP
Single Logout GET https://api.arbitex.ai/api/auth/saml/slo?idp_id={uuid} SP-initiated single logout

When your IdP rotates its signing certificate:

  1. Obtain the new certificate from your IdP.
  2. Update the IdP configuration in Arbitex with the new x509_cert value (no PEM headers):
Terminal window
PUT https://api.arbitex.ai/api/v1/admin/saml/idp/{idp_id}
Authorization: Bearer arb_live_your-admin-key
Content-Type: application/json
{ "x509_cert": "<base64-cert-no-pem-headers>" }
  1. Verify the update with a test login.

Arbitex validates the SAML Response signature against the stored certificate. If the certificate is expired or mismatched, authentication fails with a 401.

Plan for certificate rotation before expiry. Most IdP certificates have a 3-year validity period. Set a calendar reminder 30 days before expiry.


To initiate SP-initiated single logout, redirect the user to the SLO endpoint:

GET https://api.arbitex.ai/api/auth/saml/slo?idp_id={idp_id}
Authorization: Bearer arb_live_user-token

Arbitex generates a SAML LogoutRequest using the user’s stored NameID and SessionIndex (captured at login) and returns the IdP SLO redirect URL.

If the IdP does not support SLO (no slo_url configured), a 400 is returned. In this case, handle logout by clearing the local session — the IdP session remains active.


All SSO settings are also manageable from Admin → SSO in the admin panel.

The SAML IdPs tab lists all configured SAML identity providers. To add one:

  1. Navigate to Admin → SSO → SAML IdPs.
  2. Click Add Identity Provider.
  3. Fill in the required fields: Name, Entity ID, SSO URL, optional SLO URL, X.509 Certificate, and Active toggle.
  4. Click Save.

The SCIM Sync tab manages provisioning tokens. To rotate the SCIM token from the UI:

  1. Navigate to Admin → SSO → SCIM Sync.
  2. Click Rotate Token and confirm.
  3. Copy the displayed token immediately — it is shown only once.
  4. Update your IdP’s SCIM configuration with the new token.

Groups with an active SCIM link display an IdP-managed badge. Members in IdP-managed groups should not be adjusted manually.

The Auth Methods tab controls which authentication mechanisms are available:

Method Description
Local (local) Username and password stored in Arbitex
SAML 2.0 (saml) Federated login via a configured SAML IdP
OIDC / OAuth (oidc) Federated login via an OpenID Connect provider

At least one method must remain enabled.

Enabling SSO-only mode removes the local login form from the sign-in page and forces all users through a federated provider:

  1. Enable at least one of SAML 2.0 or OIDC / OAuth.
  2. Toggle SSO-only mode on.
  3. Click Save.

To configure via the API:

Terminal window
PUT /api/v1/admin/config/auth_methods_config
Authorization: Bearer <admin_token>
Content-Type: application/json
{
"allowed_methods": ["saml", "oidc"],
"sso_only_mode": true
}

The SSO Test tab runs a dry-run validation of a SAML IdP configuration without requiring a real user login:

  1. Navigate to Admin → SSO → SSO Test.
  2. Select the IdP from the dropdown.
  3. Click Run Test.

The test posts to POST /api/v1/admin/saml/test and returns a per-check result panel. A successful test shows a green header and a checklist of passed checks. A failed test shows a red header with failing checks and error messages.

Common checks:

Check What it validates
Metadata reachable The IdP’s metadata URL responds with valid XML
Certificate valid The X.509 certificate is not expired and parses correctly
SSO URL reachable The SSO endpoint returns an expected response code
Entity ID matches The entity ID in the IdP’s metadata matches what was configured
Terminal window
# API equivalent
POST /api/v1/admin/saml/test
{ "idp_id": "<idp-uuid>" }

Arbitex supports SCIM 2.0 for automated user and group provisioning. SCIM authentication uses a per-org bearer token stored as a bcrypt hash.

  • 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.
Terminal window
POST /api/v1/scim/token/rotate
Authorization: Bearer <admin_token>

The token is rotated for the org that owns <admin_token>. The target org is derived from the credential — there is no org identifier in the path, body, or headers.

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.

After rotation, update the SCIM provisioning configuration in your IdP with the new token. The previous token is invalidated at the time of rotation — any provisioning requests using the old token return 401 Unauthorized immediately.

Endpoint Method Description
/scim/v2/Users GET List users
/scim/v2/Users/{user_id} GET Get a single user
/scim/v2/Users POST Create a user
/scim/v2/Users/{user_id} PUT Replace user attributes
/scim/v2/Users/{user_id} PATCH Partial user update
/scim/v2/Users/{user_id} DELETE Deactivate a user (soft delete)
/scim/v2/Groups GET List groups
/scim/v2/Groups/{group_id} GET Get a single group
/scim/v2/Groups POST Create a group
/scim/v2/Groups/{group_id} PUT Replace group attributes
/scim/v2/Groups/{group_id} PATCH Partial group update
/scim/v2/Groups/{group_id} DELETE Delete 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 the URN urn:ietf:params:scim:schemas:extension:arbitex:2.0:Group. Your IdP can discover the schema via GET /scim/v2/Schemas.

Attribute Type Description
dlpPolicy string DLP policy ID to apply to the group
modelAccess string Model access configuration
complianceBundles list Compliance bundle IDs to assign
quotas object Per-group usage quotas

Organization administrators can require MFA for all portal users, independent of SSO configuration. MFA enforcement applies to all authentication methods including SSO.

Navigate to Settings → Security → Multi-Factor Authentication and toggle Require MFA for all users.

When enabled:

  • Users without an enrolled MFA method are prompted to set up MFA on their next login.
  • Users who dismiss MFA enrollment are blocked from accessing the portal until they complete enrollment.

When first enabling MFA enforcement, set a grace period to give existing users time to enroll. During the grace period, users without MFA are allowed to log in but see a persistent enrollment reminder. After the grace period expires, unenrolled users are blocked.

If a user loses access to their MFA device, an org admin can navigate to Settings → Members → [user] → Security and click Reset MFA to clear the user’s enrolled methods. The user is prompted to re-enroll on their next login. Admin-initiated MFA resets are recorded in the audit log with the action mfa.enrollment_reset.