detect_identity_provider
detect_identity_provider
Detects which cloud identity provider (Okta, Microsoft Entra, or Google Workspace) the endpoint is joined to. Inspects installed agents and domain configuration, not user credentials. Returns the primary IDP plus any secondary detections and the evidence. Use at the start of cloud password-reset or SSO-repair workflows.
Metadata
Name
detect_identity_provider
Updated
2 months ago
Source
Risk
Low
Requires consent
false
Affected scope
user
Code
/**
* mcp/skills/detectIdentityProvider.ts — detect_identity_provider
*
* Inspects installed agents and domain configuration to infer which
* cloud identity provider the endpoint uses. Returns a single canonical
* idp ("okta" | "entra" | "google" | "unknown") plus any secondary
* candidates and the evidence that led to the decision.
*
* Platform strategy
* -----------------
* darwin Check for /Applications/Okta Verify.app,
* /Library/Application Support/JamfConnect/,
* /Library/Intune/, and Google Credential Provider artefacts.
* win32 Parse `dsregcmd /status` (for AzureAdJoined, WorkplaceJoined),
* registry HKLM\Software\Okta\Okta Verify, and
* HKLM\Software\Google\Credential Provider.
*
* Zero detected → return { primary: "unknown" } — never throw.
* Multiple detected → first entry wins on `primary`; rest go in `secondary`.
*
* Smoke test
* npx tsx -r dotenv/config mcp/skills/detectIdentityProvider.ts
*/
import * as fs from "fs";
import { z } from "zod";
import { isDarwin, isWin32, execAsync, runPS } from "./_shared/platform";
import type { Idp } from "./_shared/idp";
// -- Meta ---------------------------------------------------------------------
export const meta = {
name: "detect_identity_provider",
description:
"Detects which cloud identity provider (Okta, Microsoft Entra, or Google " +
"Workspace) the endpoint is joined to. Inspects installed agents and " +
"domain configuration, not user credentials. Returns the primary IDP plus " +
"any secondary detections and the evidence. Use at the start of cloud " +
"password-reset or SSO-repair workflows.",
riskLevel: "low",
destructive: false,
requiresConsent: false,
supportsDryRun: false,
affectedScope: ["user"],
auditRequired: false,
schema: {}, // no params
} as const;
// -- Types --------------------------------------------------------------------
interface Detection {
idp: Exclude<Idp, "unknown">;
/** Short reason string — e.g. "Okta Verify installed". */
evidence: string;
}
export interface IdpDetectionResult {
platform: "darwin" | "win32" | "other";
primary: Idp;
secondary: Array<Exclude<Idp, "unknown">>;
evidence: string[];
}
// -- darwin implementation ----------------------------------------------------
async function detectDarwin(): Promise<Detection[]> {
const detections: Detection[] = [];
// Okta Verify — first-party Okta endpoint companion.
if (safePathExists("/Applications/Okta Verify.app")) {
detections.push({ idp: "okta", evidence: "Okta Verify.app installed" });
}
// Jamf Connect — MDM-driven Okta/Entra integration, commonly signals Okta.
if (safePathExists("/Library/Application Support/JamfConnect")) {
detections.push({ idp: "okta", evidence: "Jamf Connect configuration present" });
}
// Intune — Microsoft-managed endpoints signal Entra.
if (safePathExists("/Library/Intune")) {
detections.push({ idp: "entra", evidence: "Microsoft Intune agent installed" });
}
// Company Portal for Entra.
if (safePathExists("/Applications/Company Portal.app")) {
detections.push({ idp: "entra", evidence: "Microsoft Company Portal installed" });
}
// Google Credential Provider / Workspace agent artefacts.
// Google does not currently ship a Mac password-reset agent, but the
// Workspace Endpoint Verification helper is a reasonable signal.
if (safePathExists("/Applications/Endpoint Verification.app")) {
detections.push({ idp: "google", evidence: "Google Endpoint Verification installed" });
}
// macOS Google Drive indicates Workspace but is a weak signal only —
// do NOT return "google" solely on the basis of Google Drive.
return dedupeByIdp(detections);
}
// -- win32 implementation -----------------------------------------------------
async function detectWin32(): Promise<Detection[]> {
const detections: Detection[] = [];
// dsregcmd /status reports AzureAdJoined / WorkplaceJoined / DomainJoined.
try {
const { stdout } = await execAsync("dsregcmd /status", {
maxBuffer: 2 * 1024 * 1024, timeout: 10_000,
});
if (/AzureAdJoined\s*:\s*YES/i.test(stdout)) {
detections.push({ idp: "entra", evidence: "dsregcmd reports AzureAdJoined: YES" });
} else if (/WorkplaceJoined\s*:\s*YES/i.test(stdout)) {
detections.push({ idp: "entra", evidence: "dsregcmd reports WorkplaceJoined: YES" });
}
} catch {
// dsregcmd may not be on PATH in locked-down environments; skip silently.
}
// Okta Verify registry key.
if (await winRegistryKeyExists("HKLM\\Software\\Okta\\Okta Verify")) {
detections.push({ idp: "okta", evidence: "Okta Verify installed (registry)" });
}
if (await winRegistryKeyExists("HKCU\\Software\\Okta\\Okta Verify")) {
detections.push({ idp: "okta", evidence: "Okta Verify installed (registry, user hive)" });
}
// Google Credential Provider for Windows.
if (await winRegistryKeyExists("HKLM\\Software\\Google\\Credential Provider")) {
detections.push({ idp: "google", evidence: "Google Credential Provider installed" });
}
return dedupeByIdp(detections);
}
// -- Helpers ------------------------------------------------------------------
function safePathExists(p: string): boolean {
try {
return fs.existsSync(p);
} catch {
return false;
}
}
/**
* Preserve ordering but drop duplicate IDPs so each canonical value
* appears at most once. Evidence for the kept entry is the FIRST one
* encountered — callers can still inspect result.evidence[] for the
* full trail.
*/
function dedupeByIdp(detections: Detection[]): Detection[] {
const seen = new Set<string>();
const out: Detection[] = [];
for (const d of detections) {
if (seen.has(d.idp)) continue;
seen.add(d.idp);
out.push(d);
}
return out;
}
/**
* Query Windows registry by firing `reg query` and checking for a
* non-zero-length output. Returns false on any non-zero exit or error.
*/
async function winRegistryKeyExists(key: string): Promise<boolean> {
if (!isWin32()) return false;
const safe = key.replace(/[\