Sapir’s failed research blog

Entra Sign-In logs hidden gems

This short post is here to raise awareness about some super useful fields in the sign-in logs. We all know how essential these logs are—if you want to get things done in the cloud, it usually starts with a user 🙂 and that means a sign-in!


For this post, I’ll be referencing the fields as they appear in the Graph API’s beta version. Sure, the beta version isn’t as stable as V1.0, but using V1.0 would mean missing out on a massive amount of information!

I genuinely believe that, today, achieving solid protection in Entra just isn’t realistic without using the Beta API.

So, let’s dive in!


To query sign-in logs from the Beta API, you can use this endpoint:
https://graph.microsoft.com/beta/auditLogs/signIns

You can also grab the logs with the PowerShell module—it’s actually pretty handy for parsing and working with the data, though I seem to enjoy doing things the hard way. 🙂

Get-MgBetaAuditLogSignIn

Alright, let’s get organized with a few key fields:

  • "userPrincipalName": "sapir@mycooltenant.onmicrosoft.com"
  • "userId": <GUID>
  • "appId": <GUID>
  • "appDisplayName": "Graph Explorer"
  • "resourceDisplayName": "Microsoft Graph"
  • "resourceId": "00000003-0000-0000-c000-000000000000"

The event we’re looking at shows me (userPrincipalName/userId) authenticating to Graph Explorer (appDisplayName/appId). The “resource” here represents the audience—in this case, Microsoft Graph. You can actually see this in the token’s aud claim, which indicates the API domain for accessing the requested information.

For example, if I log in to the “Azure Portal” application, the resource would be “Azure Resource Manager,” which has the domain https://management.azure.com/.

That’s the key point I wanted to emphasize!


Now for my favorite fields!

incomingTokenType
Starting strong—this one’s really cool. The possible values here are: none, primaryRefreshToken, saml11, saml20, unknownFutureValue, remoteDesktopToken, and refreshToken.

I love this field, especially the primaryRefreshToken (PRT) value. I think it can be incredibly useful for incident response and writing detections. Since this value comes directly from the token, we can even use it to detect potential PRT theft.

authenticationDetails
This array is a big one! It contains the full details of each authentication attempt. For example, if someone is trying to use MFA fatigue attacks, where they flood with MFA prompts until one succeeds, each failed attempt leading up to success will be documented here.

Example:

"authenticationDetails": [
    {
        "authenticationStepDateTime": "2024-10-21T13:41:46Z",
        "authenticationMethod": "Password",
        "authenticationMethodDetail": "Password in the cloud",
        "succeeded": true,
        "authenticationStepResultDetail": "Correct password",
        "authenticationStepRequirement": ""
    },
    {
        "authenticationStepDateTime": "2024-10-21T13:41:46Z",
        "authenticationMethod": "Mobile app notification",
        "succeeded": false,
        "authenticationStepResultDetail": "MFA denied; user declined the authentication",
        "authenticationStepRequirement": ""
    },
    {
        "authenticationStepDateTime": "2024-10-21T13:50:46Z",
        "authenticationMethod": "Mobile app notification",
        "succeeded": false,
        "authenticationStepResultDetail": "MFA denied; user declined the authentication",
        "authenticationStepRequirement": ""
    }
    ...
]

Capturing this field is really important. Since it logs the entire authentication flow, it can reveal the attack pattern itself in some cases.

appliedConditionalAccessPolicies
I’m not sure if we can use this field directly for detections, but it’s definitely useful for investigations. For example, if someone managed to steal a PRT to bypass Conditional Access Policies (CAP), we might combine this with the incomingTokenType field and look at CAP requirements (like needing a joined device). This could help us understand how the attacker accessed the organization, potentially reversing the attack. It’s also valuable for stats—analyzing how often CAPs are enforced in the organization.

ipAddressFromResourceProvider
Here’s Microsoft’s description:

The IP address a user used to reach a resource provider, used to determine Conditional Access compliance for some policies. For example, when a user interacts with Exchange Online, the IP address that Microsoft Exchange receives from the user can be recorded here. This value is often null.

