Msal Client Credentials
AzureAD/microsoft-authentication-library-for-dotnet
Client Credentials Flow for service-to-service (daemon) authentication in MSAL.NET without user involvement
A skill your agent uses when asked to trace authentication flows, analyze SessionId chains, investigate token reuse vs interactive MFA, or assess geographic anomalies in sign-ins.
$ npx skills add SCStelz/security-investigator --skill authentication-tracing -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install SCStelz/security-investigator authentication-tracing --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/SCStelz/security-investigator.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.github/skills/authentication-tracing .claude/skills/authentication-tracing && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "authentication-tracing" agent skill from https://github.com/SCStelz/security-investigator/tree/main/.github/skills/authentication-tracing into .claude/skills/authentication-tracing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "authentication-tracing", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/SCStelz/security-investigator/tree/main/.github/skills/authentication-tracingType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add SCStelz/security-investigator --skill authentication-tracing -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install SCStelz/security-investigator authentication-tracing --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/SCStelz/security-investigator.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.github/skills/authentication-tracing .agents/skills/authentication-tracing && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "authentication-tracing" agent skill from https://github.com/SCStelz/security-investigator/tree/main/.github/skills/authentication-tracing into .agents/skills/authentication-tracing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "authentication-tracing", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add SCStelz/security-investigator --skill authentication-tracing -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install SCStelz/security-investigator authentication-tracing --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/SCStelz/security-investigator.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.github/skills/authentication-tracing .cursor/skills/authentication-tracing && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "authentication-tracing" agent skill from https://github.com/SCStelz/security-investigator/tree/main/.github/skills/authentication-tracing into .cursor/skills/authentication-tracing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "authentication-tracing", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/SCStelz/security-investigator.git --path .github/skills/authentication-tracing--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add SCStelz/security-investigator --skill authentication-tracing -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install SCStelz/security-investigator authentication-tracing --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/SCStelz/security-investigator.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.github/skills/authentication-tracing .gemini/skills/authentication-tracing && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "authentication-tracing" agent skill from https://github.com/SCStelz/security-investigator/tree/main/.github/skills/authentication-tracing into .gemini/skills/authentication-tracing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "authentication-tracing", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install SCStelz/security-investigator authentication-tracingInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add SCStelz/security-investigator --skill authentication-tracing -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/SCStelz/security-investigator.git skills-src && mkdir -p .github/skills && cp -r skills-src/.github/skills/authentication-tracing .github/skills/authentication-tracing && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "authentication-tracing" agent skill from https://github.com/SCStelz/security-investigator/tree/main/.github/skills/authentication-tracing into .github/skills/authentication-tracing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "authentication-tracing", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add SCStelz/security-investigator --skill authentication-tracing -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install SCStelz/security-investigator authentication-tracing --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/SCStelz/security-investigator.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.github/skills/authentication-tracing .opencode/skills/authentication-tracing && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "authentication-tracing" agent skill from https://github.com/SCStelz/security-investigator/tree/main/.github/skills/authentication-tracing into .opencode/skills/authentication-tracing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "authentication-tracing", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
authentication-tracingA skill your agent uses when asked to trace authentication flows, analyze SessionId chains, investigate token reuse vs interactive MFA, or assess geographic anomalies in sign-ins.
Authentication Tracing is an agent skill from SCStelz/security-investigator. Use this skill when asked to trace authentication flows, analyze SessionId chains, investigate token reuse vs interactive MFA, or assess geographic anomalies in sign-ins. Triggers on keywords like "trace authentication", "trace back to interactive MFA", "SessionId analysis", "token reuse", "geographic anomaly", "impossible travel", or when investigating suspicious sign-in locations. This skill provides forensic analysis of Entra ID authentication chains to distinguish legitimate activity from credential/token…
Its SKILL.md is about 8.6k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Backend & APIs, covering Authentication and Digital forensics. It works with Microsoft Entra ID. The repository describes itself as: Automated security investigation tool using Microsoft MCP Servers, GitHub Copilot, Python Modules and custom copilot-instructions. The licence is MIT.
9 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit b38152e. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are kql and json).
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Authentication Tracing loads about 8.6k tokens when it runs. Until then it costs about 136 tokens; SKILL.md has 3,234 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from SCStelz/security-investigator at commit b38152e, republished under its MIT licence (© SCStelz). 3,234 words, ~8,573 tokens.
.claude/skills/authentication-tracing/SKILL.md (or your agent's skills folder).This skill performs forensic analysis of Entra ID authentication flows to determine whether anomalous sign-ins represent:
The key distinction is whether the user actively performed MFA at a suspicious location or if the authentication used a refresh token from a prior session.
🚨 MANDATORY CHECKPOINT: Before providing ANY risk assessment for authentication anomalies:
Before executing ANY authentication tracing queries, you MUST:
ip_enrichment array) - PRIMARY DATA SOURCESkipping these steps will result in incomplete or incorrect analysis.
When investigating anomalous sign-ins (e.g., from new countries, IPs, or devices), it's critical to determine whether the user actively performed MFA at that location or if the authentication used a refresh token from a prior session.
| Value | Meaning | Implication |
|---|---|---|
RequestSequence: 1 or higher | Interactive authentication | User was challenged and responded |
RequestSequence: 0 | Token-based authentication | No user interaction required |
Interactive Pattern:
RequestSequence > 0Token Reuse Pattern:
authenticationStepDateTime references a time when NO interactive auth occurred, it indicates token reuseRequestSequence > 0 to trace token originCRITICAL: The investigation JSON contains a comprehensive ip_enrichment array with authoritative detection flags.
Always reference this data FIRST before making VPN/proxy/Tor determinations.
{
"ip": "203.0.113.42", // ← KEY: Use "ip" field, not "ip_address"
"city": "Singapore",
"region": "Singapore",
"country": "SG",
"org": "AS12345 Example Hosting Ltd",
"asn": "AS12345",
"timezone": "Asia/Singapore",
"risk_level": "HIGH", // ← Overall risk assessment (LOW/MEDIUM/HIGH)
"assessment": "⚠️ Threat Intelligence Match: Commercial VPN Service Detected",
"is_vpn": true, // ← PRIMARY VPN DETECTION FLAG (ipinfo.io detection)
"is_proxy": false, // ← PRIMARY PROXY DETECTION FLAG
"is_tor": false, // ← PRIMARY TOR DETECTION FLAG
"abuse_confidence_score": 0, // ← AbuseIPDB score 0-100 (0=clean, 75+=high risk)
"total_reports": 2, // ← Number of abuse reports in AbuseIPDB
"is_whitelisted": false,
"threat_description": "Commercial VPN Service: Known Anonymization Infrastructure",
"anomaly_type": "NewInteractiveIP",
"first_seen": "2025-10-16", // ← First sign-in from this IP (date string)
"last_seen": "2025-10-16", // ← Last sign-in from this IP (date string)
"hit_count": 5, // ← Number of anomaly detections
"signin_count": 8, // ← Total sign-ins from this IP
"success_count": 7, // ← Successful authentications
"failure_count": 1, // ← Failed authentications
"last_auth_result_detail": "MFA requirement satisfied by claim in the token",
"threat_detected": false, // ← Legacy field (use threat_description instead)
"threat_confidence": 0,
"threat_tlp_level": "",
"threat_activity_groups": ""
}CRITICAL: Always use ip_enrichment[].ip to match IPs, NOT ip_address!
| Field | Purpose | Usage Example |
|---|---|---|
| is_vpn | Definitive VPN detection | is_vpn: true → Confirmed VPN endpoint (don't infer, use this flag) |
| is_proxy | Definitive proxy detection | is_proxy: true → Confirmed proxy (anonymized traffic) |
| is_tor | Definitive Tor detection | is_tor: true → Confirmed Tor exit node (high anonymity risk) |
| abuse_confidence_score | AbuseIPDB reputation (0-100) | >= 75 = High risk, >= 25 = Medium risk, 0 = Clean |
| threat_detected | Threat intel match flag | true → IP matches ThreatIntelIndicators table |
| threat_description | Threat intel details | "Surfshark VPN", "Malicious activity detected", etc. |
| org / asn | Network ownership | AS9009 = M247 Europe (VPN infrastructure provider) |
| signin_count | Total sign-ins from IP | High count (>100) = established pattern vs transient |
| last_auth_result_detail | Authentication method | "MFA satisfied by token" vs "Correct password" = interactive vs token reuse |
| first_seen / last_seen | Temporal pattern | Single day = transient, multi-day = established behavior |
is_vpn, is_proxy, is_tor) - Most authoritative sourceabuse_confidence_score, total_reports) - Community-validated risk datathreat_detected, threat_description) - IOC matches from Sentinelorg, asn, company_type) - Infrastructure context (hosting, ISP, etc.)last_auth_result_detail, signin_count) - Behavioral context⚠️ NEVER say "likely VPN" or "probably proxy" if enrichment data has explicit boolean flags!
Scenario: Anomalous sign-ins detected from new IP/location. Determine if user performed fresh MFA or reused token.
| Lookback | Tool | Table | Why |
|---|---|---|---|
| ≤ 30 days (default) | RunAdvancedHuntingQuery | EntraIdSignInEvents | Single table covers interactive + non-interactive. No union needed. Direct columns for Country, City, Browser, UserAgent. Free on Analytics tier. |
| > 30 days | mcp_sentinel-data_query_lake | union SigninLogs, AADNonInteractiveUserSignInLogs | AH Graph API caps at 30d. Data Lake retains 90d+. See Data Lake Fallback Queries below. |
Column mapping — EntraIdSignInEvents vs SigninLogs:
| EntraIdSignInEvents (AH) | SigninLogs (Data Lake) | Notes |
|---|---|---|
Timestamp | TimeGenerated | |
AccountUpn | UserPrincipalName | |
Application | AppDisplayName | |
ErrorCode (int) | ResultType (string) | AH: ErrorCode == 0, DL: ResultType == "0" |
Country, City (direct strings) | Location or parse_json(LocationDetails) | No parsing needed in AH |
LogonType (JSON array) | Separate tables (SigninLogs vs AADNonInteractive) | AH: has "interactiveUser", DL: check which table |
AuthenticationRequirement | AuthenticationRequirement | Same values: singleFactorAuthentication, multiFactorAuthentication |
UserAgent, Browser, OSPlatform | parse_json(DeviceDetail) | Direct columns in AH |
UniqueTokenId | (not available) | AH-only — token-level forensics |
SessionId | SessionId | Same |
| (not available) | AuthenticationDetails (JSON array) | DL-only — per-step RequestSequence + authenticationMethod |
Key trade-off:
AuthenticationDetails(Data Lake only) provides per-stepRequestSequenceandauthenticationMethod("Password", "Previously satisfied", "Mobile app notification").EntraIdSignInEventsreplaces this with row-levelLogonType(interactive vs non-interactive) +AuthenticationRequirement(singleFactor/multiFactor) +UniqueTokenId. Both achieve the same forensic goal — determining interactive MFA vs token reuse — through different signals.
⚠️ Table name casing: Capital I in SignIn — EntraIdSignInEvents, NOT EntraIdSigninEvents.
⚠️ LogonType is a JSON array string (e.g., ["interactiveUser"]). Use has for filtering, NOT ==.
CRITICAL: START WITH SessionId - This is Your Primary and Most Efficient Investigation Pattern:
AVOID chronological searching without SessionId - it requires multiple queries and is less efficient.
Tool: RunAdvancedHuntingQuery
This single query gives you SessionId AND enough context to determine next steps:
let suspicious_ips = dynamic(["<IP_1>", "<IP_2>"]); // All suspicious IPs
EntraIdSignInEvents
| where Timestamp > ago(30d)
| where AccountUpn =~ '<UPN>'
| where IPAddress in (suspicious_ips)
| project Timestamp, IPAddress, Country, City, Application,
SessionId, LogonType, AuthenticationRequirement,
UserAgent, Browser, OSPlatform, ErrorCode, UniqueTokenId
| order by Timestamp asc
| take 50What This Returns:
Critical Decision Point:
Tool: RunAdvancedHuntingQuery
Once you have SessionId from Step 1, query ALL authentications in that session:
let target_session_id = "<SESSION_ID_FROM_STEP_1>";
EntraIdSignInEvents
| where Timestamp > ago(30d)
| where AccountUpn =~ '<UPN>'
| where SessionId == target_session_id
| project Timestamp, IPAddress, Country, City, Application,
LogonType, AuthenticationRequirement, ErrorCode,
UserAgent, Browser, OSPlatform, UniqueTokenId
| order by Timestamp ascThis Single Query Reveals:
LogonType has "interactiveUser" + AuthenticationRequirement == "multiFactorAuthentication")LogonType has "nonInteractiveUser" — all subsequent events using cached tokens)UniqueTokenId — same token reused across IPs = session continuity)Critical Evidence - What SessionId Indicates:
Analysis Pattern:
LogonType has "interactiveUser" → User performed interactive authentication at that IP/locationAuthenticationRequirement → multiFactorAuthentication = MFA was required and satisfied; singleFactorAuthentication = password-onlyLogonType has "nonInteractiveUser" = token reuse (expected OAuth flow)UniqueTokenId — same token ID across geographically distant IPs = session continuity (could be VPN OR stolen token)Tool: RunAdvancedHuntingQuery (≤30d) or Data Lake fallback (>30d)
Use this when Step 2 shows only nonInteractiveUser logon types (no interactive auth in the session)
Query Pattern:
EntraIdSignInEvents
| where Timestamp > ago(30d)
| where AccountUpn =~ '<UPN>'
| where LogonType has "interactiveUser"
| where ErrorCode == 0
| summarize
SignInCount = count(),
Apps = make_set(Application, 5),
Countries = make_set(Country, 3),
AuthReqs = make_set(AuthenticationRequirement),
TokenIds = dcount(UniqueTokenId),
FirstSeen = min(Timestamp),
LastSeen = max(Timestamp)
by IPAddress, SessionId
| order by LastSeen desc
| take 20What This Returns:
AuthenticationRequirement per IP — reveals whether MFA was required or bypassedTokenIds count — how many distinct tokens were issued from each IP/session pairProgressive expansion (if AH 30d window is insufficient):
Use these when the AH 30d window is insufficient — e.g., tracing token origins older than 30 days.
Tool: mcp_sentinel-data_query_lake with workspaceId
Step 1 (Data Lake):
let suspicious_ips = dynamic(["<IP_1>", "<IP_2>"]);
union isfuzzy=true SigninLogs, AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(90d)
| where UserPrincipalName =~ '<UPN>'
| where IPAddress in (suspicious_ips)
| project TimeGenerated, IPAddress, Location, AppDisplayName,
SessionId = tostring(SessionId), UserAgent, ResultType, CorrelationId
| order by TimeGenerated asc
| take 50Step 2 (Data Lake) — with per-step auth detail:
let target_session_id = "<SESSION_ID>";
union isfuzzy=true SigninLogs, AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(90d)
| where UserPrincipalName =~ '<UPN>'
| where SessionId == target_session_id
| extend AuthDetails = parse_json(tostring(AuthenticationDetails))
| mv-expand AuthDetails
| extend AuthMethod = tostring(AuthDetails.authenticationMethod)
| extend AuthStepDateTime = todatetime(AuthDetails.authenticationStepDateTime)
| extend RequestSeq = toint(AuthDetails.RequestSequence)
| project TimeGenerated, IPAddress, Location, AppDisplayName,
AuthMethod, AuthStepDateTime, RequestSeq, UserAgent, ResultType
| order by TimeGenerated ascData Lake advantage:
AuthenticationDetailsprovides granular per-stepRequestSequenceandauthenticationMethod("Password", "Previously satisfied", "Mobile app notification") not available inEntraIdSignInEvents. Use this for forensic-grade MFA step tracing when AH'sLogonType+AuthenticationRequirementcolumns are insufficient.
Step 3 (Data Lake) — interactive MFA search:
union isfuzzy=true SigninLogs, AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(90d)
| where UserPrincipalName =~ '<UPN>'
| extend AuthDetails = parse_json(tostring(AuthenticationDetails))
| mv-expand AuthDetails
| extend AuthMethod = tostring(AuthDetails.authenticationMethod)
| extend RequestSeq = toint(AuthDetails.RequestSequence)
| where AuthMethod != "Previously satisfied"
| where RequestSeq > 0
| project TimeGenerated, IPAddress, Location, AppDisplayName, AuthMethod,
RequestSeq, SessionId = tostring(SessionId), UserAgent, ResultType
| order by TimeGenerated desc
| take 30CRITICAL: After completing the SessionId trace, extract ALL unique IP addresses discovered:
Build comprehensive IP list for enrichment analysis.
MANDATORY: Search investigation JSON ip_enrichment array for EVERY IP in the authentication chain:
For each IP address discovered in Steps 1-3:
Locate IP in ip_enrichment array (search by "ip": "<IP_ADDRESS>" field)
Extract key risk indicators:
is_vpn, is_proxy, is_tor (anonymization detection)abuse_confidence_score, total_reports (reputation)threat_description, threat_detected (threat intel matches)org, asn (network ownership - hosting vs ISP)last_auth_result_detail (authentication pattern)signin_count, success_count, failure_count (frequency/behavior)first_seen, last_seen (temporal pattern - transient vs established)Document findings for EACH IP in the chain:
This creates a complete evidence picture showing the full authentication journey with enrichment context.
⚠️ MANDATORY CHECKPOINT - Before writing risk assessment:
Present findings in clear evidence trail:
Risk Assessment Framework - SessionId Interpretation:
Scenario: User sign-ins detected from two geographically distant locations within 18 hours.
Location A Analysis:
SMS verification and RequestSeq: 1authenticationStepDateTime: 2025-10-15T14:23:05Z with RequestSequence: 1Location B Analysis:
"MFA requirement satisfied by claim in the token"Query to compare sessions across both IPs:
let suspicious_ips = dynamic(["<IP_ADDRESS_1>", "<IP_ADDRESS_2>"]);
union isfuzzy=true SigninLogs, AADNonInteractiveUserSignInLogs
| where TimeGenerated between (datetime(<START_DATE>) .. datetime(<END_DATE>))
| where UserPrincipalName =~ '<UPN>'
| where IPAddress in (suspicious_ips)
| project TimeGenerated, IPAddress, Location, SessionId, UserAgent
| order by TimeGenerated ascCRITICAL FINDING:
<SESSION_ID_EXAMPLE>Initial Appearance: Potential geographic anomaly requiring investigation Further Analysis Required: Correlate SessionId with UserAgent, behavior patterns, and user confirmation
| Evidence Type | Finding | Observation |
|---|---|---|
| Interactive MFA | Location A only | User performed SMS authentication |
| Location B Auth Methods | "Previously satisfied" only | Token reuse (normal OAuth flow) |
| SessionId | Same across both locations | Session continuity maintained |
| Time Gap | 18 hours | Within typical refresh token lifetime (24-90 days) |
| User Agent | Same | Consistent device fingerprint |
| Applications | Consistent across locations | Consistent workflow pattern |
The same SessionId requires careful analysis because:
Possible Scenarios Requiring Investigation:
| Scenario | Description | Action Required |
|---|---|---|
| Legitimate VPN Connection | User switched VPN exit nodes (same device, different apparent location) | Requires user confirmation |
| Legitimate User Travel | User traveled between locations with sufficient time gap (tokens remained valid) | Requires user confirmation |
| Multi-Device User | User has laptop + phone active simultaneously (different IPs, concurrent activity) | Check UserAgent for mobile vs desktop - Requires user confirmation |
| Stolen Token Replay | Attacker obtained refresh token (SessionId stays same, may show different UserAgent) | Cannot be ruled out by SessionId alone |
| Mobile Carrier Routing | Carrier routes traffic through regional gateways (device in one location, exits another) | Check IP enrichment for ISP org |
ip_enrichment array to verify:is_vpn, is_proxy, is_tor)abuse_confidence_score, total_reports)threat_detected, threat_description)last_auth_result_detail, signin_count, success_count, failure_count)first_seen, last_seen - transient vs established pattern)Use IP enrichment data from investigation JSON to strengthen your analysis, then confirm with user:
is_vpn: true)Only after user confirmation can you conclude VPN usage or travel is legitimate. Same SessionId + IP enrichment data together provide strong evidence, but user confirmation is still required.
| Authentication Method | RequestSeq > 0 Meaning | RequestSeq = 0 Meaning |
|---|---|---|
| Passkey (device-bound) | User physically approved with biometric/PIN | Passkey used in prior session, token reused |
| Phone sign-in | User approved notification on phone | Phone approval in prior session, token reused |
| SMS verification | User entered SMS code | SMS verification in prior session, token reused |
| Microsoft Authenticator app | User approved push notification | Authenticator used in prior session, token reused |
| Previously satisfied | N/A - never has RequestSeq > 0 | Always indicates token/claim reuse |
CRITICAL: Always check IP enrichment data before making risk determination!
threat_detected: true in IP enrichmentabuse_confidence_score >= 75, is_tor: true, or malicious threat_descriptionabuse_confidence_score >= 25, is_vpn: true without user confirmation, or total_reports > 0abuse_confidence_score: 0, residential ISP org (TELUS, Comcast, etc.), is_vpn: false, high signin_count with consistent success rateip_enrichment array for VPN, abuse scores, threat intelThis skill requires:
RunAdvancedHuntingQuery for EntraIdSignInEventsmcp_sentinel-data_query_lake for SigninLogs + AADNonInteractiveUserSignInLogs unionAuthenticationDetails per-step granularity is neededip_enrichment array (from user-investigation skill)temp/investigation_<upn_prefix>_<timestamp>.jsonfile_search or list_dir to locate existing investigationsAuthentication tracing is typically performed as a follow-up analysis after running a user investigation:
ip_enrichment arrayKey Integration Points:
© SCStelz, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in .github/skills/authentication-tracing of SCStelz/security-investigator.
Open the folder on GitHubat commit b38152e
Authentication Tracing next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Authentication Tracing this skillSCStelz/security-investigator | 249 | — | ~8.6k | Automated safety check: Pass | MIT | |
| Msal Client CredentialsAzureAD/microsoft-authentication-library-for-dotnet | 1.5k | — | ~1.1k | Automated safety check: Pass | MIT | |
| Azure APIM Policy Authoringthomast1906/github-copilot-agent-skills | 202 | — | ~1.5k | Automated safety check: Pass | MIT | |
| Maui Authenticationdavidortinau/maui-skills | 174 | — | ~1.5k | Automated safety check: Pass | MIT | |
| Microsoft Azure Webjobs Extensions Authentication Events Dotnetmicrosoft/skills | 3.1k | 5 repos | ~3.8k | Automated safety check: Pass | MIT | |
| Implementing Google Workspace Sso Configurationmukul975/Anthropic-Cybersecurity-Skills | 34k | — | ~2k | Automated safety check: Pass | Apache-2.0 |
AzureAD/microsoft-authentication-library-for-dotnet
Client Credentials Flow for service-to-service (daemon) authentication in MSAL.NET without user involvement
thomast1906/github-copilot-agent-skills
Generates Azure API Management policy XML for authentication, rate limiting, CORS, error handling and transformations, consulting Azure best-practice and documentation tools first.
davidortinau/maui-skills
Add authentication to .NET MAUI apps. An agent skill from davidortinau/maui-skills.
microsoft/skills
Microsoft Entra Authentication Events SDK for .NET. An agent skill from microsoft/skills.
mukul975/Anthropic-Cybersecurity-Skills
Configures SAML 2.0 single sign-on for Google Workspace against a third-party identity provider (Okta, Azure AD/Entra ID, ADFS), with Workspace as the Service Provider, to centralize authentication…
microsoft/GitHub-Copilot-for-Azure
Guides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration.
SCStelz/security-investigator
A skill your agent uses when asked to investigate Conditional Access policy changes, sign-in failures related to CA policies (error codes 53000, 50074, 530032), or suspected policy…
SCStelz/security-investigator
Weekly review of an investigation tenant-context memory file against the most recent SOC scan reports (e.g.
SCStelz/security-investigator
A skill your agent uses when asked to create heatmaps, visualize patterns over time, show activity grids, or display aggregated data in a matrix format.
SCStelz/security-investigator
Report/investigate RUNTIME ACTIVITY of AI agents (Agent 365 / Copilot Studio / M365 Copilot / Work IQ) — agents used, tools/connectors, channels, tokens, prompt/reply content, and Prompt Shield…
SCStelz/security-investigator
Audit or report on AI agent security posture across Copilot Studio, Microsoft 365 Copilot, Microsoft Foundry, and third-party agents.
SCStelz/security-investigator
Audit Entra ID app registration and service principal security posture.
Works with
Categories
A skill your agent uses when asked to trace authentication flows, analyze SessionId chains, investigate token reuse vs interactive MFA, or assess geographic anomalies in sign-ins. Authentication Tracing is an agent skill from SCStelz/security-investigator. Use this skill when asked to trace authentication flows, analyze SessionId chains, investigate token reuse vs interactive MFA, or assess geographic anomalies in sign-ins.
Authentication Tracing fits situations like: asked to trace authentication flows; analyze SessionId chains; investigate token reuse vs interactive MFA; assess geographic anomalies in sign-ins.
Run `npx skills add SCStelz/security-investigator --skill authentication-tracing -a claude-code`. Or copy the skill folder (.github/skills/authentication-tracing in SCStelz/security-investigator) into .claude/skills/authentication-tracing in your project. Claude Code loads it when a task matches its description.
Run `npx skills add SCStelz/security-investigator --skill authentication-tracing -a codex`. Or copy the skill folder (.github/skills/authentication-tracing in SCStelz/security-investigator) into .agents/skills/authentication-tracing in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add SCStelz/security-investigator --skill authentication-tracing -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/authentication-tracing, .gemini/skills/authentication-tracing, .github/skills/authentication-tracing and .opencode/skills/authentication-tracing in your project.
SKILL.md names no scripts, command-line tools or credentials: Authentication Tracing is instructions for the agent only.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Authentication Tracing is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 8.6k tokens (SKILL.md is roughly 34k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Authentication Tracing: Msal Client Credentials (AzureAD/microsoft-authentication-library-for-dotnet, 1.5k stars), Azure APIM Policy Authoring (thomast1906/github-copilot-agent-skills, 202 stars), Maui Authentication (davidortinau/maui-skills, 174 stars) and Microsoft Azure Webjobs Extensions Authentication Events Dotnet (microsoft/skills, 3.1k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
SCStelz (a GitHub user) maintains it in SCStelz/security-investigator, which has 249 GitHub stars. The repository holds 22 skills in this directory. The repository was last updated on October 6, 2026.
Source: SCStelz/security-investigator on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.