Back
Id67802748-435b-4f80-9f61-b9a9ac6ea15c
Rulename[Entra ID] Suspicious Continuous OAuth Token Usage
DescriptionDetects repeated use of the same OAuth token across different IPs or locations over time. This can indicate token theft or session abuse.
SeverityHigh
TacticsCredentialAccess
TechniquesT1606
Required data connectorsAzureActiveDirectory
KindScheduled
Query frequency1d
Query period1d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/eDCRule/Analytic%20Rules/%5BEntra%20ID%5D%20Suspicious%20Continuous%20OAuth%20Token%20Usage.yaml
Version1.0.0
Arm template67802748-435b-4f80-9f61-b9a9ac6ea15c.json
Deploy To Azure
// =====================
// Low-noise Token Reuse Detector (fixed timespan calc)
// =====================
let GapDays = 1;
let MinIPChanges = 2;
let MinLocChanges = 2;
let ExcludeApps = dynamic([
    "Microsoft Authentication Broker",
    "Microsoft Teams",
    "Office 365"
    ]);
let Src =
    union isfuzzy=true
        AADNonInteractiveUserSignInLogs,
        SigninLogs
    | where ResultType == 0
    | where isnotempty(UniqueTokenIdentifier);
let Past =
    Src
    | where TimeGenerated between (ago(14d) .. ago(1d))
    | summarize
        PastLastSeen = max(TimeGenerated),
        PastUPNs     = make_set(UserPrincipalName, 20),
        PastIPs      = make_set(IPAddress, 50),
        PastLocs     = make_set(tostring(Location), 50),
        PastApps     = make_set(AppDisplayName, 50)
        by UniqueTokenIdentifier;
let Last24h =
    Src
    | where TimeGenerated >= ago(1d)
    //| where AppDisplayName !in (ExcludeApps)
    | summarize
        CurrentFirstSeen = min(TimeGenerated),
        CurrentLastSeen  = max(TimeGenerated),
        CurrentUPNs      = make_set(UserPrincipalName, 20),
        CurrentIPs       = make_set(IPAddress, 50),
        CurrentLocs      = make_set(tostring(Location), 50),
        CurrentApps      = make_set(AppDisplayName, 50),
        IPCount          = dcount(IPAddress),
        LocCount         = dcount(tostring(Location))
        by UniqueTokenIdentifier, ResultType;
Last24h
| join kind=inner Past on UniqueTokenIdentifier
| extend Gap = CurrentFirstSeen - PastLastSeen
| extend GapThreshold = totimespan(strcat(GapDays, "d"))
| where Gap >= GapThreshold
| where IPCount >= MinIPChanges or LocCount >= MinLocChanges
| extend NewIPs  = set_difference(CurrentIPs, PastIPs)
| extend NewLocs = set_difference(CurrentLocs, PastLocs)
| where array_length(NewIPs) > 0 or array_length(NewLocs) > 0
| extend
    CurrentUPNCount = array_length(CurrentUPNs),
    PastUPNCount = array_length(PastUPNs)
| project
    UniqueTokenIdentifier,
    PastLastSeen,
    CurrentFirstSeen,
    CurrentLastSeen,
    Gap,
    GapThreshold,
    CurrentUPNs,
    CurrentUPNCount,
    CurrentIPs,
    IPCount,
    NewIPs,
    CurrentLocs,
    LocCount,
    NewLocs,
    CurrentApps,
    PastApps,
    PastUPNs,
    ResultType
