Understanding the authentication flow for Microsoft Entra is essential when working with IAM in a Microsoft environment. Like the philosopher Sir Francis Bacon once said, Knowledge is power—and in this case, it is what allows us to make informed decisions to best secure our users, businesses, and, ultimately, our bosses' wallets.
Much like Neo needed to decode the Matrix to navigate its complexities, mastering the authentication flow in Microsoft Entra gives you the edge in securing your environment.
Today, we’ll explore how Conditional Access policies interact with Entra through API calls behind the scenes—empowering you to optimize your authentication strategy.
Table of content:
Plugging In: Preparing to Uncover the Microsoft Entra Authentication Flow
Jumping straight into the authentication flow API calls without a life vest can seem daunting. Don't worry—I'll be playing the role of Tank, overlooking the dive and making sure we stay safe from any Sentinels.
To kick off the authentication flow, I’ll be accessing the Azure Portal at portal.azure.com (AppID: c44b4083-3bb0-49c1-b47d-974e53cbdf3c). While I'll be masking the IP, everything else you’ll see are the exact requests and responses from the flow.
Authentication: START
When an authentication flow begins, the first step is sending a payload to Microsoft Entra Authentication Services, containing various pieces of information.
To follow along, the payload is sent to the endpoint: https://login.microsoftonline.com/common/GetCredentialType?mkt=en
This is the payload sent by my authentication attempt:
checkPhones: true
country: "DK"
federationFlags: 0
flowToken: "AQABIQEAAADW6jl31mB3T7ugrWTT8pFea9QmjeKhyeYuUjZEp1kBtW9Lw8OpsYZN989CN9d_xokXRTfo7oKEF95_gV6CUR5q2y4IGZzU2O3D7ViBvLmcWqKWsL1nSKR-qbxzsXJpY6dG2fofCchYHGZ6HVBAq1ch_CdebwU_MUv3P91j2CCsR3wEQkQSzTES5UFlFXd1r4Hcq6m7k2dl0XBM6oAVHcdncABR_aVZIMaoCHmiJ06DC__yxaRt_GRFvHp8aOE37pahHYj7rYvyNGQUyxTl9uTXpi3ybf8EDIypR3UYfabTN0USa4Rzbs8XZkzbUWHVkGRCs4ddrK_-nzqu0Xw9a_Q3-pV4gY6ullbQSZtkmFwhJTTTdswAwbnKVw3dYOrq_9qaPQREDe6ygEcptgbLN-kyNsRA3Ii2xmtK7EDisjUfwZNc15eEYyJGF2fAVO0Vvk7gZg7rEvCbElL-9EXlJJ4ZzzorHMtN-kAmKI_aF83ORlzRv5CVuzD4J3pBlYpJw1BobNGJc0sP_Hm-Ue5CFq8DjQtpZdIsNKAQUcIzL-4GRyAA"
forceotclogin: false
isAccessPassSupported: true
isCookieBannerShown: false
isExternalFederationDisallowed: false
isFidoSupported: true
isOtherIdpSupported: true
isQrCodePinSupported: true
isRemoteConnectSupported: false
isRemoteNGCSupported: true
isSignup: false
originalRequest: "rQQIARAAjdNLiON0HAfwdh6dB6wziMge57A-ENP-02SSdGCEpknTdCavTtqkESx5J21eTdK0zcWrF3GZkwhePM5pVRBZPOxFkDnInr3oTZnDIoKw4sXugve9_Pgeft_b53u4h9dP66AO3ttu1sHZAxNFDRQQCIQYBoDQlglDBopbUAtH7VPENCwHMfu1UwCjCEhfPzze_v3n7K1nHzKf9v_-4OuP_z2-qb7t5XmSnTUaSZzmelDXy0Vq1804bGS-G_lRw48se9X4vlp9Wq3-Ua3ebGUYQmAogoEmIAgCh8EpWuea7EqQtYCT2ZwvTSB0AOCVgXep0EuudPNNDsYhCzSGDznZRbmp2xQY3tems5yjtHDsA6DJLnypsEBgpJxTNG9cSoCj2oCXZ-UvW0dCe5F7zRcnTv3S_mvrwInTcJLEWf7F9vWOkNgRa3XiKLLNvP7izY5y39RzP47ENE7sNPft7JycXGKtPMOduWG1MZiEteJiOuRmiorLDNDXYO6KS9s3SaRA07QD2b3skpT1voJP-aFkxvpoSgxcBBKBNrawqDMTYGEUOFlz4nkQ3YF5mvfWy6K1WiXz02JK9QwaSfpTEI4y0l50_ZU0HcZeXw6ynkzDeG-FJQQbo-RSVNYzzpFVT05QUXSNcKalA4gTGTJisXa7rXqmyK9TJCsDBEHkllA4UDqf4W4473YZlNCHUDKz_H6IOAlBtXtLvDQwV7fZLMbgzjLtKH1iPNRZORYhXe0RbbUZIFHUtIzcWw8hZYC0u6x5xbOjVWtA20Zh8xNAMWp33SuyYrFkOGilILpz5U0vBJCDheX0aKolALVwO6rnlxwqpJYtpfDyogj7-TrreC122Olj9pqXBmruk6M5k6ohZWhagUgi6Vxlc0Uiu4u0rZTwROAdUmLQFWfyijgXgxaLUoAq_BSDqGZzMUzWvavhKWFd0J4_p1MjKiUFK9ZaP2CpLjZeyFwpk7DuoQIOeF4LpJvtB__bDvVId-1wI2Ije8N7uUEdL7N6ZOeNb7bvxamrR3750kp2u335KrXGIrPTiR9uYGVx9LJ6slEW-daJHep-cJKkseMH9tOd6t3Om_t7x7v3KyeVd98Au2f7-4fHR_f3TirPd6pf7W5WWZL_vPPdR19Sn3_7w48_PcIrt7sNjsxZlJ4puLvmSG_JSDADiunVhUDra8XlRmIeXhjmVRKMs3PiDH5Yqz6s1W5rByw14WkZxZt_1qqf7FUeH7zivp_cqzx_7cn13fVnvz5-1rs7ep-77IrTruuovD4PF9NIkdr9hWpZA3rRagO0xEbd5VCVpcZweP7ouPLbceU_0"
username: "admin@m365x88629914.onmicrosoft.com"
I'll explain the different fields in this matrix
Field | Description |
checkPhones | Indicates whether the system should check for phone authentication options (e.g., SMS or Voice calls). |
country | Indicates the Entra Identity country, not the user's current location. |
federationFlags | Shows federation settings with an integer. 0 indicates no federations, such as external IdP, SSO, or ADFS. |
flowToken | Session token used to track and authenticate the ongoing flow. Passed between the client and Entra to ensure session security. |
forceotclogin | Indicates whether the user is being forced to use a One-Time Code (OTC) login. |
isAccessPassSupported | Indicates that the Access Pass authentication method is supported. |
isCookieBannerShown | Indicates whether the cookie consent banner was shown to the user during the login session. |
isExternalFederationDisallowed | Indicates whether external Identity Providers (IdPs) are disallowed. |
isFidoSupported | Indicates whether FIDO authentication methods (e.g., security keys, biometric authentication) are supported. |
isOtherIdpSupported | Indicates whether 'Other' IdPs (e.g., Google, Facebook, third-party services) are supported. |
isQrCodePinSupported | Indicates that QR code-based authentication using a PIN is supported (e.g., for signing in via mobile or external devices). |
isRemoteConnectSupported | Indicates whether Remote Connect is supported for the session (e.g., resource access via Windows Hello for Business). |
isRemoteNGCSupported | Indicates that Next Generation Credentials (NGC) are supported remotely, such as biometric authentication or PINs for security. |
isSignup | Indicates whether user sign-up (account creation) is allowed. |
originalRequest | Encoded request string containing details about the original sign-in request, including session parameters and processing data. |
username | The username of the user attempting to sign in. |
Authentication: Response
The response provides information that will be processed further during the next steps of the authentication flow. This response is what determines the type of authentication needed, such as whether a password, Authenticator app, or FIDO security key is required.
Here’s the response received from the same endpoint:
{
"Username": "admin@m365x88629914.onmicrosoft.com",
"Display": "admin@m365x88629914.onmicrosoft.com",
"IfExistsResult": 0,
"IsUnmanaged": false,
"ThrottleStatus": 0,
"Credentials": {
"PrefCredential": 2,
"HasPassword": true,
"HasRemoteNGC": true,
"RemoteNgcParams": {
"SessionIdentifier": "GAQABIQEAAADW6jl31mB3T7ugrWTT8pFeToAntP0kOkivVKZgrpIOVJNdeJADouBEtU1M7Plf0mLMg0Fp3iuPXqoBA8WWZL8SsRzwQei2FYdPtJxNMBsX9X8O_2KN4crWZnwmlxW63ui4_gfILVNIuCJJ_4QuWZuJ2SNEkoGj-RGzJdFzUomWPtR42uoGyuTG1gvPicuy85856VxRcJ0gABnM1cxkixrjpxLeR6u-MJQpWDqFS5XEHVXaRGrn9oSyCok7hkvjYYvY3Ba-MF2d34RLTWEXkrJOBPIg3p_dvLqCp32kZ5GfkRswqll860XSGCaMQCtgbOTU72mxsx5Ovw9Ww3F0Ffgd139Ux5KDPJ0FNBQpzKCL-dSSL1spz1MWqUgMilbveJBZWhlnXkdkP_hi3TBz1AxtKm3Gdjlfhazq_fiLx9D1haZol2ODZp732mlRschCP3ZAlJOvHzQguvlGLWr3ZNI6yoeHDYVGK2l-Esq_Xvb036hZMDG0vfgkf_x7PdZIF1AgAA",
"Entropy": 34,
"DefaultType": 1,
"Application": "c44b4083-3bb0-49c1-b47d-974e53cbdf3c"
},
"FidoParams": null,
"QrCodePinParams": null,
"SasParams": null,
"CertAuthParams": null,
"GoogleParams": null,
"FacebookParams": null,
"OtcNotAutoSent": false
},
"DfpProperties": {},
"EstsProperties": {
"UserTenantBranding": [
{
"Locale": 0,
"BannerLogo": "https://aadcdn.msftauthimages.net/c1c6b6c8-ghxaruhvyyzmslo-umhkd-ewsyqz5bsqultsxlwuudu/logintenantbranding/0/bannerlogo?ts=638635885340469186",
"TileLogo": "https://aadcdn.msftauthimages.net/c1c6b6c8-ghxaruhvyyzmslo-umhkd-ewsyqz5bsqultsxlwuudu/logintenantbranding/0/tilelogo?ts=638635885344693590",
"TileDarkLogo": "https://aadcdn.msftauthimages.net/c1c6b6c8-ghxaruhvyyzmslo-umhkd-ewsyqz5bsqultsxlwuudu/logintenantbranding/0/squarelogodark?ts=638635885349869441",
"Illustration": "https://aadcdn.msftauthimages.net/c1c6b6c8-ghxaruhvyyzmslo-umhkd-ewsyqz5bsqultsxlwuudu/logintenantbranding/0/illustration?ts=638635885335589029",
"BoilerPlateText": "<p>Contoso</p>\n",
"KeepMeSignedInDisabled": false,
"UseTransparentLightBox": false
}
],
"DomainType": 3
},
"FlowToken": "AQABIQEAAADW6jl31mB3T7ugrWTT8pFed2LOf_Lio8EsJp64lq0hOH-TTaZfVTCL4skZBAlXupwYCeCGIDmM0jRSqyJtQQDgWFYNC_ChOTOxlXmjqd6jDgprCR53QM1j_qT8Nz_0zuqZ2X4Il_qo3dOunoHUXOAXfjQL_HVL26LyFFHjblPdZV7DmTfZZBTULAcuIDj5Dky7N-Gr7otVdBxcdUFckkiLkOofVFBCQGeA2vMidayMv_YTFZPKse9zfV5e2IvA2b-_ZHcMJcIw0NrCDoE5IQffZVuAX3zh8uQ68C4pqO3OtuQcWBxwMqf7ZkjpkERxOCL3lV52r9TaPgI8cJR_Gyo6euDaq1USzmTUbG3lI1tqqUfJtdr4dxwrnqzwH4vbaaJ6EMmXsn3TbIA4uWzHJaIUGpUvHfMdx7R4n0QwcmQjCwT4YiobkV6k_idVZsSSNh60LxVELV8wD3YWGxO9sV_qRiM1Q-b_3GvFketYQkP90hhfVOBEBRiLjwQJca55ejiJdxMTIG708XmASl23ZO_afsM6ZjGyCrf-6SEtWeRAstEBHigdZf9sQmIcvORHcgMyP6OvccpgnGYzfSeFK_CqIAA",
"IsSignupDisallowed": true,
"apiCanary": "PAQABDgEAAADW6jl31mB3T7ugrWTT8pFeRLeVeWkvqCistNFs5W3OQsXWyGGcTwPEfdZ8RAYRXCv9wOfM_F6tR3Od1eEI1bdGBOQztP-UBLFTrAW-N7Sz3UicpGOFdmEKya8e8BEBKVaQQ9D9Qe0j7j9XpSpfSOJYX5B8NkQRAd2_Tu_PRNk32YUKH2isCBTRR6CIUDfpxDDmPxjEnyqVDVPcxNfm1MGGLLDIkIqqxwoZOfVaaH6AtiAA"
}
Once again I'll give a quick explanation for the different fields
Field | Description |
Username | The username of the user who initiated the request. |
Display | The display name, typically the same as the username in this context. |
IfExistsResult | Indicates the result of checking whether the user exists (0 means the user exists). |
IsUnmanaged | Indicates whether the identity is part of a managed Entra tenant (corporate) or unmanaged (personal). |
ThrottleStatus | |
Credentials.PrefCredential | Preferred credential type for the user (e.g., password, biometric, etc.). |
Credentials.HasPassword | Indicates whether the user has a password set for their account. |
Credentials.HasRemoteNGC | Indicates whether the user has Next Generation Credentials (NGC) like Windows Hello for Business. |
Credentials.RemoteNgcParams.SessionIdentifier | Unique identifier for the remote session using NGC. |
Credentials.RemoteNgcParams.Entropy | Entropy value for generating the NGC, ensuring security by introducing randomness. |
Credentials.RemoteNgcParams.DefaultType | The default type of credential, typically used to specify the primary credential type. |
Credentials.RemoteNgcParams.Application | Application ID associated with the NGC session. |
FidoParams | Parameters for FIDO authentication (null if not configured). |
QrCodePinParams | Parameters for QR code-based PIN authentication (null if not configured). |
SasParams | Parameters for SAS (Secure Access Service) authentication (null if not configured). |
CertAuthParams | Parameters for certificate-based authentication (null if not configured). |
GoogleParams | Parameters for Google authentication (null if not configured). |
FacebookParams | Parameters for Facebook authentication (null if not configured). |
OtcNotAutoSent | Indicates whether a One-Time Code (OTC) was not automatically sent. |
DfpProperties | Holds device fingerprint properties used to identify the device or browser, empty in this case. |
EstsProperties.UserTenantBranding.Locale | The locale used for branding the user's tenant (usually based on language preferences). |
EstsProperties.UserTenantBranding.BannerLogo | URL for the banner logo displayed during the sign-in process. |
EstsProperties.UserTenantBranding.TileLogo | URL for the tile logo displayed during the sign-in process. |
EstsProperties.UserTenantBranding.TileDarkLogo | URL for the dark-mode version of the tile logo displayed during sign-in. |
EstsProperties.UserTenantBranding.Illustration | URL for an illustration displayed during the sign-in process. |
EstsProperties.UserTenantBranding.BoilerPlateText | Boilerplate text provided by the tenant (e.g., company name or other legal text). |
EstsProperties.UserTenantBranding.KeepMeSignedInDisabled | Indicates whether the 'Keep Me Signed In' option is disabled. |
EstsProperties.UserTenantBranding.UseTransparentLightBox | Indicates whether a transparent lightbox is used during branding. |
EstsProperties.DomainType | Indicates the type of domain associated with the tenant (e.g., managed, federated, etc.). |
FlowToken | Session token used to track and authenticate the ongoing flow. |
IsSignupDisallowed | Indicates whether user sign-up (account creation) is disallowed. |
apiCanary | Internal token used by Microsoft for ensuring the consistency and validity of the authentication flow. |
The most important fields, are the fields that holds authentication data, especially the credentials such as the SasParams, FidoParams, NGC fields and Credentials.xx fields.
Authentication: Conditional Acces evaluation
The full response is saved in a cookie for the session, which is then used during the login process. The login involves a series of API calls bouncing between different session-specific endpoints, notably https://aadcdn.msftauth.net, where the Conditional Access evaluation occurs.
This process ultimately results in a JWT access token, which is retrieved from the response to this endpoint:https://portal.azure.com/api/Portal/GetEarlyUserData
I used JWT.io to decode the token and inspect what information Microsoft Entra provides after authentication.
Now, if you're feeling brave and want to see the full JavaScript code behind the Conditional Access evaluation, click the button below.
Caution! Expand at your own risk
Taking a look at the decoded JWT token, we can see several key pieces of information, such as the User Principal Name (UPN), ObjectID, Display Name, Group memberships, and geolocation based on the IP address of the successful sign-in:
{
"aud": "https://management.core.windows.net/",
"iss": "https://sts.windows.net/d2caea66-5d09-460a-a14a-8b5931a39f9a/",
"iat": 1728507280,
"nbf": 1728507280,
"exp": 1728511600,
"acr": "1",
"aio": "AXQAi/8YAAAAZu04S3lQlh0pwqgQ+jCEi8c0NHaKDNHOFbH82DxeuSmv+JuunurEMq1r/suQPLblRhwYc7wNwskc1IDK5myeZS24/1g0RdLAH9QVZqh8zeKqbvEUE7QnnnamLVWPV0/Tklj68LhU9VlIljPCRIYP1w==",
"amr": [
"mfa"
],
"appid": "c44b4083-3bb0-49c1-b47d-974e53cbdf3c",
"appidacr": "0",
"family_name": "Administrator",
"given_name": "MOD",
"groups": [
"c87ace63-6bf1-4202-82a1-51351ac6a9a8",
"2125e705-1750-4bfe-9c01-cc8fcec0fa77",
"46c44632-2a4d-42fe-a11e-a30dee699cb9",
"1fd73d4b-6950-48cf-96d9-4ebb26c31da5",
"cbebe27c-ec33-4765-99f8-268a60368f27",
"e7562e7d-f868-4050-bd06-651c09e6b7f3",
"82fdda8d-537a-403a-9586-5f5b415acdda",
"e7e2dc96-a669-4492-bd0b-4ab3ae6d5b6e",
"527ddedd-8a70-4e35-92a2-e4cf612a9ea9",
"7ab04adf-3450-44ce-be31-b1b55a06b081"
],
"idtyp": "user",
"ipaddr": "2a00:fd00:9016:c100[...]",
"name": "MOD Administrator",
"oid": "e587e4bc-3c24-4be2-83fe-14d55441f7a8",
"puid": "10032003DB7D75B4",
"rh": "0.AUEBZurK0gldCkahSotZMaOfmkZIf3kAutdPukPawfj2MBNCAQg.",
"scp": "user_impersonation",
"sub": "GBtPu4-H-61t2UNwgDjobc22iceVdrUvntypuMNNvE8",
"tid": "d2caea66-5d09-460a-a14a-8b5931a39f9a",
"unique_name": "admin@M365x88629914.onmicrosoft.com",
"upn": "admin@M365x88629914.onmicrosoft.com",
"uti": "iZDDLa9XfEqenaEMNdojAQ",
"ver": "1.0",
"wids": [
"62e90394-69f5-4237-9190-012177145e10"
],
"xms_idrel": "16 1",
"xms_tcdt": 1727988574
}
In this token, you can observe key claims, such as:
aud: Audience of the token (https://management.core.windows.net/), indicating the resource the token is intended for.
iss: Issuer of the token (https://sts.windows.net/), representing the Azure AD tenant that issued the token.
groups: Lists the user’s group memberships.
ipaddr: Geolocation based on the IP address from which the sign-in occurred.
upn: User Principal Name (UPN) of the authenticated user.
oid: Object ID of the user in Azure AD.
scp: Scope of permissions granted (in this case, user_impersonation).
These claims are essential for determining the user’s access to resources and enforcing the correct security measures.
Flow State Achieved: Wrapping Up the Journey
Decoding the authentication flow in Microsoft Entra might seem like diving deep into a rabbit hole, but once you're plugged into the system, it all starts to make sense.
We now have the same understanding as Neo, giving us a home in the code.
From analyzing the payload to breaking down the responses and eventually collecting the JWT token, every step brings us closer to understanding how Conditional Access evaluates and enforces security.
And remember, much like the Matrix, understanding the code gives you the power—to secure your users, safeguard data, and keep your environment running smoothly.
And to have a breather after all of that mess, here's a joke to give you a round of laughter:
Why do security engineers make terrible comedians?
Because their jokes take too long to crack! 😎
Stay tuned, as I'm planning to keep my blog very active, and got loads of plans for the future!
Comments