I think this field could be quite powerful for tracking attacks. Having the IP address can make a big difference when trying to reverse-engineer an attack.

tokenIssuerType
This field could be a handy one for detecting federated authentication, especially with the ADFederationServices value.

I believe we’ll see an uptick in lateral movement attacks across cloud platforms, and federated authentication might become a target for abuse.

clientCredentialType
The possible values here are none, clientSecret, clientAssertion, federatedIdentityCredential, managedIdentity, certificate, and unknownFutureValue.

The federatedIdentityCredential value looks especially interesting, but I also think monitoring managed identities is essential. Managed identities should typically have stable, predictable behavior, so any unusual activity—like one suddenly authenticating with the PowerShell app when it normally only uses Microsoft Graph—could be suspicious.

authenticationMethodsUsed
Values include SMS, Authenticator App, App Verification code, Password, FIDO, PTA, or PHS.

This field is useful in several ways:

  • Incident Response (IR): To see how an attacker authenticated.
  • Understanding Your Org: To identify accounts not using strong MFA methods.
  • Detections: For example, you can combine this with the authenticationDetails field to detect interesting attacks. Today, we know it’s possible to fake or steal PTA agents (For example – https://www.secureworks.com/research/azure-active-directory-pass-through-authentication-flaws). The authenticationDetails tab shows the agentId that authenticated the user, so by focusing on events with authenticationMethodUsed: PTA, we could build rules to track or analyze suspicious agent behavior.

uniqueTokenIdentifier
This is essential for correlating authentication events with audit/Graph activity events, making it especially valuable for incident response.

homeTenantId
We’re all aware of the risks that come with external users. Some useful detection rules could be built around this, such as flagging external users who authenticate to the PowerShell application. This could hint at an attacker using tools like RoadRecon or AADInternals.

authenticationProtocol
Possible values include none, oAuth2, ropc, wsFederation, saml20, deviceCode, unknownFutureValue, authenticationTransfer, and nativeAuth.

One of my favorite values here is DeviceCodeFlow. We all know that Device Code Flow phishing attacks are incredibly common these days. But there are other interesting values too! Ever heard of the ropc flow? It allows an identity provider to grant an access token to an application with just a username and password—no MFA support. Surprisingly, some first-party applications allow this flow, and attackers are definitely exploiting it! You can read more about it here.

It might be worth checking if any of your applications allow this flow—or even creating one as a sort of honeypot!

resourceTenantId
This field can be a great way to spot if accounts are authenticating to resources in a different tenant. This could indicate unauthorized cross-tenant access.

errorCode/failureReason
This is where the real magic happens! You can learn a lot from failure reasons. I highly recommend exploring the possible values here. Some of my favorites:

  • AADSTS50020
  • AADSTS50053
  • AADSTS50088
  • AADSTS530034

And there are plenty more worth looking into!

managedServiceIdentity
This object includes the fields:

"managedServiceIdentity": {
    "msiType": "none",
    "associatedResourceId": null,
    "federatedTokenId": null,
    "federatedTokenIssuer": null
}

Interestingly, I’ve found that federatedTokenId and federatedTokenIssuer are always null. Not sure why 🙁 But monitoring Managed Identity authentication is still a smart move, in my opinion.

crossTenantAccessType
Possible values include none, b2bCollaboration, b2bDirectConnect, microsoftSupport, serviceProvider, unknownFutureValue, and passthrough.

I’ve seen some great detections based on this field, like these examples:

isInteractive
This field is only populated in the Beta version, which means if you’re reading sign-in logs using V1.0, you’re missing all non-interactive sign-ins in your environment. Same goes for Managed Identity authentication!

I didn’t go into basic fields like application or resource, since they’re generally well-known and widely used. But just to reiterate—they’re really, really important.

Thanks for tuning into this little ramble of mine about sign-in logs! I hope you picked up a few new insights—or at least found something useful buried in all my thoughts. There’s definitely more to come, so stay tuned for more log-inspired musings! 😊

P.S. Let me know if you’d be interested in a guide on using Python’s Pandas module to create some interesting stats on the sign-in logs (with cool, silly graphs) that could lead to great detections!

Leave a comment