Federating Microsoft 365 with Cloudflare Zero Trust (SAML)

Jack Beaman

 

Microsoft Entra ID (formerly Azure AD) is a cloud-based identity and access management solution that natively supports SAML federation but, if you’re not using Microsoft as your Identity Provider (IdP), official guidance quickly runs thin. 

At Shopify, and across many modern SaaS environments, identity is often centralized in platforms like Google Workspace, while Cloudflare Zero Trust is used as the control plane for authentication, access policies, and MFA enforcement. This guide documents a safe, production-tested approach to federating a Microsoft 365 domain using Cloudflare Access as the SAML Identity Provider, regardless of the upstream IdP (Google Workspace, Okta, etc.).

"Think of a traveler wishing to come from their home country to the USA. When they arrive at the border, they are asked for their passport in order to authenticate and possibly authorize their access. If the traveler has no passport, they are redirected to their home country's government to get one." -- Auth0

This post focuses as much on guardrails and recovery paths as it does on configuration.


 

Architecture Overview

 

This authentication flow describes a chained SAML federation where Cloudflare Access acts as a "bridge" or middleman between Microsoft 365 and your primary identity source. Cloudflare serves as the SAML Identity Provider (IdP) for Microsoft 365 while simultaneously acting as a Service Provider (SP) for your upstream IdP (like Google or Okta). 


Authentication Flow

User → Microsoft 365 → Cloudflare Access (SAML IdP)
     → Upstream IdP (Google Workspace, Okta, etc.)
     → Cloudflare MFA / Policies
     → Microsoft Entra ID


The Step-by-Step Flow


  1. Request Initiation (User → Microsoft 365):
    The user attempts to log into a Microsoft 365 service (e.g., Outlook or Teams). Microsoft Entra ID recognizes the user's domain as "federated" and redirects the browser to the configured SAML IdP—in this case, the Cloudflare Team Domain.

  2. Primary Authentication (Cloudflare → Upstream IdP):
    Cloudflare Access receives the request but does not yet know the user's identity. It redirects the user to the Upstream IdP (e.g., Google Workspace or Okta). The user authenticates there using their standard corporate credentials.

  3. Security Enforcement (Cloudflare MFA / Policies):
    Once the upstream IdP confirms the user's identity, the flow returns to Cloudflare. Before passing the user back to Microsoft, Cloudflare applies its Zero Trust policies. This may include:
    • MFA Challenges: Requiring a hardware key (FIDO2) or mobile push even if the upstream IdP didn't.
    • Device Posture: Checking if the device is managed, has an active antivirus, or is at a specific version.
    • Network Context: Verifying the request is coming from a permitted country or IP range.

  4. Final Assertion (Cloudflare → Microsoft Entra ID):
    After all policies are satisfied, Cloudflare generates a SAML assertion signed with its private key. This assertion is sent back to Microsoft Entra ID, which trusts the "stamp of approval" from Cloudflare. Entra ID then issues the final session token to the user for Microsoft 365 access.

 

Why use this flow?

  • Centralized Control Plane: You can enforce strict Zero Trust rules across all Microsoft apps without migrating your entire identity away from Google or Okta.
  • Enhanced MFA: It allows you to use Cloudflare’s modern MFA (like FIDO2) for Microsoft apps, even if your legacy IdP or Entra ID tier doesn't support it easily.
  • Conditional Access: You can block access based on real-time Cloudflare intelligence (e.g., known malicious IPs) before the user even reaches the Microsoft login screen.

 


Phase 0: Should You Even Do This?

Before touching anything, answer these questions:

 

Decision Tree: Is Federation Appropriate?

Do you want Microsoft to control MFA and sign-in policy?

→ Yes → ❌ Do not federate

→ No → Continue


Do you already use Cloudflare Zero Trust for workforce access?

→ No → ❌ Federation adds complexity

→ Yes → Continue


Do you rely on Azure AD Connect or hybrid sync?

→ Yes → ⚠️ Federation may break sync

→ No → Continue


Do you have at least one non-federated admin account (two is recommended)?

→ No → ❌ STOP

→ Yes → Safe to proceed


Phase 1: Prerequisites & Health Checks



1. Create a Break-Glass Admin Account (Mandatory)

Before federating anything, ensure you have at least one (TWO!) Global Administrator that uses the *.onmicrosoft.com domain, is not federated and has a long password stored offline that is not MFA dependent. If SAML breaks, Cloudflare is down, or certificates expire, this account is your only recovery path.

 

2. Cloudflare Zero Trust Requirements

You must have Cloudflare Access enabled with at least one upstream IdP configured (Google Workspace, Okta, etc.) with access to create SaaS applications. Most Zero Trust plans support this. Free plans work for testing but are not recommended for production.


3. Understand Microsoft’s ImmutableId (Critical)

Microsoft Entra uses a value called onPremisesImmutableId to bind a user account to a federated identity. Rule: The SAML NameID must exactly match the user’s ImmutableId.

In this guide, we intentionally choose:

NameID = user email (UPN)
ImmutableId = user email (UPN)

This is simple, predictable, and works well for cloud-only tenants.


4. Audit Existing ImmutableId Values

Connect to Microsoft Graph:

Connect-MgGraph -TenantId "YOUR_TENANT_ID" `
  -Scopes "User.Read.All" `
  -UseDeviceAuthentication

Check your users:

Get-MgUser -All -Property OnPremisesImmutableId, UserPrincipalName |
Where-Object { $_.UserPrincipalName -like "*@yourdomain.com" } |
Select UserPrincipalName, OnPremisesImmutableId

