Skip to content

SSO & Entra ID

Monozu Cloud integrates with Microsoft Entra ID (formerly Azure AD) for single sign-on. There are two distinct integration modes: a platform-level integration for Cloud SPA users, and a per-tenant OIDC configuration that tenants can set up in Settings.

Per-tenant Azure AD OIDC now shares its storage table, account-linking safety mechanism, group sync, enforcement, and owner break-glass path with the SAML 2.0 integration — see SSO & SAML 2.0 for the parts that are common to both providers. This page covers the Entra-specific flow.

The Cloud Backend registers a single multi-tenant Entra ID app (/common endpoint) for the “Sign in with Microsoft” button on the login page.

Flow:

sequenceDiagram
    participant Browser
    participant SPA as Cloud SPA
    participant API as Cloud Backend
    participant Entra as Microsoft Entra ID

    Browser->>SPA: Click "Sign in with Microsoft"
    SPA->>API: GET /api/v1/auth/microsoft/login
    API-->>Browser: 302 Redirect to Entra authorization URL
    Browser->>Entra: Authorization request (PKCE)
    Entra-->>Browser: Redirect to {API_PUBLIC_URL}/api/v1/auth/microsoft/callback?code=...
    Browser->>API: GET /api/v1/auth/microsoft/callback?code=...
    API->>Entra: Token exchange (code → ID token + access token)
    Entra-->>API: ID token { email, name, oid }
    API->>API: Find or create user by email, issue JWT pair
    API-->>Browser: Redirect to {APP_BASE_URL}/auth/callback?token=<access_token>
    Browser->>SPA: /auth/callback — store access_token

Required env vars (Cloud Backend):

VariableDescription
MS_CLIENT_IDEntra ID application (client) ID
MS_CLIENT_SECRETClient secret
API_PUBLIC_URLMust match the redirect URI registered in Entra

The redirect URI registered in Entra must be exactly:

{API_PUBLIC_URL}/api/v1/auth/microsoft/callback

Each tenant can configure their own Entra ID app registration in Settings → SSO — a real, self-service admin UI (not a future aspiration; it ships in the same project that added SAML support). This allows tenant users to log in with their corporate identity.

The per-tenant configuration is stored in the tenant_sso_providers table, with provider = 'azure_ad'. This table replaced the older tenant_auth_settings table (which only ever held a single Azure AD OIDC config per tenant, with no room for a second provider) in db/migration/V58__saml_sso.sqltenant_sso_providers is shared with the SAML 2.0 integration (provider = 'saml'), with a UNIQUE (tenant_id, provider) constraint allowing at most one row per provider per tenant. See SSO & SAML 2.0 — Architecture for the full column reference.

Unlike the SAML provider config API, this project does not expose a generic /settings/sso/providers create/update path for azure_ad rows — Azure AD configuration is created via /auth/register/azure-ad during self-service tenant registration. The GET /settings/sso/providers list endpoint does return any existing azure_ad row alongside SAML providers, for visibility.

Tenant resolution at login time is domain-based: POST /api/v1/auth/check-method accepts either an explicit tenant slug or just an email address, in which case the tenant is resolved via the email’s domain against tenant_sso_domains (a domain must be DNS-TXT-verified before it can route a login this way).

Required tenant configuration (stored in tenant_sso_providers, managed via Settings → SSO):

  • Client ID (oidc_client_id)
  • Client Secret (oidc_client_secret_enc, encrypted at rest)
  • Tenant ID / Entra Directory ID (oidc_directory_id)
  • Redirect URI (same {API_PUBLIC_URL}/api/v1/auth/microsoft/callback pattern)

Dual-provider precedence and shared safety mechanisms

Section titled “Dual-provider precedence and shared safety mechanisms”

If a tenant configures both azure_ad and saml, and a user’s account hasn’t yet completed either SSO flow, check-method picks SAML by a fixed precedence rule (not configuration recency) — see SSO & SAML 2.0 — Dual-provider precedence for the full reasoning.

The account-linking safety mechanism — matching by external_id first, falling back to an admin-approved pending-link flow rather than trusting email alone — is shared code (SSOProvisioner.MatchExisting/JITProvision in internal/domain/auth/provisioning.go) used by this OIDC flow, the SAML flow, and platform Microsoft OAuth alike. See SSO & SAML 2.0 — Login flow for the details.

Enforce SSO (tenants.enforce_sso, blocking local password login tenant-wide except for the owner) and the owner’s audited break-glass exception are also provider-agnostic — once enabled, they block local login regardless of whether the tenant uses Azure AD OIDC, SAML, or both. See SSO & SAML 2.0 — Enforce SSO.

AUTH_POLICYEmail/passwordPlatform SSOPer-tenant OIDC
standard
restricted

Under restricted policy, the AUTH_ALLOWED_EMAIL_DOMAINS env var must be set. Users with non-matching email domains are rejected at the OIDC callback.

The Management Backend has its own independent Entra ID app registration, used for management staff authentication. Its env vars follow the same pattern:

VariableDescription
MS_CLIENT_IDManagement app registration client ID
MS_CLIENT_SECRETClient secret
MS_TENANT_IDThe Entra tenant hosting the management app
API_PUBLIC_URLManagement Backend public URL (for redirect URI)

The redirect URI for Management must match:

{API_PUBLIC_URL}/api/v1/auth/microsoft/callback

Note that this is the Management backend URL, not the Management SPA URL.

Microsoft and Entra ID redirects include large Cookie headers. The default Fiber/fasthttp read buffer of 4096 bytes can trigger HTTP 431 Request Header Fields Too Large. The Cloud Backend defaults to 65536 bytes:

HTTP_READ_BUFFER_BYTES=65536
HTTP_WRITE_BUFFER_BYTES=65536

Do not reduce these values below ~16384 in production.