Microsoft Entra Sign-in Logs Analysis
Authentication is the primary perimeter in cloud environments. When an adversary targets a Microsoft 365 tenant, the initial point of friction, compromise, and persistent access is recorded in Microsoft Entra ID sign-in logs.
Sign-in logs are not mere access timestamps; they capture the complete cryptographic and operational state of the authentication handshake: conditional access policy evaluations, device compliance claims, multifactor authentication (MFA) validation methods, risk telemetry, network routing, and session token redemptions.
This guide provides an exhaustive forensic breakdown of the four Entra ID sign-in log streams, their underlying JSON schemas, investigative methodologies, and production-grade hunting queries.
Concept
Section titled “Concept”Microsoft Entra ID partitions sign-in telemetry into four distinct streams, each tracking a different authentication actor and transport mechanism:
graph TD subgraph "Entra ID Sign-in Log Architecture" STS[Entra Security Token Service - STS]
STS -->|1. Human + Credential Entry| STREAM1["1. Interactive User Sign-ins<br/>(SignInLogs)"] STS -->|2. Refresh Token / PRT Redemption| STREAM2["2. Non-Interactive User Sign-ins<br/>(AADNonInteractiveUserSignInLogs)"] STS -->|3. App Secret / Certificate Auth| STREAM3["3. Service Principal Sign-ins<br/>(AADServicePrincipalSignInLogs)"] STS -->|4. Azure VM / Container Identity| STREAM4["4. Managed Identity Sign-ins<br/>(AADManagedIdentitySignInLogs)"] endThe Four Sign-in Streams
Section titled “The Four Sign-in Streams”- Interactive User Sign-ins (
interactiveUser): Generated when a human user explicitly inputs an authentication factor—such as entering a password, responding to an MFA push prompt, scanning a FIDO2 key, or authenticating via Windows Hello for Business. - Non-Interactive User Sign-ins (
nonInteractiveUser): Generated when a client application (Outlook, Teams, OneDrive sync client, or a custom script) presents an existing Refresh Token or Primary Refresh Token (PRT) to acquire fresh, short-lived Access Tokens silently. In 90% of token theft and session hijacking incidents (e.g., AiTM), the adversary’s activity appears exclusively in this stream. - Service Principal Sign-ins (
servicePrincipal): Generated when an Enterprise Application or automated backend service authenticates using its own client credentials (client secret or certificate) without human interaction. - Managed Identity Sign-ins (
managedIdentity): Generated by Azure-hosted resources (Virtual Machines, App Services, Azure Functions) leveraging platform-assigned identities to access Entra-protected resources without storing secrets in code.
Why It Matters in DFIR
Section titled “Why It Matters in DFIR”Sign-in logs represent the single most critical forensic evidence source for answering initial-access questions during an incident:
- Detecting Password Sprays & Brute Force: Repeated authentication failures with specific error codes (
50126,50053) distributed across hundreds of accounts from specialized proxy networks. - Exposing Session Hijacking & AiTM Phishing: An interactive login succeeds through an Adversary-in-the-Middle reverse proxy, followed minutes later by non-interactive access token redemptions from an entirely different IP address and autonomous system number (ASN) without an MFA prompt.
- Auditing Conditional Access Failures and Bypasses: Verifying whether an attacker evaded MFA requirements by spoofing a legacy client application, abusing a trusted IP exclusion, or enrolling a rogue device.
- Tracking Malicious Automation: Detecting compromised service principal credentials used to dump tenant data via Microsoft Graph.
JSON Schema Deep Dive: The Critical Forensic Fields
Section titled “JSON Schema Deep Dive: The Critical Forensic Fields”When querying sign-in logs via Microsoft Graph API (/auditLogs/signIns) or Azure Log Analytics (SigninLogs), the following fields provide essential evidentiary value:
| Field Name (Graph API) | Field Name (Log Analytics) | Forensic Significance & Investigative Interpretation |
|---|---|---|
id | CorrelationId / Id | Unique identifier for the sign-in transaction. Essential for pivoting to correlate downstream Purview UAL events. |
createdDateTime | TimeGenerated | Exact UTC timestamp of the authentication request at the Entra STS gateway. |
userPrincipalName | UserPrincipalName | Target account identity. Always cross-reference with userId to verify the account was not renamed. |
userId | UserId | Immutable GUID of the user object in the directory. |
appDisplayName | AppDisplayName | Name of the application requesting the token (e.g., “Microsoft Office”, “Azure Portal”, “MyCustomApp”). |
appId | AppId | GUID of the client application. Attackers frequently spoof appDisplayName, but cannot spoof registered appId GUIDs. |
ipAddress | IPAddress | Public IPv4/IPv6 source address of the client request. Must be evaluated against VPN, hosting, and residential proxy databases. |
location | LocationDetails | Country, state, and city inferred from IP geolocation. Contains geoCoordinates (latitude/longitude estimate). |
networkLocationDetails | NetworkLocationDetails | Dynamic JSON array indicating whether traffic matched a Named Location or traversed Global Secure Access (GSA) SSE edges. |
clientAppUsed | ClientAppUsed | Protocol / client category: “Browser”, “Mobile Apps and Desktop clients”, “Exchange ActiveSync”, “IMAP4”, “POP3”. |
deviceDetail | DeviceDetail | JSON object detailing deviceId, operatingSystem, browser, isCompliant, isManaged, and trustType (“Azure AD joined”). |
conditionalAccessStatus | ConditionalAccessStatus | High-level CA result: success, failure, notApplied. Must be unpacked via conditionalAccessPolicies. |
conditionalAccessPolicies | ConditionalAccessPolicies | Array of all evaluated policies. Indicates which policies required MFA, whether controls were satisfied, and reasons for exclusions. |
riskDetail | RiskDetail | Reason for identity risk assignment: none, adminGeneratedTemporaryPassword, userReportedSuspiciousActivity, aiConfirmedSigninSafe. |
riskLevelAggregated | RiskLevelAggregated | Machine-learning aggregate risk assessment: none, low, medium, high, hidden. |
authenticationProcessingDetails | AuthenticationProcessingDetails | Key/value pairs containing internal STS mechanics: IsPRT (True/False), TokenIssuerType, Legacy TLS version. |
authenticationRequirement | AuthenticationRequirement | Indicates whether the transaction demanded singleFactorAuthentication or multiFactorAuthentication. |
status | ResultType / ResultDescription | Status code (0 = Success). Non-zero codes indicate exact failure mechanisms. |
Critical Entra Authentication Error Codes
Section titled “Critical Entra Authentication Error Codes”Incident responders must memorize the primary Entra ID authentication error codes to immediately classify attack patterns:
| Error Code | Official Description | DFIR Interpretation & Threat Actor Context |
|---|---|---|
0 | Success | Successful authentication handshake. Token issued. |
50053 | Account is locked | Account lockout triggered by Smart Lockout or legacy lockout policies following password spray. |
50074 | Strong Auth required | User authenticated password correctly, but failed or abandoned the required MFA challenge. |
50076 | Strong Auth required from user | User must perform MFA because they are accessing a sensitive resource or triggered a Conditional Access rule. |
50088 | Faulty token / Token expired | Client presented an invalid or expired refresh token. Common during token replay after revocation. |
50097 | Device authentication required | Request failed because device was not recognized as managed, compliant, or hybrid joined. |
50126 | Invalid username or password | Classic invalid credential error. High volume from single IP = brute force; low volume across users = password spray. |
50131 | Device trust failure | Conditional access blocked the request due to suspicious device state or MDM compliance failure. |
53003 | Blocked by Conditional Access | Access was explicitly blocked by an administrator policy (e.g., untrusted country, non-compliant device). |
70044 | Session revoked | Session was invalidated due to admin action (Revoke-MgUserSignInSession) or Continuous Access Evaluation (CAE). |
What Is Possible vs What Is Not Possible
Section titled “What Is Possible vs What Is Not Possible”What Is Possible
Section titled “What Is Possible”- Reconstructing Authentication Timelines: Tracing every interactive login and background token refresh over the past 30 days (P1/P2).
- Proving MFA Bypass vs MFA Satisfaction: Examining
authenticationProcessingDetailsto prove whether MFA was satisfied via an authenticator app push, FIDO2 key, or bypassed via a trusted IP whitelist. - Detecting AiTM Session Cookie Theft: Identifying discrepancies where an interactive login occurred from IP A (phishing proxy), followed by non-interactive Graph API calls from IP B (attacker’s real infrastructure) using the identical session ID.
- Unmasking Legacy Authentication Attacks: Detecting brute-force attempts targeting basic authentication protocols (IMAP, POP3, SMTP) that bypass modern MFA policies.
What Is Not Possible
Section titled “What Is Not Possible”- Inspecting Resource Data Plane Activity: Sign-in logs record that a token was issued for SharePoint; they do not record which files were downloaded.
- Retroactive Recovery on Entra Free: On an unmonitored Entra Free tenant, sign-in records older than 7 days are completely unrecoverable.
- Tracing Local Machine Offline Logins: Local Windows workstation logons using cached credentials do not generate Entra STS sign-in events unless the machine connects to Entra cloud services.
- De-anonymizing Residential Proxies via Headers Alone: If an attacker routes their browser traffic through a high-reputation residential proxy (e.g., BrightData, Oxylabs), the sign-in log records the residential ISP IP address; uncovering the adversary’s true origin IP requires legal subpoenas to the proxy provider.
Investigation Methodology: Forensic Extraction and KQL Playbooks
Section titled “Investigation Methodology: Forensic Extraction and KQL Playbooks”# ==============================================================================# Hermes Codex - Production-Grade Entra Sign-In Extractor# Exports Interactive, Non-Interactive, and Service Principal logs# ==============================================================================
Import-Module Microsoft.Graph.Authentication, Microsoft.Graph.Reports -ErrorAction StopConnect-MgGraph -Scopes "AuditLog.Read.All", "Directory.Read.All" -NoWelcome
$TargetUser = "compromised.user@domain.com"$DaysBack = 14$StartDate = (Get-Date).AddDays(-$DaysBack).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")$OutDir = "./Entra_SignIns_$(Get-Date -Format 'yyyyMMdd')"New-Item -ItemType Directory -Path $OutDir -Force | Out-Null
# 1. Query Interactive Sign-insWrite-Host "[*] Querying Interactive Sign-ins for $TargetUser..." -ForegroundColor Cyan$filter = "userPrincipalName eq '$TargetUser' and createdDateTime ge $StartDate"$interactiveUri = "https://graph.microsoft.com/v1.0/auditLogs/signIns?`$filter=$filter&`$top=500"
$allSignIns = [System.Collections.Generic.List[PSObject]]::new()do { $resp = Invoke-MgGraphRequest -Method GET -Uri $interactiveUri if ($resp.value) { $allSignIns.AddRange($resp.value) } $interactiveUri = $resp.'@odata.nextLink' Start-Sleep -Milliseconds 150} while ($null -ne $interactiveUri)
$interactiveFile = "$OutDir/SignIns_Interactive.jsonl"$allSignIns | ForEach-Object { $_ | ConvertTo-Json -Compress -Depth 10 } | Set-Content -Path $interactiveFileWrite-Host "[+] Exported $($allSignIns.Count) interactive sign-in events." -ForegroundColor Green
# 2. Compute integrity checksum$hash = Get-FileHash -Path $interactiveFile -Algorithm SHA256Write-Host "[✓] Artifact SHA-256: $($hash.Hash)" -ForegroundColor Green// ==============================================================================// Detect AiTM Session Cookie Reuse (IP / ASN Discrepancy within Active Session)// Identifies an interactive logon followed by non-interactive activity from a new IP// ==============================================================================let TimeWindow = 24h;
// Step 1: Collect successful interactive loginslet InteractiveLogins = SigninLogs| where TimeGenerated >= ago(TimeWindow)| where ResultType == 0| project InteractiveTime = TimeGenerated, UserPrincipalName, InteractiveIP = IPAddress, InteractiveCity = LocationDetails.city, InteractiveASN = AutonomousSystemNumber, AppDisplayName, CorrelationId;
// Step 2: Correlate with non-interactive token refreshesAADNonInteractiveUserSignInLogs| where TimeGenerated >= ago(TimeWindow)| where ResultType == 0| project NonInteractiveTime = TimeGenerated, UserPrincipalName, NonInteractiveIP = IPAddress, NonInteractiveCity = LocationDetails.city, NonInteractiveASN = AutonomousSystemNumber, NonInteractiveApp = AppDisplayName, CorrelationId| join kind=inner (InteractiveLogins) on UserPrincipalName| where NonInteractiveTime between (InteractiveTime .. (InteractiveTime + 2h))| where NonInteractiveIP != InteractiveIP| where NonInteractiveASN != InteractiveASN| project NonInteractiveTime, UserPrincipalName, InteractiveIP, NonInteractiveIP, InteractiveCity, NonInteractiveCity, NonInteractiveApp, CorrelationId| order by NonInteractiveTime desc// ==============================================================================// Password Spray Detection: High Volume Failures across multiple accounts// ==============================================================================let SprayWindow = 1h;let FailureThreshold = 10; // Distinct accounts targeted
SigninLogs| where TimeGenerated >= ago(SprayWindow)| where ResultType in (50126, 50053) // Invalid password or account locked| summarize TargetedAccountsCount = dcount(UserPrincipalName), SampleAccounts = make_set(UserPrincipalName, 10), TotalAttempts = count() by IPAddress, Location, AppDisplayName, bin(TimeGenerated, 15m)| where TargetedAccountsCount >= FailureThreshold| order by TargetedAccountsCount descInvestigation Scenario: The 4-Minute Session Hijack
Section titled “Investigation Scenario: The 4-Minute Session Hijack”Intrusion Walkthrough
Section titled “Intrusion Walkthrough”-
10:14:02 UTC - Interactive Phishing Authentication:
- Stream:
interactiveUser(inSigninLogs). UserPrincipalName:cfo@corp.com.IPAddress:185.220.101.5(Tor exit node utilized by an Evilginx proxy).ResultType:0(Success).ClientAppUsed:Browser(Chrome).AuthenticationDetails: Password satisfied + MFA push approved via Microsoft Authenticator.- Forensic Fact: The user successfully authenticated through the proxy, which intercepted the resulting
ESTSAUTHandESTSAUTHPERSISTENTsession cookies.
- Stream:
-
10:18:14 UTC - Attacker Session Injection:
- Stream:
nonInteractiveUser(inAADNonInteractiveUserSignInLogs). UserPrincipalName:cfo@corp.com.IPAddress:91.240.118.12(Hosting provider in Eastern Europe).ResultType:0(Success).AppDisplayName:Microsoft Graph PowerShell.AuthenticationProcessingDetails:IsPRT: False,TokenIssuerType: AzureAD.ConditionalAccessStatus:success(MFA satisfied via inherited session claim).- Forensic Fact: The attacker imported the stolen session cookie into a headless browser, immediately requesting an access token for Graph PowerShell without encountering an MFA prompt.
- Stream:
The Transversal Doctrine: Sign-in Telemetry vs Malicious Proof
Section titled “The Transversal Doctrine: Sign-in Telemetry vs Malicious Proof”In cloud incident response, a sign-in record must be interpreted through the Hermes Codex certainty scale:
+-------------------------------------------------------------------------------+| THE 7 LEVELS OF FORENSIC CERTAINTY || || 1. Possible -> Account identity exists and can authenticate to Entra. || 2. Configured -> Conditional Access policies and MFA registration active. || 3. Authorized -> Account permissions allow access to requested cloud app. || 4. Accessible -> Network and IP whitelists allowed connection to Entra STS.|| 5. Utilized -> Credentials submitted; token request processed by STS. || 6. Observed -> Entry recorded in SigninLogs or NonInteractive logs. || 7. Proven -> Replay proven via IP discrepancy, token hash, or UAL link.|+-------------------------------------------------------------------------------+Forensic Distinctions in Sign-in Logs
Section titled “Forensic Distinctions in Sign-in Logs”- Authorized != Utilized: An administrator account was authorized to authenticate to the Azure Management portal. Unless an entry with
AppId = "c44b3eab-8279-4727-b0e6-c558e6fbda3a"is observed inSigninLogs, they did not open the portal. - Utilized != Observed: If an adversary initiates a brute-force attack against an Entra Free tenant, but the incident is investigated on Day 12, the attack was utilized by the adversary, but is no longer observed because the 7-day native retention window expired.
- Observed != Proven: Observing a sign-in with
ResultType = 0from an unfamiliar IP address observes an authentication event. Proving it was malicious requires demonstrating that the legitimate user was elsewhere (e.g., impossible travel with concurrent office badge swipes), or correlating the sign-in with unauthorized downstream actions in the Unified Audit Log (e.g., mass file downloading or rule tampering).
Common Pitfalls and Traps
Section titled “Common Pitfalls and Traps”| Trap | Technical Root Cause | Investigative Impact | Corrective Action |
|---|---|---|---|
| Ignoring Non-Interactive Sign-In Logs | Examining only SignInLogs (interactive) and omitting AADNonInteractiveUserSignInLogs. | Misses 90% of token theft, session replay, and automated data exfiltration activities. | Always query both interactive and non-interactive tables simultaneously. |
| Trusting IP Geolocation Immutably | Commercial MaxMind / GeoIP databases frequently misclassify VPNs, cloud providers, and satellite ISPs. | False accusations of “compromise from abroad” when an employee was simply using a flight Wi-Fi or VPN. | Correlate IP geolocation with ISP Autonomous System Number (ASN) and threat intelligence feeds. |
| Confusing 50126 with Targeted Attack | Error 50126 (Invalid password) occurs millions of times globally due to benign mistyping. | Wasting investigative hours analyzing background internet background noise. | Filter for sudden spikes, abnormal User-Agents, or password spray patterns across multiple accounts. |
| Assuming MFA Proves Legitimate User | Adversary-in-the-Middle (AiTM) proxies forward real-time MFA prompts to victims who legitimately approve them. | Concluding an account was “not compromised because MFA succeeded”. | Inspect session continuity: compare interactive IP with subsequent non-interactive token refresh IPs. |
| Overlooking IPv6 Sign-ins | Many mobile devices and modern ISPs default to dynamic IPv6 addresses that change hourly. | Incorrectly flagging routine mobile device IP rotation as an “impossible travel” incident. | Verify device trust (isManaged, deviceId) across rotating IPv6 subnets. |
2026 Feature State: Entra ID Sign-in Logs
Section titled “2026 Feature State: Entra ID Sign-in Logs”Recent Changes
Section titled “Recent Changes”- Log Analytics & Graph Schema Parity: Azure Log Analytics table schemas (
SigninLogs,AADNonInteractiveUserSignInLogs) maintain complete field parity with the Microsoft Graph v1.0/auditLogs/signInsendpoint. - Global Secure Access (GSA) Flagging: The
networkLocationDetailsproperty explicitly identifies traffic routed through Microsoft’s Security Service Edge (SSE) network. - Token Protection Logging: When Token Protection (device-bound tokens) is enforced, sign-in records reflect cryptographic proof-of-possession validation in
authenticationProcessingDetails.
Deprecated Features
Section titled “Deprecated Features”- Azure AD Graph API (
graph.windows.net): Completely offline. All scripted queries targeting sign-in logs must querygraph.microsoft.com. - Legacy Exchange ActiveSync Basic Auth: Basic authentication headers are rejected platform-wide. Any legacy sign-in attempts result in immediate 50126/50034 rejections.
Current Limitations
Section titled “Current Limitations”- Ingestion Delay on Heavy Tenants: In tenants with over 50,000 users, non-interactive sign-in log ingestion into Log Analytics can experience transient latencies of up to 20 minutes.
- Dynamic Field Unpacking in KQL: Certain nested fields (such as
DeviceDetailandAuthenticationProcessingDetails) are ingested as dynamic JSON strings requiring explicittostring()casting in KQL queries.
Key Takeaways
Section titled “Key Takeaways”- Non-interactive logs are the real crime scene: Token theft, AiTM session replay, and automated API exfiltration live in
AADNonInteractiveUserSignInLogs. - Master the error codes: Memorize
50126(bad password),50053(locked),50074(MFA needed), and53003(CA block) for rapid triage. - Pivoting on CorrelationId: Use the sign-in
CorrelationIdto bridge identity logs to downstream Purview UAL file and email access events. - Beware of AiTM MFA illusions: A successful MFA push does not guarantee security; verify that subsequent token refreshes originate from the same IP and ASN.
- Enforce external streaming immediately: Entra Free tenants lose all authentication evidence after 7 days; archive logs to Log Analytics without delay.