Decision Tree: Can You Proceed?

ImmutableId matches UPN → Proceed

ImmutableId is blank → Legacy cleanup required

ImmutableId is different → Legacy cleanup required


If you ignore this, users will hit:

AADSTS51004: The user account does not exist in the directory


5. Confirm Domain Authentication Type

Your domain must be Managed before federation:

Get-MgDomain -DomainId "yourdomain.com" |
Select AuthenticationType

If it’s already federated, stop and investigate before proceeding.

 


Phase 2: Configure Cloudflare Access (SaaS App)


  1. Go to Cloudflare One Dashboard
  2. Access → Applications → Add Application
  3. Choose SaaS
  4. Select Microsoft
  5. Authentication protocol: SAML


Required SAML Values

Setting

Value

Entity ID

urn:federation:MicrosoftOnline

ACS URL

https://login.microsoftonline.com/login.srf

NameID Format

EmailAddress

 

SAML Transformation (JSONata) - Only the NameID matters to Microsoft. Extra claims are ignored.

$merge([$, {"userPrincipalName": email}])

 

Save These Values (You’ll Need Them)

 

  • Issuer URI
  • SSO Endpoint
  • Signing Certificate (Base64)



Phase 3: Federate the Domain (Microsoft Graph)

Connect to Graph:

 

Connect-MgGraph -TenantId "YOUR_TENANT_ID" `
  -Scopes "Domain.ReadWrite.All" `
  -UseDeviceAuthentication

Apply federation:

$domainName = "yourdomain.com"
$issuerUri = "CLOUDFLARE_ISSUER"
$ssoUrl    = "CLOUDFLARE_SSO_URL"
$cert      = "CLOUDFLARE_PUBLIC_CERT"

New-MgDomainFederationConfiguration `
  -DomainId $domainName `
  -DisplayName "CloudflareZeroTrust" `
  -IssuerUri $issuerUri `
  -ActiveSignInUri $ssoUrl `
  -PassiveSignInUri $ssoUrl `
  -SigningCertificate $cert `
  -PreferredAuthenticationProtocol saml `
  -FederatedIdpMfaBehavior acceptIfMfaDoneByFederatedIdp `
  -PromptLoginBehavior nativeSupport

Verify:

Get-MgDomain -DomainId $domainName |
Select AuthenticationType

Expected output: Federated


Phase 4: Testing & Troubleshooting

Test with Domain Hint - You shuold be redirected to Cloudflare.

https://login.microsoftonline.com/?domain_hint=yourdomain.com

Avoiding Double MFA

If users see Microsoft MFA after Cloudflare MFA, make sure to disable Security Defaults, use Conditional Access instead and ensure CA policies do not re-challenge federated users.

 

Emergency Rollback (Know This Before You Start) - Log in using your break-glass account.

$domainName = "yourdomain.com"
$fedId = (Get-MgDomainFederationConfiguration -DomainId $domainName).Id

Remove-MgDomainFederationConfiguration `
  -DomainId $domainName `
  -InternalDomainFederationId $fedId

Update-MgDomain `
  -DomainId $domainName `
  -AuthenticationType "Managed"

 

 

Legacy Cleanup (Pre-Federation Only)

This is the most dangerous step in the entire process. This is required when Users have blank or mismatched ImmutableId, the Tenant is cloud-only and the domain is still Managed.

When You Must NOT Do This:

  • Hybrid tenants
  • Azure AD Connect
  • Synced users
  • Unscoped bulk operations

 

Safe Bulk Update (Scoped)

Connect-MgGraph -TenantId "YOUR_TENANT_ID" `
  -Scopes "User.ReadWrite.All" `
  -UseDeviceAuthentication

Get-MgUser -All |
Where-Object {
  $_.UserPrincipalName -like "*@yourdomain.com" -and
  $_.UserType -eq "Member"
} |
ForEach-Object {
  Invoke-MgGraphRequest -Method PATCH `
    -Uri "https://graph.microsoft.com/v1.0/users/$($_.Id)" `
    -Body @{ onPremisesImmutableId = $_.UserPrincipalName }

  Write-Host "Updated: $($_.UserPrincipalName)"
}

 


“Don’t Brick Your Tenant” Checklist

I suppose if you've read this far and have decided to proceed, make sure to run through this checklist before moving along any further. It probably would have been best to put this in the beginning of the article, but here we are. Configuring network architecture on the fly is rarely a good idea, and ensuring your identity provider and firewall rules are aligned now will save you from an accidental lockout once we hit 'deploy.' Let’s verify the fundamentals before we get into the complex routing.

Before clicking Run:

☐ At least one onmicrosoft.com Global Admin

☐ Domain is Managed

☐ ImmutableId audit completed

☐ No Azure AD Connect

☐ Test users validated

☐ Federation rollback commands saved

☐ Cloudflare cert expiration tracked

☐ Change window approved

☐ Login tested via domain_hint

☐ Break-glass credentials verified


If any box is unchecked — stop.

 

 

Federating Microsoft 365 with Cloudflare Zero Trust is powerful. It centralizes identity, enforces consistent MFA, and removes Microsoft as the policy authority. But it is not forgiving. Treat this like a schema migration for identity. Test it. Stage it. Guard it. And always keep a way back in.

If you don’t, just remember Microsoft won’t be there to help you.

Back to blog

Leave a comment

Please note, comments need to be approved before they are published.