| order by Gap desc, CurrentLastSeen desc
| extend UserNames = strcat_array(CurrentUPNs, ",")
| extend NewIP = strcat_array(NewIPs, ",")
| extend FirstNewIP = tostring(NewIPs[0])
| extend NewLoc = strcat_array(NewLocs, ",")
| extend PastUPN = strcat_array(PastUPNs, ",")
| extend Source_Network_IPLocation = ""
| project
    Alert_Time_TW = datetime_utc_to_local(CurrentLastSeen, 'Asia/Taipei'),
    Alert_Time_UTC0 = CurrentLastSeen,
        Alert_Category_en = "Entra ID",
    Alert_SubCategory_en = "Anomaly Network Access User",
    Alert_Name_en = "Abnormal Sign-in Token Reuse",
    Alert_Description_en=strcat(
                         "At Taiwan time: ",
                         format_datetime(datetime_utc_to_local(CurrentLastSeen, "Asia/Taipei"), "yyyy-MM-dd HH:mm:ss"),
                         ", detected suspected Token Reuse behavior (UniqueTokenIdentifier appeared repeatedly and the interval reached the threshold).",
                         "Token:",
                         iff(isnotempty(UniqueTokenIdentifier), UniqueTokenIdentifier, "<NoTokenId>"),
                         ", users (last 2 days): ",
                         iff(isnotempty(tostring(UserNames)), tostring(UserNames), "<NoUserNames>"),
                         ", previous last seen: ",
                         format_datetime(datetime_utc_to_local(PastLastSeen, "Asia/Taipei"), "yyyy-MM-dd HH:mm:ss"),
                         ", current first seen: ",
                         format_datetime(datetime_utc_to_local(CurrentFirstSeen, "Asia/Taipei"), "yyyy-MM-dd HH:mm:ss"),
                         ", gap: ",
                         tostring(Gap),
                         " days",
                         ", IP count: ",
                         tostring(IPCount),
                         ", current sign-in IP: ",
                         iff(isnotempty(tostring(NewIP)), tostring(NewIP), "<NoNewIP>"),
                         ", location count: ",
                         tostring(LocCount),
                         ", current sign-in location: ",
                         iff(isnotempty(tostring(NewLoc)), tostring(NewLoc), "<NoNewLoc>"),
                         "."
                     ),
    Alert_TriageStep_en=strcat(
                        " 1. Check whether this is truly token reuse. The reused IP is: ",
                        iff(isnotempty(tostring(NewIP)), tostring(NewIP), "<NoNewIPs>"),
                        ", current sign-in location: ",
                        iff(isnotempty(tostring(NewLoc)), tostring(NewLoc), "<NoNewLocs>"),
                        "  to determine whether it is an uncommon source, cross-country/cross-region, or anonymous cloud egress.",
                        " 2. Check whether multiple accounts share the same token. Current triggering account count: ",
                        tostring(CurrentUPNCount),
                        ", accounts that previously triggered this token: ",
                        tostring(PastUPN),
                        "; if the UPN count is greater than 1, prioritize suspicion of token leakage or proxy/automation abuse.",
                        " 3. Check the applications currently involved in sign-in: ",
                        iff(isnotempty(tostring(CurrentApps)), tostring(CurrentApps), "<NoCurrentApps>"),
                        "previous sign-in applications: ",
                        iff(isnotempty(tostring(PastApps)), tostring(PastApps), "<NoPastApps>"),
                        "  to determine whether they include administrative/highly sensitive applications or abnormally newly added apps."
                    ),
    Alert_Containment_en=strcat(
                         "1. Immediately revoke sign-in tokens/sessions and force re-sign-in for users (last 2 days): ",
                         iff(isnotempty(tostring(UserNames)), tostring(UserNames), "<NoUserNames>"),
                         "  to block continued access using the reused token.  ",
                         "2. If the current sign-in IP or location is abnormal, immediately block the current sign-in IP: ",
                         iff(isnotempty(tostring(NewIP)), tostring(NewIP), "<NoNewIP>"),
                         ", current sign-in location: ",
                         iff(isnotempty(tostring(NewLoc)), tostring(NewLoc), "<NoNewLoc>"),
                         ", and tighten Conditional Access (MFA/compliant device/named location).  ",
                         "3. Immediately require account security recovery: reset the password, re-register MFA, and check for suspicious devices or added authentication methods.  "
                     ),
    Alert_Remediation_en=strcat(
                         "1. Strengthen risk-based access: establish Conditional Access policies to block new IPs/new locations.  ",
                         "2. Implement token protection and endpoint governance: promote compliant devices, reduce long-lived token risk, and restrict access from unmanaged devices or legacy clients.  ",
                         "3. Inventory automation and applications: regularly check for abnormally added applications and high-privilege applications, and remove unnecessary permissions and old credentials.  ",
                         "4. Establish automated response: automatically revoke tokens, block IPs, notify users for confirmation, and create incident tickets for follow-up investigation when alerts trigger."
                     ),
            Event_Code = ResultType,
    //Event_Description = ResultDescription,
    Event_TimeRange_Start_UTC0 = CurrentFirstSeen,
    Event_TimeRange_End_UTC0 = CurrentLastSeen,
    Event_TimeRange_Start_TW = datetime_utc_to_local(CurrentFirstSeen, 'Asia/Taipei'),
    Event_TimeRange_End_TW = datetime_utc_to_local(CurrentFirstSeen, 'Asia/Taipei'),
    Source_Identity_FullName = UserNames,
    Source_Network_IPAddress = NewIP,
    Source_Network_IPLocation = Source_Network_IPLocation,
    Target_Identity_FullName = UniqueTokenIdentifier,
    //Target_Network_IPAddress = UniqueTokenIdentifier,
    //Target_Resource_ID = "",
    Target_Resource_Name = "Microsoft Entra ID",
    Target_Resource_Type = "Microsoft Entra ID"
