/**
* mcp/skills/probeIdpSsprAvailable.ts — probe_idp_sspr_available
*
* Best-effort HTTPS probe to determine whether the tenant's self-service
* password-reset (SSPR) endpoint is enabled. The skill uses this to
* choose between the primary SSPR path and the fallback idemeum-cloud
* orchestration path.
*
* Detection strategy (per IDP)
* ----------------------------
* Okta: GET https://<tenant>.okta.com/api/v1/authn/recovery/password
* with a trivial JSON probe — if SSPR is enabled the endpoint
* responds with 400/403 (factor challenge) rather than 404/405.
* Entra: GET https://login.microsoftonline.com/common/userrealm/<user>?api-version=2.1
* — returns tenant metadata; reachability alone is a reasonable
* signal since SSPR availability isn't cleanly exposed publicly.
* Google: no reliable public probe — returns "unknown".
*
* Returns { available: "yes" | "no" | "unknown", evidence }.
* Never throws. Network errors resolve to { available: "unknown" }.
*/
import { z } from "zod" ;
import { httpGet, httpPost } from "./_shared/platform" ;
import { isSupportedIdp, type Idp } from "./_shared/idp" ;
// -- Meta ---------------------------------------------------------------------
export const meta = {
name: "probe_idp_sspr_available" ,
description:
"Probes the IDP's public endpoints to infer whether self-service password " +
"reset (SSPR) is enabled for this tenant. Best-effort: Okta and Entra " +
"return a meaningful signal; Google returns 'unknown' because no reliable " +
"public probe exists. The parent skill uses the result to choose between " +
"the SSPR portal path and the idemeum-cloud fallback." ,
riskLevel: "low" ,
destructive: false ,
requiresConsent: false ,
supportsDryRun: false ,
affectedScope: [ "user" ],
auditRequired: false ,
schema: {
idp: z
. enum ([ "okta" , "entra" , "google" , "unknown" ])
. describe ( "IDP identifier from detect_identity_provider." ),
tenant: z
. string ()
. optional ()
. describe (
"IDP tenant slug — required for Okta (e.g. 'acme' for acme.okta.com); " +
"optional for Entra (falls back to 'common'); ignored for Google." ,
),
probeUsername: z
. string ()
. optional ()
. describe (
"Synthetic username for the probe request (Okta + Entra). Does NOT " +
"authenticate — the endpoint only reveals whether the SSPR surface " +
"is live. Defaults to 'probe@example.invalid'." ,
),
},
} as const ;
// -- Types --------------------------------------------------------------------
export interface SsprProbeResult {
idp : Idp ;
tenant : string | null ;
available : "yes" | "no" | "unknown" ;
/** Short explanation (status code, endpoint hit, reason). */
evidence : string ;
}
// -- Implementation -----------------------------------------------------------
async function probeOkta ( tenant : string , username : string ) : Promise < SsprProbeResult > {
if ( ! / ^ [a-z0-9-] +$ / i . test (tenant)) {
return {
idp: "okta" , tenant, available: "unknown" ,
evidence: `Tenant slug "${ tenant }" contains invalid characters — skipping probe.` ,
};
}
const url = `https://${ tenant . toLowerCase () }.okta.com/api/v1/authn/recovery/password` ;
const body = JSON . stringify ({
username,
factorType: "EMAIL" ,
});
const r = await httpPost (url, body, {
"content-type" : "application/json" ,
"accept" : "application/json" ,
}, { timeoutMs: 5_000 });
if (r.failureReason) {
return {
idp: "okta" , tenant, available: "unknown" ,
evidence: `Okta probe ${ r . failureReason === "timeout" ? "timed out" : "network error"} for ${ url }` ,
};
}
// 200/400/403/429 — endpoint is live (SSPR likely enabled).
// 401 — endpoint live but auth required; treat as "yes".
// 404/405/501 — SSPR surface not present for this tenant.
// 0 (network error) already handled above.
if ([ 200 , 400 , 401 , 403 , 429 ]. includes (r.statusCode)) {
return {
idp: "okta" , tenant, available: "yes" ,
evidence: `Okta SSPR endpoint responded ${ r . statusCode } (live)` ,
};
}
if ([ 404 , 405 , 501 ]. includes (r.statusCode)) {
return {
idp: "okta" , tenant, available: "no" ,
evidence: `Okta SSPR endpoint returned ${ r . statusCode } — recovery API not enabled` ,
};
}
return {
idp: "okta" , tenant, available: "unknown" ,
evidence: `Okta SSPR endpoint returned unexpected status ${ r . statusCode }` ,
};
}
async function probeEntra ( tenant : string | null , username : string ) : Promise < SsprProbeResult > {
const tenantSeg = tenant && tenant. length > 0 ? tenant : "common" ;
const url =
`https://login.microsoftonline.com/${ encodeURIComponent ( tenantSeg ) }` +
`/userrealm/${ encodeURIComponent ( username ) }?api-version=2.1` ;
const r = await httpGet (url, { accept: "application/json" }, { timeoutMs: 5_000 });
if (r.failureReason) {
return {
idp: "entra" , tenant, available: "unknown" ,
evidence: `Entra userrealm probe ${ r . failureReason === "timeout" ? "timed out" : "network error"}` ,
};
}
// 200 → userrealm returned metadata, tenant reachable → SSPR typically available.
// 400/404 → tenant unknown or invalid → SSPR not available for this tenant.
if (r.statusCode === 200 ) {
return {
idp: "entra" , tenant, available: "yes" ,
evidence: `Entra userrealm endpoint responded 200 for tenant "${ tenantSeg }"` ,
};
}
if (r.statusCode === 400 || r.statusCode === 404 ) {
return {
idp: "entra" , tenant, available: "no" ,
evidence: `Entra userrealm endpoint returned ${ r . statusCode } — tenant not reachable` ,
};
}
return {
idp: "entra" , tenant, available: "unknown" ,
evidence: `Entra userrealm endpoint returned unexpected status ${ r . statusCode }` ,
};
}
function probeGoogle () : SsprProbeResult {
return {
idp: "google" , tenant: null , available: "unknown" ,
evidence: "Google Workspace has no reliable public SSPR probe; present both paths to the user." ,
};
}
// Exported for unit tests.
export const __testing = { probeOkta, probeEntra, probeGoogle };
// -- Exported run function ----------------------------------------------------
export async function run ({
idp ,
tenant ,
probeUsername = "probe@example.invalid" ,
} : {
idp : Idp ;
tenant ?: string ;
probeUsername ?: string ;
}) : Promise < SsprProbeResult > {
if (idp === "unknown" ) {
return {
idp: "unknown" , tenant: null , available: "unknown" ,
evidence: "IDP is unknown — cannot probe SSPR availability." ,
};
}
if ( ! isSupportedIdp (idp)) {
return {
idp, tenant: tenant ?? null , available: "unknown" ,
evidence: `IDP "${ idp }" is not supported in Wave 1 alpha — present both paths to the user.` ,
};
}
switch (idp) {
case "okta" :
if ( ! tenant) {
return {
idp: "okta" , tenant: null , available: "unknown" ,
evidence: "Okta probe requires a tenant slug; none supplied." ,
};
}
return probeOkta (tenant, probeUsername);
case "entra" :
return probeEntra (tenant ?? null , probeUsername);
case "google" :
return probeGoogle ();
}
}
// -- CLI smoke test -----------------------------------------------------------
if ( false ) {
run ({ idp: "okta" , tenant: "acme" })
. then (( r ) => console. log ( JSON . stringify (r, null , 2 )))
. catch (( err : Error ) => { console. error (err.message); process. exit ( 1 ); });
}