Query Details

Sensitive Microsoft Graph Delegated Permission Access

Query

let SensitiveMsGraphPermissions = externaldata(
    EAMTierLevelName: string,
    Category: string,
    ScopeDisplayName: string
    ) [@"https://raw.githubusercontent.com/Cloud-Architekt/AzurePrivilegedIAM/refs/heads/main/Classification/Classification_MsGraphScopes.json"] with (format="multijson")
    |;
let SignInsWithDelegatedScope = union SigninLogs, AADNonInteractiveUserSignInLogs
| where ResourceDisplayName == "Microsoft Graph"
// Enrichment of CAE, OAuthScope and Token Binding
| extend AuthenticationMethod = tostring(parse_json(AuthenticationDetails)[0].authenticationMethod)
| extend AuthenticationDetail = tostring(parse_json(AuthenticationDetails)[0].authenticationStepResultDetail)
| extend AuthProcessDetails = replace_string(AuthenticationProcessingDetails, " " , "")
| extend AuthProcessDetails = replace_string(AuthProcessDetails, "\r\n" , "")
| parse AuthProcessDetails with * "IsClientCapable\",\"value\":\"" IsClientCapable "\"" *
| parse AuthProcessDetails with * "IsCAEToken\",\"value\":\"" IsCaeToken "\"" *
| parse AuthProcessDetails with * "OauthScopeInfo\",\"value\":\"" OauthScopeInfo "\"}" *
| extend OAuthDelegatedScope = replace_string(OauthScopeInfo, '\\', '')
| extend TokenProtectionStatus = iff(isempty( TokenProtectionStatusDetails_dynamic ), todynamic(TokenProtectionStatusDetails_string), TokenProtectionStatusDetails_dynamic)
| extend SignInSessionStatus = tostring(TokenProtectionStatus.signInSessionStatus)
// Enrichment for AuthMethod and DeviceDetails
| extend AuthenticationMethod = tostring(parse_json(AuthenticationDetails)[0].authenticationMethod) 
| extend AuthenticationDetail = tostring(parse_json(AuthenticationDetails)[0].authenticationStepResultDetail)
| extend DeviceDetail = iff(isempty( DeviceDetail_dynamic ), todynamic(DeviceDetail_string), DeviceDetail_dynamic)    
| extend DeviceName = tostring(toupper(DeviceDetail.displayName))
| extend DeviceOS = tostring(parse_json(DeviceDetail).operatingSystem)
| extend DeviceTrust = tostring(parse_json(DeviceDetail).trustType)
| extend DeviceCompliance = tostring(parse_json(DeviceDetail).isCompliant)
| project TimeGenerated = CreatedDateTime, CorrelationId, UserPrincipalName, RiskLevelDuringSignIn, RiskState, AppDisplayName, ResourceDisplayName, tostring(OAuthDelegatedScope), AuthenticationMethod, AuthenticationDetail, DeviceName, DeviceOS, DeviceTrust, DeviceCompliance, IncomingTokenType, IsCaeToken, SignInSessionStatus;
SignInsWithDelegatedScope
| mv-expand parse_json(OAuthDelegatedScope)
| extend ScopeDisplayName = tostring(OAuthDelegatedScope)   
| join kind=leftouter(
        SensitiveMsGraphPermissions | project ScopePermissionTierLevel = tostring(EAMTierLevelName), tostring(Category), tostring(ScopeDisplayName)
        ) on ScopeDisplayName
| where isnotempty(ScopeDisplayName)
| extend ScopePermissionTierLevel = iff(isnotempty(ScopePermissionTierLevel), ScopePermissionTierLevel, "Unclassified")
| sort by TimeGenerated
| extend ScopePermissionDetails = bag_pack_columns(ScopeDisplayName, ScopePermissionTierLevel)
// Optional: Filter sign-ins with delegated permissions on Control Plane
//| where ScopePermissionTierLevel == "ControlPlane"
| summarize ScopePermissions = make_set(ScopePermissionDetails), ScopePermissionTierLevels = array_sort_asc(make_set(ScopePermissionTierLevel)) by TimeGenerated, CorrelationId, UserPrincipalName, RiskLevelDuringSignIn, RiskState, AppDisplayName, ResourceDisplayName, AuthenticationMethod, AuthenticationDetail, DeviceName, DeviceOS, DeviceTrust, DeviceCompliance, IncomingTokenType, IsCaeToken, SignInSessionStatus

Explanation

This KQL query is designed to analyze sign-in logs related to Microsoft Graph, focusing on delegated permissions. Here's a simplified breakdown of what the query does:

  1. Load Sensitive Permissions Data: It starts by loading a dataset of sensitive Microsoft Graph permissions from an external JSON file hosted on GitHub. This dataset includes information about the permission tier level, category, and scope display name.

  2. Combine Sign-In Logs: It combines data from two sources: SigninLogs and AADNonInteractiveUserSignInLogs, filtering for entries where the resource display name is "Microsoft Graph".

  3. Extract and Enrich Data: The query extracts and enriches various details from the sign-in logs:

    • Authentication method and details.
    • Information about the authentication process, including whether the client is capable and if a CAE token is used.
    • OAuth scope information and token protection status.
    • Device details like name, operating system, trust type, and compliance status.
  4. Project Relevant Fields: It selects specific fields to focus on, such as the time of the sign-in, user principal name, risk level, application name, and various authentication and device details.

  5. Expand and Join with Sensitive Permissions: The query expands the OAuth delegated scopes and joins them with the sensitive permissions data to identify the tier level and category of each scope.

  6. Classify and Sort: It classifies scopes that don't have a tier level as "Unclassified" and sorts the results by the time of the sign-in.

  7. Summarize Results: Finally, it summarizes the data by grouping it based on several fields and creating sets of scope permissions and their tier levels.

This query helps in identifying and analyzing sign-ins that involve sensitive Microsoft Graph permissions, providing insights into the risk levels and compliance of devices used during these sign-ins.

Details

Thomas Naunheim profile picture

Thomas Naunheim

Released: June 11, 2026

Tables

SigninLogsAADNonInteractiveUserSignInLogs

Keywords

SensitiveMsGraphPermissionsSigninLogsAADNonInteractiveUserSignInLogsMicrosoftGraphAuthenticationDetailsAuthenticationProcessingDetailsTokenProtectionStatusDetailsDeviceDetailUserPrincipalNameRiskLevelDuringSignInRiskStateAppDisplayNameResourceDisplayNameOAuthDelegatedScopeAuthenticationMethodAuthenticationDetailDeviceNameDeviceOSDeviceTrustDeviceComplianceIncomingTokenTypeIsCaeTokenSignInSessionStatusScopePermissionTierLevelCategoryScopeDisplayNameEAMTierLevelNameCorrelationId

Operators

letexternaldatawithunionwhereextendtostringparse_jsonreplace_stringparseiffisemptytodynamicprojectmv-expandjoinkind=leftouteronisnotemptysort bybag_pack_columnssummarizemake_setarray_sort_asc

Actions