kind: Scheduled
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/eDCRule/Analytic%20Rules/%5BEntra%20ID%5D%20Suspicious%20Continuous%20OAuth%20Token%20Usage.yaml
version: 1.0.0
status: Available
requiredDataConnectors:
- dataTypes:
  - AADNonInteractiveUserSignInLogs
  - SigninLogs
  connectorId: AzureActiveDirectory
alertDetailsOverride:
  alertDynamicProperties: []
relevantTechniques:
- T1606
query: |
  // =====================
  // Low-noise Token Reuse Detector (fixed timespan calc)
  // =====================
  let GapDays = 1;
  let MinIPChanges = 2;
  let MinLocChanges = 2;
  let ExcludeApps = dynamic([
      "Microsoft Authentication Broker",
      "Microsoft Teams",
      "Office 365"
      ]);
  let Src =
      union isfuzzy=true
          AADNonInteractiveUserSignInLogs,
          SigninLogs
      | where ResultType == 0
      | where isnotempty(UniqueTokenIdentifier);
  let Past =
      Src
      | where TimeGenerated between (ago(14d) .. ago(1d))
      | summarize
          PastLastSeen = max(TimeGenerated),
          PastUPNs     = make_set(UserPrincipalName, 20),
          PastIPs      = make_set(IPAddress, 50),
          PastLocs     = make_set(tostring(Location), 50),
          PastApps     = make_set(AppDisplayName, 50)
          by UniqueTokenIdentifier;
  let Last24h =
      Src
      | where TimeGenerated >= ago(1d)
      //| where AppDisplayName !in (ExcludeApps)
      | summarize
          CurrentFirstSeen = min(TimeGenerated),
          CurrentLastSeen  = max(TimeGenerated),
          CurrentUPNs      = make_set(UserPrincipalName, 20),
          CurrentIPs       = make_set(IPAddress, 50),
          CurrentLocs      = make_set(tostring(Location), 50),
          CurrentApps      = make_set(AppDisplayName, 50),
          IPCount          = dcount(IPAddress),
          LocCount         = dcount(tostring(Location))
          by UniqueTokenIdentifier, ResultType;
  Last24h
  | join kind=inner Past on UniqueTokenIdentifier
  | extend Gap = CurrentFirstSeen - PastLastSeen
  | extend GapThreshold = totimespan(strcat(GapDays, "d"))
  | where Gap >= GapThreshold
  | where IPCount >= MinIPChanges or LocCount >= MinLocChanges
  | extend NewIPs  = set_difference(CurrentIPs, PastIPs)
  | extend NewLocs = set_difference(CurrentLocs, PastLocs)
  | where array_length(NewIPs) > 0 or array_length(NewLocs) > 0
  | extend
      CurrentUPNCount = array_length(CurrentUPNs),
      PastUPNCount = array_length(PastUPNs)
  | project
      UniqueTokenIdentifier,
      PastLastSeen,
      CurrentFirstSeen,
      CurrentLastSeen,
      Gap,
      GapThreshold,
      CurrentUPNs,
      CurrentUPNCount,
      CurrentIPs,
      IPCount,
      NewIPs,
      CurrentLocs,
      LocCount,
      NewLocs,
      CurrentApps,
      PastApps,
      PastUPNs,
      ResultType
  | order by Gap desc, CurrentLastSeen desc
  | extend UserNames = strcat_array(CurrentUPNs, ",")
  | extend NewIP = strcat_array(NewIPs, ",")
  | extend FirstNewIP = tostring(NewIPs[0])
  | extend NewLoc = strcat_array(NewLocs, ",")
  | extend PastUPN = strcat_array(PastUPNs, ",")
  | extend Source_Network_IPLocation = ""
  | project
      Alert_Time_TW = datetime_utc_to_local(CurrentLastSeen, 'Asia/Taipei'),
      Alert_Time_UTC0 = CurrentLastSeen,
          Alert_Category_en = "Entra ID",
      Alert_SubCategory_en = "Anomaly Network Access User",
      Alert_Name_en = "Abnormal Sign-in Token Reuse",
      Alert_Description_en=strcat(
                           "At Taiwan time: ",
                           format_datetime(datetime_utc_to_local(CurrentLastSeen, "Asia/Taipei"), "yyyy-MM-dd HH:mm:ss"),
                           ", detected suspected Token Reuse behavior (UniqueTokenIdentifier appeared repeatedly and the interval reached the threshold).",
                           "Token:",
                           iff(isnotempty(UniqueTokenIdentifier), UniqueTokenIdentifier, "<NoTokenId>"),
                           ", users (last 2 days): ",
                           iff(isnotempty(tostring(UserNames)), tostring(UserNames), "<NoUserNames>"),
                           ", previous last seen: ",
                           format_datetime(datetime_utc_to_local(PastLastSeen, "Asia/Taipei"), "yyyy-MM-dd HH:mm:ss"),
                           ", current first seen: ",
                           format_datetime(datetime_utc_to_local(CurrentFirstSeen, "Asia/Taipei"), "yyyy-MM-dd HH:mm:ss"),
                           ", gap: ",
                           tostring(Gap),
                           " days",
                           ", IP count: ",
                           tostring(IPCount),
                           ", current sign-in IP: ",
                           iff(isnotempty(tostring(NewIP)), tostring(NewIP), "<NoNewIP>"),
                           ", location count: ",
                           tostring(LocCount),
                           ", current sign-in location: ",
                           iff(isnotempty(tostring(NewLoc)), tostring(NewLoc), "<NoNewLoc>"),
                           "."
                       ),
      Alert_TriageStep_en=strcat(
                          " 1. Check whether this is truly token reuse. The reused IP is: ",
                          iff(isnotempty(tostring(NewIP)), tostring(NewIP), "<NoNewIPs>"),
                          ", current sign-in location: ",
                          iff(isnotempty(tostring(NewLoc)), tostring(NewLoc), "<NoNewLocs>"),
                          "  to determine whether it is an uncommon source, cross-country/cross-region, or anonymous cloud egress.",
                          " 2. Check whether multiple accounts share the same token. Current triggering account count: ",
                          tostring(CurrentUPNCount),
                          ", accounts that previously triggered this token: ",
                          tostring(PastUPN),
                          "; if the UPN count is greater than 1, prioritize suspicion of token leakage or proxy/automation abuse.",
                          " 3. Check the applications currently involved in sign-in: ",
                          iff(isnotempty(tostring(CurrentApps)), tostring(CurrentApps), "<NoCurrentApps>"),
                          "previous sign-in applications: ",
                          iff(isnotempty(tostring(PastApps)), tostring(PastApps), "<NoPastApps>"),
                          "  to determine whether they include administrative/highly sensitive applications or abnormally newly added apps."
                      ),
      Alert_Containment_en=strcat(
                           "1. Immediately revoke sign-in tokens/sessions and force re-sign-in for users (last 2 days): ",
                           iff(isnotempty(tostring(UserNames)), tostring(UserNames), "<NoUserNames>"),
                           "  to block continued access using the reused token.  ",
                           "2. If the current sign-in IP or location is abnormal, immediately block the current sign-in IP: ",
                           iff(isnotempty(tostring(NewIP)), tostring(NewIP), "<NoNewIP>"),
                           ", current sign-in location: ",
                           iff(isnotempty(tostring(NewLoc)), tostring(NewLoc), "<NoNewLoc>"),
                           ", and tighten Conditional Access (MFA/compliant device/named location).  ",
                           "3. Immediately require account security recovery: reset the password, re-register MFA, and check for suspicious devices or added authentication methods.  "
                       ),
      Alert_Remediation_en=strcat(
                           "1. Strengthen risk-based access: establish Conditional Access policies to block new IPs/new locations.  ",
                           "2. Implement token protection and endpoint governance: promote compliant devices, reduce long-lived token risk, and restrict access from unmanaged devices or legacy clients.  ",
                           "3. Inventory automation and applications: regularly check for abnormally added applications and high-privilege applications, and remove unnecessary permissions and old credentials.  ",
                           "4. Establish automated response: automatically revoke tokens, block IPs, notify users for confirmation, and create incident tickets for follow-up investigation when alerts trigger."
                       ),
              Event_Code = ResultType,
      //Event_Description = ResultDescription,
      Event_TimeRange_Start_UTC0 = CurrentFirstSeen,
      Event_TimeRange_End_UTC0 = CurrentLastSeen,
      Event_TimeRange_Start_TW = datetime_utc_to_local(CurrentFirstSeen, 'Asia/Taipei'),
      Event_TimeRange_End_TW = datetime_utc_to_local(CurrentFirstSeen, 'Asia/Taipei'),
      Source_Identity_FullName = UserNames,
      Source_Network_IPAddress = NewIP,
      Source_Network_IPLocation = Source_Network_IPLocation,
      Target_Identity_FullName = UniqueTokenIdentifier,
      //Target_Network_IPAddress = UniqueTokenIdentifier,
      //Target_Resource_ID = "",
      Target_Resource_Name = "Microsoft Entra ID",
      Target_Resource_Type = "Microsoft Entra ID"
