Outcome
Clear exact authorized target MFA and backup-code hashes; physical authenticators, passkeys and sessions are separate.
These examples prove local handler behavior with real isolated JWT/TOTP/envelope/SQL and explicitly synthetic identity/key dependencies.
Before you start
-
Verify pinned identity and exact target authority. Cookie identity precedes Bearer identity; self sub/userId or exact management authority is required. An authenticated denial is not proof of expired OAuth.
-
Use a deliberate private human enrollment/recovery journey. CLI/stdio MCP protects setup secret/token and embedded QR URI, and SDK 0.23.2 protects each backup-code array item separately through a configured protected store. Do not extract protected material, log live codes, use fixture values on real accounts or claim agent credential delivery is proven.
-
Review exact plans/effects before approval. Stored MFA flags, authenticator enrollment, backup-code delivery/use, session state and notifications are separate outcomes. Tokens are actor/target bound and can be consumed before storage fails; already-enabled state and conditional SQL prevent overwriting active MFA.
Discover the target
topolo whoami --json
topolo apps --json
topolo actions get app_topolo_auth.user_mfa.disable --detail --json
Resolve the actual authenticated self or authorized managed user and read user_security.get for that exact target. Never retrieve a secret/hash for discovery. Supply current TOTP through the intended private authenticator interface, and pass protected setup-token references unchanged.
Input fields
| Field | Required | Meaning |
|---|---|---|
userId | Yes | Existing Auth user from credential-scoped discovery. Self identity may use sub or userId; another target requires exact management authority over its organization. Cookie identity precedes Bearer identity for these handlers. A valid administrator credential cannot manage a foreign target. |
Effects
-
Removes enabled MFA secret and all stored backup-code hashes for the exact authorized target.
-
Does not clear passkeys, current sessions or physical authenticator-device entries; notification/audit are nonfatal.
Interpret the response
Clears enabled state, protected secret and all stored backup-code hashes for the authorized target. Repeated disable succeeds. The handler requires current access/target authority but does not request a fresh TOTP or password challenge.
Successful data identifies the affected userId and mfaEnabled false; require userId to match the unchanged reviewed plan. Verify user_security.get reports disabled MFA and no backup-code presence. This operation does not erase authenticator-device entries, passkeys, current sessions or independently pending setup-token copies.
Prior secret/codes are not a reversible snapshot. New MFA requires a new deliberately approved enrollment and private authenticator interaction. Notification/audit are nonfatal and delivery needs independent verification.
Worked examples
These are complete raw target-handler responses from isolated fixtures, not live or CLI/gateway envelopes. All identifiers, seeds, backup codes and signing keys are synthetic; captured tokens have only a local fixture signature and a fixed already-past lifetime. Never use them for live enrollment. CLI/MCP payloads show the declared request shape and require discovery, private recipient interaction and a reviewed approved plan. SDK 0.23.2 protects each backup-code array item separately through a configured protected store; hosts without one redact sensitive items. Local published-SDK tests do not prove live CLI/MCP or private recipient delivery. A protected setup-token reference must be passed unchanged; no secret extraction or QR reconstruction is authorized by this guide.
Disable removes protected secret and stored backup-code hashes
Input · payload.json:
{
"userId": "usr_self"
}
CLI:
topolo actions validate app_topolo_auth.user_mfa.disable --data @payload.json --json
topolo actions plan app_topolo_auth.user_mfa.disable --data @payload.json --confirm --json
topolo actions call app_topolo_auth.user_mfa.disable --data @payload.json --confirm --json
MCP call after validation and plan review:
{
"tool": "topolo_call_action",
"arguments": {
"actionId": "app_topolo_auth.user_mfa.disable",
"input": {
"userId": "usr_self"
},
"confirm": true
}
}
Captured local handler response · HTTP 200:
{
"success": true,
"message": "MFA disabled successfully",
"data": {
"userId": "usr_self",
"mfaEnabled": false
},
"timestamp": "2026-10-02T00:00:00.000Z"
}
Repeated disable acknowledges unchanged disabled state
Input · payload.json:
{
"userId": "usr_self"
}
CLI:
topolo actions validate app_topolo_auth.user_mfa.disable --data @payload.json --json
topolo actions plan app_topolo_auth.user_mfa.disable --data @payload.json --confirm --json
topolo actions call app_topolo_auth.user_mfa.disable --data @payload.json --confirm --json
MCP call after validation and plan review:
{
"tool": "topolo_call_action",
"arguments": {
"actionId": "app_topolo_auth.user_mfa.disable",
"input": {
"userId": "usr_self"
},
"confirm": true
}
}
Captured local handler response · HTTP 200:
{
"success": true,
"message": "MFA disabled successfully",
"data": {
"userId": "usr_self",
"mfaEnabled": false
},
"timestamp": "2026-10-02T00:00:00.000Z"
}
Managing administrator disables exactly the selected target
Input · payload.json:
{
"userId": "usr_managed"
}
CLI:
topolo actions validate app_topolo_auth.user_mfa.disable --data @payload.json --json
topolo actions plan app_topolo_auth.user_mfa.disable --data @payload.json --confirm --json
topolo actions call app_topolo_auth.user_mfa.disable --data @payload.json --confirm --json
MCP call after validation and plan review:
{
"tool": "topolo_call_action",
"arguments": {
"actionId": "app_topolo_auth.user_mfa.disable",
"input": {
"userId": "usr_managed"
},
"confirm": true
}
}
Captured local handler response · HTTP 200:
{
"success": true,
"message": "MFA disabled successfully",
"data": {
"userId": "usr_managed",
"mfaEnabled": false
},
"timestamp": "2026-10-02T00:00:00.000Z"
}
Verify the result
-
Keep the exact userId from the reviewed input/plan and read user_security.get for that same authorized user. Compare mfa_enabled and has_backup_codes; never retrieve or log secret/hash material.
-
Stored status is separate from private authenticator enrollment, normal MFA/backup-code sign-in, physical device entries, session invalidation and notification delivery. Verify the intended human journey independently; synthetic local cryptographic fixtures prove none of these live outcomes.
Errors and recovery
Anonymous MFA operation is refused
Captured local refusal · HTTP 401:
{
"input": {
"userId": "usr_self"
},
"response": {
"success": false,
"error": {
"message": "Access token required",
"code": "AUTH_ERROR",
"details": null,
"timestamp": "2026-10-02T00:00:00.000Z"
}
}
}
Ordinary user cannot manage another MFA target
Captured local refusal · HTTP 401:
{
"input": {
"userId": "usr_managed"
},
"response": {
"success": false,
"error": {
"message": "Access denied: You can only disable MFA for yourself",
"code": "AUTH_ERROR",
"details": null,
"timestamp": "2026-10-02T00:00:00.000Z"
}
}
}
Organization administrator cannot manage a foreign target
Captured local refusal · HTTP 401:
{
"input": {
"userId": "usr_foreign"
},
"response": {
"success": false,
"error": {
"message": "Access denied: You can only disable MFA for yourself",
"code": "AUTH_ERROR",
"details": null,
"timestamp": "2026-10-02T00:00:00.000Z"
}
}
}
Missing target user is not provisioned
Captured local refusal · HTTP 404:
{
"input": {
"userId": "usr_missing"
},
"response": {
"success": false,
"error": {
"message": "User not found",
"code": "NOT_FOUND",
"details": null,
"timestamp": "2026-10-02T00:00:00.000Z"
}
}
}
Published recovery guidance
-
state_conflict(HTTP 409): Read the current resource state, reconcile the intended change, and retry only if still valid. -
AUTH_ERROR(HTTP 401): Verify pinned identity and cookie/Bearer consistency. Respect target organization authority; an authenticated refusal is not proof of expired OAuth. -
NOT_FOUND(HTTP 404): Resolve an existing authorized user through published discovery; these actions do not provision users. -
VALIDATION_ERROR(HTTP 400): Read current target security state. Do not reuse a consumed/expired token, transfer it to another actor, overwrite enabled MFA or infer a backup-code set from status. When disabled after a consumed setup attempt, separately approve a fresh setup; code regeneration requires enabled MFA. -
SERVER_ERROR(HTTP 500): Preserve the sanitized result/request id and read exact-target state before retry. A verify failure may follow token claim; a successful acknowledgment identifies its user but cannot prove live sign-in or delivery. Do not extract secrets or guess a repair endpoint.
Recovery boundaries
The removed secret/code set is not restored. Re-enabling requires separately approved fresh setup and private authenticator enrollment. Do not use an old pending token or captured secret as compensation; separately review intended active sessions/passkeys.
Discovery and evidence
This record belongs to app_topolo_auth. Local captures use real controller authority, JWT signature/expiry, TOTP, envelope encryption, hash-only backup-code storage and canonical core/session SQL with simulated access-token identity, fixed synthetic credential generation and isolated keys. Lifecycle notification is unconfigured. No live user, authenticator, MFA configuration, code delivery or OAuth state was changed. Stored local state and cryptographic behavior do not prove protected gateway handoff, sign-in, recipient delivery or production completion.