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.
Platform SSO (Cloud Backend)
Section titled “Platform SSO (Cloud Backend)”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):
| Variable | Description |
|---|---|
MS_CLIENT_ID | Entra ID application (client) ID |
MS_CLIENT_SECRET | Client secret |
API_PUBLIC_URL | Must match the redirect URI registered in Entra |
The redirect URI registered in Entra must be exactly:
{API_PUBLIC_URL}/api/v1/auth/microsoft/callbackPer-tenant OIDC
Section titled “Per-tenant OIDC”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.sql — tenant_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/callbackpattern)
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 policy interaction
Section titled “Auth policy interaction”AUTH_POLICY | Email/password | Platform SSO | Per-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.
Management Backend OIDC
Section titled “Management Backend OIDC”The Management Backend has its own independent Entra ID app registration, used for management staff authentication. Its env vars follow the same pattern:
| Variable | Description |
|---|---|
MS_CLIENT_ID | Management app registration client ID |
MS_CLIENT_SECRET | Client secret |
MS_TENANT_ID | The Entra tenant hosting the management app |
API_PUBLIC_URL | Management Backend public URL (for redirect URI) |
The redirect URI for Management must match:
{API_PUBLIC_URL}/api/v1/auth/microsoft/callbackNote that this is the Management backend URL, not the Management SPA URL.
HTTP buffer size note
Section titled “HTTP buffer size note”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=65536HTTP_WRITE_BUFFER_BYTES=65536Do not reduce these values below ~16384 in production.