tactics:
- CredentialAccess
queryFrequency: 1d
id: 67802748-435b-4f80-9f61-b9a9ac6ea15c
triggerThreshold: 0
triggerOperator: gt
name: '[Entra ID] Suspicious Continuous OAuth Token Usage'
queryPeriod: 1d
severity: High
entityMappings:
- fieldMappings:
  - columnName: Source_Identity_FullName
    identifier: Name
  entityType: Account
- fieldMappings:
  - columnName: Source_Network_IPAddress
    identifier: Address
  entityType: IP
description: |
  Detects repeated use of the same OAuth token across different IPs or locations over time. This can indicate token theft or session abuse.
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workspace": {
      "type": "String"
    }
  },
  "resources": [
    {
      "apiVersion": "2024-01-01-preview",
      "id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/67802748-435b-4f80-9f61-b9a9ac6ea15c')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/67802748-435b-4f80-9f61-b9a9ac6ea15c')]",
      "properties": {
        "alertDetailsOverride": {
          "alertDynamicProperties": []
        },
        "alertRuleTemplateName": "67802748-435b-4f80-9f61-b9a9ac6ea15c",
        "customDetails": null,
        "description": "Detects repeated use of the same OAuth token across different IPs or locations over time. This can indicate token theft or session abuse.\n",
        "displayName": "[Entra ID] Suspicious Continuous OAuth Token Usage",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "Source_Identity_FullName",
                "identifier": "Name"
              }
            ]
          },
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "columnName": "Source_Network_IPAddress",
                "identifier": "Address"
              }
            ]
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/eDCRule/Analytic%20Rules/%5BEntra%20ID%5D%20Suspicious%20Continuous%20OAuth%20Token%20Usage.yaml",
        "query": "// =====================\n// Low-noise Token Reuse Detector (fixed timespan calc)\n// =====================\nlet GapDays = 1;\nlet MinIPChanges = 2;\nlet MinLocChanges = 2;\nlet ExcludeApps = dynamic([\n    \"Microsoft Authentication Broker\",\n    \"Microsoft Teams\",\n    \"Office 365\"\n    ]);\nlet Src =\n    union isfuzzy=true\n        AADNonInteractiveUserSignInLogs,\n        SigninLogs\n    | where ResultType == 0\n    | where isnotempty(UniqueTokenIdentifier);\nlet Past =\n    Src\n    | where TimeGenerated between (ago(14d) .. ago(1d))\n    | summarize\n        PastLastSeen = max(TimeGenerated),\n        PastUPNs     = make_set(UserPrincipalName, 20),\n        PastIPs      = make_set(IPAddress, 50),\n        PastLocs     = make_set(tostring(Location), 50),\n        PastApps     = make_set(AppDisplayName, 50)\n        by UniqueTokenIdentifier;\nlet Last24h =\n    Src\n    | where TimeGenerated >= ago(1d)\n    //| where AppDisplayName !in (ExcludeApps)\n    | summarize\n        CurrentFirstSeen = min(TimeGenerated),\n        CurrentLastSeen  = max(TimeGenerated),\n        CurrentUPNs      = make_set(UserPrincipalName, 20),\n        CurrentIPs       = make_set(IPAddress, 50),\n        CurrentLocs      = make_set(tostring(Location), 50),\n        CurrentApps      = make_set(AppDisplayName, 50),\n        IPCount          = dcount(IPAddress),\n        LocCount         = dcount(tostring(Location))\n        by UniqueTokenIdentifier, ResultType;\nLast24h\n| join kind=inner Past on UniqueTokenIdentifier\n| extend Gap = CurrentFirstSeen - PastLastSeen\n| extend GapThreshold = totimespan(strcat(GapDays, \"d\"))\n| where Gap >= GapThreshold\n| where IPCount >= MinIPChanges or LocCount >= MinLocChanges\n| extend NewIPs  = set_difference(CurrentIPs, PastIPs)\n| extend NewLocs = set_difference(CurrentLocs, PastLocs)\n| where array_length(NewIPs) > 0 or array_length(NewLocs) > 0\n| extend\n    CurrentUPNCount = array_length(CurrentUPNs),\n    PastUPNCount = array_length(PastUPNs)\n| project\n    UniqueTokenIdentifier,\n    PastLastSeen,\n    CurrentFirstSeen,\n    CurrentLastSeen,\n    Gap,\n    GapThreshold,\n    CurrentUPNs,\n    CurrentUPNCount,\n    CurrentIPs,\n    IPCount,\n    NewIPs,\n    CurrentLocs,\n    LocCount,\n    NewLocs,\n    CurrentApps,\n    PastApps,\n    PastUPNs,\n    ResultType\n| order by Gap desc, CurrentLastSeen desc\n| extend UserNames = strcat_array(CurrentUPNs, \",\")\n| extend NewIP = strcat_array(NewIPs, \",\")\n| extend FirstNewIP = tostring(NewIPs[0])\n| extend NewLoc = strcat_array(NewLocs, \",\")\n| extend PastUPN = strcat_array(PastUPNs, \",\")\n| extend Source_Network_IPLocation = \"\"\n| project\n    Alert_Time_TW = datetime_utc_to_local(CurrentLastSeen, 'Asia/Taipei'),\n    Alert_Time_UTC0 = CurrentLastSeen,\n        Alert_Category_en = \"Entra ID\",\n    Alert_SubCategory_en = \"Anomaly Network Access User\",\n    Alert_Name_en = \"Abnormal Sign-in Token Reuse\",\n    Alert_Description_en=strcat(\n                         \"At Taiwan time: \",\n                         format_datetime(datetime_utc_to_local(CurrentLastSeen, \"Asia/Taipei\"), \"yyyy-MM-dd HH:mm:ss\"),\n                         \", detected suspected Token Reuse behavior (UniqueTokenIdentifier appeared repeatedly and the interval reached the threshold).\",\n                         \"Token:\",\n                         iff(isnotempty(UniqueTokenIdentifier), UniqueTokenIdentifier, \"<NoTokenId>\"),\n                         \", users (last 2 days): \",\n                         iff(isnotempty(tostring(UserNames)), tostring(UserNames), \"<NoUserNames>\"),\n                         \", previous last seen: \",\n                         format_datetime(datetime_utc_to_local(PastLastSeen, \"Asia/Taipei\"), \"yyyy-MM-dd HH:mm:ss\"),\n                         \", current first seen: \",\n                         format_datetime(datetime_utc_to_local(CurrentFirstSeen, \"Asia/Taipei\"), \"yyyy-MM-dd HH:mm:ss\"),\n                         \", gap: \",\n                         tostring(Gap),\n                         \" days\",\n                         \", IP count: \",\n                         tostring(IPCount),\n                         \", current sign-in IP: \",\n                         iff(isnotempty(tostring(NewIP)), tostring(NewIP), \"<NoNewIP>\"),\n                         \", location count: \",\n                         tostring(LocCount),\n                         \", current sign-in location: \",\n                         iff(isnotempty(tostring(NewLoc)), tostring(NewLoc), \"<NoNewLoc>\"),\n                         \".\"\n                     ),\n    Alert_TriageStep_en=strcat(\n                        \" 1. Check whether this is truly token reuse. The reused IP is: \",\n                        iff(isnotempty(tostring(NewIP)), tostring(NewIP), \"<NoNewIPs>\"),\n                        \", current sign-in location: \",\n                        iff(isnotempty(tostring(NewLoc)), tostring(NewLoc), \"<NoNewLocs>\"),\n                        \"  to determine whether it is an uncommon source, cross-country/cross-region, or anonymous cloud egress.\",\n                        \" 2. Check whether multiple accounts share the same token. Current triggering account count: \",\n                        tostring(CurrentUPNCount),\n                        \", accounts that previously triggered this token: \",\n                        tostring(PastUPN),\n                        \"; if the UPN count is greater than 1, prioritize suspicion of token leakage or proxy/automation abuse.\",\n                        \" 3. Check the applications currently involved in sign-in: \",\n                        iff(isnotempty(tostring(CurrentApps)), tostring(CurrentApps), \"<NoCurrentApps>\"),\n                        \"previous sign-in applications: \",\n                        iff(isnotempty(tostring(PastApps)), tostring(PastApps), \"<NoPastApps>\"),\n                        \"  to determine whether they include administrative/highly sensitive applications or abnormally newly added apps.\"\n                    ),\n    Alert_Containment_en=strcat(\n                         \"1. Immediately revoke sign-in tokens/sessions and force re-sign-in for users (last 2 days): \",\n                         iff(isnotempty(tostring(UserNames)), tostring(UserNames), \"<NoUserNames>\"),\n                         \"  to block continued access using the reused token.  \",\n                         \"2. If the current sign-in IP or location is abnormal, immediately block the current sign-in IP: \",\n                         iff(isnotempty(tostring(NewIP)), tostring(NewIP), \"<NoNewIP>\"),\n                         \", current sign-in location: \",\n                         iff(isnotempty(tostring(NewLoc)), tostring(NewLoc), \"<NoNewLoc>\"),\n                         \", and tighten Conditional Access (MFA/compliant device/named location).  \",\n                         \"3. Immediately require account security recovery: reset the password, re-register MFA, and check for suspicious devices or added authentication methods.  \"\n                     ),\n    Alert_Remediation_en=strcat(\n                         \"1. Strengthen risk-based access: establish Conditional Access policies to block new IPs/new locations.  \",\n                         \"2. Implement token protection and endpoint governance: promote compliant devices, reduce long-lived token risk, and restrict access from unmanaged devices or legacy clients.  \",\n                         \"3. Inventory automation and applications: regularly check for abnormally added applications and high-privilege applications, and remove unnecessary permissions and old credentials.  \",\n                         \"4. Establish automated response: automatically revoke tokens, block IPs, notify users for confirmation, and create incident tickets for follow-up investigation when alerts trigger.\"\n                     ),\n            Event_Code = ResultType,\n    //Event_Description = ResultDescription,\n    Event_TimeRange_Start_UTC0 = CurrentFirstSeen,\n    Event_TimeRange_End_UTC0 = CurrentLastSeen,\n    Event_TimeRange_Start_TW = datetime_utc_to_local(CurrentFirstSeen, 'Asia/Taipei'),\n    Event_TimeRange_End_TW = datetime_utc_to_local(CurrentFirstSeen, 'Asia/Taipei'),\n    Source_Identity_FullName = UserNames,\n    Source_Network_IPAddress = NewIP,\n    Source_Network_IPLocation = Source_Network_IPLocation,\n    Target_Identity_FullName = UniqueTokenIdentifier,\n    //Target_Network_IPAddress = UniqueTokenIdentifier,\n    //Target_Resource_ID = \"\",\n    Target_Resource_Name = \"Microsoft Entra ID\",\n    Target_Resource_Type = \"Microsoft Entra ID\"\n",
        "queryFrequency": "P1D",
        "queryPeriod": "P1D",
        "severity": "High",
        "status": "Available",
        "subTechniques": [],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "CredentialAccess"
        ],
        "techniques": [
          "T1606"
        ],
        "templateVersion": "1.0.0",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}