If you’ve ever tried to let EntraID (Azure AD) guest users sign in to an Okta-protected app via the built-in Microsoft IdP, you’ve probably hit a wall. The login flow either drops the user on a personal Microsoft account screen or fails outright with a confusing error.
The root cause is simple: guest accounts (B2B) live in a specific tenant, but Okta’s default Microsoft IdP talks to the common endpoint, which assumes the user can be discovered globally. Here are the notes I keep handy when wiring this up.
Why the default Microsoft IdP fails for guests #
Okta’s out-of-the-box Microsoft IdP is pre-configured against the multi-tenant endpoint:
https://login.microsoftonline.com/common/oauth2/v2.0/authorizeThe /common endpoint relies on Microsoft’s home realm discovery: it inspects the email entered by the user and routes the authentication to the tenant where that identity lives. For a guest, the email (alice@example.com) resolves to a personal Microsoft account namespace, not the inviting tenant. The result is the classic error:
You can’t sign in here with a personal account. Use your work or school account instead.
…unless the user happens to also own a personal Microsoft Account with that email — in which case they’ll authenticate against the wrong identity and Okta will receive a token that has no relationship with your tenant.
Fix: use a tenant-specific Custom OIDC IdP #
The solution is to bypass /common and pin the authentication to your tenant by using its tenant ID (a GUID, found in EntraID → Overview). This forces Microsoft to authenticate the user against your directory, where the guest object actually exists.
In Okta, don’t edit the built-in Microsoft IdP — create a new OIDC Identity Provider (Security → Identity Providers → Add → OpenID Connect IdP) with the following endpoints, replacing [MS_TENANT_ID] with your tenant GUID:
| Setting | Value |
|---|---|
| Issuer | https://login.microsoftonline.com/[MS_TENANT_ID]/v2.0 |
| Authorization endpoint | https://login.microsoftonline.com/[MS_TENANT_ID]/oauth2/v2.0/authorize |
| Token endpoint | https://login.microsoftonline.com/[MS_TENANT_ID]/oauth2/v2.0/token |
| JWKS endpoint | https://login.microsoftonline.com/[MS_TENANT_ID]/discovery/v2.0/keys |
| Userinfo endpoint | https://graph.microsoft.com/oidc/userinfo |
| Scopes | openid profile email https://graph.microsoft.com/User.Read |
You’ll also need an App Registration in EntraID (Application type: Web) with:
- The Okta redirect URI (
https://<your-okta-domain>/oauth2/v1/authorize/callback) added under Authentication → Redirect URIs. - A client secret generated under Certificates & secrets.
- The delegated permissions
openid,profile,email, andUser.Readgranted under API permissions.
Understand the guest username format #
When an external user (e.g. alice@example.com) is invited as a guest into an EntraID tenant, Microsoft does not store their email as the UPN. Instead it generates an internal identifier:
alice_example.com#EXT#@contoso.onmicrosoft.comThis matters because:
- Microsoft’s
preferred_username/upnclaim will return that mangled string, notalice@example.com. - If you want Okta to match the user by email, you must map the IdP’s
emailclaim (notsuborupn) to Okta’s username/email attribute in the IdP profile mapping. - JIT provisioning rules, group rules, and policy conditions should be written against the
emailclaim too, otherwise you’ll end up with users whose Okta username looks likealice_example.com#ext#@contoso.onmicrosoft.com.
Always inspect the ID token returned by Microsoft (e.g. via the Okta System Log → “Authentication of user via IDP” event) before finalizing the mapping. Different tenants and consent scopes return slightly different claim sets.
The User.Read scope is what allows Okta to call https://graph.microsoft.com/oidc/userinfo and retrieve the guest’s actual email address rather than the #EXT# UPN.