Outcome
Begin pending actor/target-bound authenticator enrollment; protected material and code delivery require separate human transport proof.
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.setup --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
-
Creates pending enrollment material and a signed time-limited actor/target token; may create default security state.
-
Does not enable MFA, persist the pending secret/code set or prove safe recipient delivery.
Interpret the response
Identifies the target userId and returns a pending secret, otpauth enrollment URI (not a rendered image), ten backup codes, an actor/target-bound signed setup token, configured expiry interval and instructions. Setup can create default user-security state but does not enable MFA or persist the new secret/codes.
Default setup-token lifetime is 900 seconds. Repeated pending setup creates independent token ids; tokens are not an idempotency key. An already-enabled account is refused. Use one deliberate enrollment journey rather than creating unused pending material.
Raw handler examples are synthetic. CLI/stdio MCP protects secret/setupToken and the URI containing the secret. SDK 0.23.2 protects each backupCodes array item as its own opaque reference when a protected store is configured; hosts without a store redact the items. Agent-visible output and the human authenticator enrollment interface need separate proof; do not extract protected secrets to build a QR code.
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.
Begin self enrollment; response is not enabled MFA
Input · payload.json:
{
"userId": "usr_self"
}
CLI:
topolo actions validate app_topolo_auth.user_mfa.setup --data @payload.json --json
topolo actions plan app_topolo_auth.user_mfa.setup --data @payload.json --confirm --json
topolo actions call app_topolo_auth.user_mfa.setup --data @payload.json --confirm --json
MCP call after validation and plan review:
{
"tool": "topolo_call_action",
"arguments": {
"actionId": "app_topolo_auth.user_mfa.setup",
"input": {
"userId": "usr_self"
},
"confirm": true
}
}
Captured local handler response · HTTP 200:
{
"success": true,
"message": "MFA setup initiated - verification required to complete",
"data": {
"userId": "usr_self",
"secret": "JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP",
"qrCodeUrl": "otpauth://totp/TopoloAuth:usr_self%40example.test?secret=JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP&issuer=TopoloAuth",
"backupCodes": [
"0000000a",
"0000000b",
"0000000c",
"0000000d",
"0000000e",
"0000000f",
"00000010",
"00000011",
"00000012",
"00000013"
],
"setupToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0b3BvbG8tYXV0aCIsImF1ZCI6InRvcG9sby1zZXJ2aWNlcyIsInN1YiI6InVzcl9zZWxmIiwiYWN0b3IiOiJ1c3Jfc2VsZiIsInNlY3JldCI6IkpCU1dZM0RQRUhQSzNQWFBKQlNXWTNEUEVIUEszUFhQIiwiYmFja3VwQ29kZXMiOlsiMDAwMDAwMGEiLCIwMDAwMDAwYiIsIjAwMDAwMDBjIiwiMDAwMDAwMGQiLCIwMDAwMDAwZSIsIjAwMDAwMDBmIiwiMDAwMDAwMTAiLCIwMDAwMDAxMSIsIjAwMDAwMDEyIiwiMDAwMDAwMTMiXSwiaWF0IjoxNzkwODk5MjAwLCJleHAiOjE3OTA5MDAxMDAsInR5cGUiOiJtZmFfc2V0dXAiLCJqdGkiOiJiOTNmNDNkYi01ZDFjLTRjODktYjA1NS1hNGQ4MDRkZmIyYjAifQ.4QO9W68TJGEPL7WahVV-Cm1-6hkTN7AdSU0m3XrP9OI",
"setupTokenExpiresIn": 900,
"instructions": "Scan the QR code with your authenticator app, then verify with a TOTP code to complete setup"
},
"timestamp": "2026-10-02T00:00:00.000Z"
}
Administrator begins managed-user setup bound to that same actor
Input · payload.json:
{
"userId": "usr_managed"
}
CLI:
topolo actions validate app_topolo_auth.user_mfa.setup --data @payload.json --json
topolo actions plan app_topolo_auth.user_mfa.setup --data @payload.json --confirm --json
topolo actions call app_topolo_auth.user_mfa.setup --data @payload.json --confirm --json
MCP call after validation and plan review:
{
"tool": "topolo_call_action",
"arguments": {
"actionId": "app_topolo_auth.user_mfa.setup",
"input": {
"userId": "usr_managed"
},
"confirm": true
}
}
Captured local handler response · HTTP 200:
{
"success": true,
"message": "MFA setup initiated - verification required to complete",
"data": {
"userId": "usr_managed",
"secret": "JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP",
"qrCodeUrl": "otpauth://totp/TopoloAuth:usr_managed%40example.test?secret=JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP&issuer=TopoloAuth",
"backupCodes": [
"0000000a",
"0000000b",
"0000000c",
"0000000d",
"0000000e",
"0000000f",
"00000010",
"00000011",
"00000012",
"00000013"
],
"setupToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0b3BvbG8tYXV0aCIsImF1ZCI6InRvcG9sby1zZXJ2aWNlcyIsInN1YiI6InVzcl9tYW5hZ2VkIiwiYWN0b3IiOiJ1c3JfYWRtaW4iLCJzZWNyZXQiOiJKQlNXWTNEUEVIUEszUFhQSkJTV1kzRFBFSFBLM1BYUCIsImJhY2t1cENvZGVzIjpbIjAwMDAwMDBhIiwiMDAwMDAwMGIiLCIwMDAwMDAwYyIsIjAwMDAwMDBkIiwiMDAwMDAwMGUiLCIwMDAwMDAwZiIsIjAwMDAwMDEwIiwiMDAwMDAwMTEiLCIwMDAwMDAxMiIsIjAwMDAwMDEzIl0sImlhdCI6MTc5MDg5OTIwMCwiZXhwIjoxNzkwODk5MzIwLCJ0eXBlIjoibWZhX3NldHVwIiwianRpIjoiMmJiZmI5MzUtZWNlOS00NzM0LWFmZDUtZmRlYTljM2RmZmY1In0.WsxzlnY5hfgU4jdoUam5y9_gndYyK4LUKq8SfPybsW8",
"setupTokenExpiresIn": 120,
"instructions": "Scan the QR code with your authenticator app, then verify with a TOTP code to complete setup"
},
"timestamp": "2026-10-02T00:00:00.000Z"
}
Repeated pending setup creates another independently consumable token
Input · payload.json:
{
"userId": "usr_self"
}
CLI:
topolo actions validate app_topolo_auth.user_mfa.setup --data @payload.json --json
topolo actions plan app_topolo_auth.user_mfa.setup --data @payload.json --confirm --json
topolo actions call app_topolo_auth.user_mfa.setup --data @payload.json --confirm --json
MCP call after validation and plan review:
{
"tool": "topolo_call_action",
"arguments": {
"actionId": "app_topolo_auth.user_mfa.setup",
"input": {
"userId": "usr_self"
},
"confirm": true
}
}
Captured local handler response · HTTP 200:
{
"success": true,
"message": "MFA setup initiated - verification required to complete",
"data": {
"userId": "usr_self",
"secret": "JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP",
"qrCodeUrl": "otpauth://totp/TopoloAuth:usr_self%40example.test?secret=JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP&issuer=TopoloAuth",
"backupCodes": [
"0000000a",
"0000000b",
"0000000c",
"0000000d",
"0000000e",
"0000000f",
"00000010",
"00000011",
"00000012",
"00000013"
],
"setupToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0b3BvbG8tYXV0aCIsImF1ZCI6InRvcG9sby1zZXJ2aWNlcyIsInN1YiI6InVzcl9zZWxmIiwiYWN0b3IiOiJ1c3Jfc2VsZiIsInNlY3JldCI6IkpCU1dZM0RQRUhQSzNQWFBKQlNXWTNEUEVIUEszUFhQIiwiYmFja3VwQ29kZXMiOlsiMDAwMDAwMGEiLCIwMDAwMDAwYiIsIjAwMDAwMDBjIiwiMDAwMDAwMGQiLCIwMDAwMDAwZSIsIjAwMDAwMDBmIiwiMDAwMDAwMTAiLCIwMDAwMDAxMSIsIjAwMDAwMDEyIiwiMDAwMDAwMTMiXSwiaWF0IjoxNzkwODk5MjAwLCJleHAiOjE3OTA5MDAxMDAsInR5cGUiOiJtZmFfc2V0dXAiLCJqdGkiOiI3MmExMTJjNi03ZDExLTQ2NmEtYjc5Yy1mN2YxNDBmYTFiMzMifQ.EMKQj-qVo5fSkBm6QNu4VbMlx_e-A8Uv6SS-42t-y_I",
"setupTokenExpiresIn": 900,
"instructions": "Scan the QR code with your authenticator app, then verify with a TOTP code to complete setup"
},
"timestamp": "2026-10-02T00:00:00.000Z"
}
Begin setup whose later protected-state write will fail
Input · payload.json:
{
"userId": "usr_partial"
}
CLI:
topolo actions validate app_topolo_auth.user_mfa.setup --data @payload.json --json
topolo actions plan app_topolo_auth.user_mfa.setup --data @payload.json --confirm --json
topolo actions call app_topolo_auth.user_mfa.setup --data @payload.json --confirm --json
MCP call after validation and plan review:
{
"tool": "topolo_call_action",
"arguments": {
"actionId": "app_topolo_auth.user_mfa.setup",
"input": {
"userId": "usr_partial"
},
"confirm": true
}
}
Captured local handler response · HTTP 200:
{
"success": true,
"message": "MFA setup initiated - verification required to complete",
"data": {
"userId": "usr_partial",
"secret": "JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP",
"qrCodeUrl": "otpauth://totp/TopoloAuth:usr_partial%40example.test?secret=JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP&issuer=TopoloAuth",
"backupCodes": [
"00000136",
"00000137",
"00000138",
"00000139",
"0000013a",
"0000013b",
"0000013c",
"0000013d",
"0000013e",
"0000013f"
],
"setupToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0b3BvbG8tYXV0aCIsImF1ZCI6InRvcG9sby1zZXJ2aWNlcyIsInN1YiI6InVzcl9wYXJ0aWFsIiwiYWN0b3IiOiJ1c3JfYWRtaW4iLCJzZWNyZXQiOiJKQlNXWTNEUEVIUEszUFhQSkJTV1kzRFBFSFBLM1BYUCIsImJhY2t1cENvZGVzIjpbIjAwMDAwMTM2IiwiMDAwMDAxMzciLCIwMDAwMDEzOCIsIjAwMDAwMTM5IiwiMDAwMDAxM2EiLCIwMDAwMDEzYiIsIjAwMDAwMTNjIiwiMDAwMDAxM2QiLCIwMDAwMDEzZSIsIjAwMDAwMTNmIl0sImlhdCI6MTc5MDg5OTIwMCwiZXhwIjoxNzkwOTAwMTAwLCJ0eXBlIjoibWZhX3NldHVwIiwianRpIjoiOGVjOTVjMGQtNzE0Ni00NTE2LTk5MDctMDY0NWY2M2NjM2ZlIn0.2s4H7XhRxz5pRVlpkNlSK_6K27SQkOqXB-u_8Dp0qDc",
"setupTokenExpiresIn": 900,
"instructions": "Scan the QR code with your authenticator app, then verify with a TOTP code to complete setup"
},
"timestamp": "2026-10-02T00:00:00.000Z"
}
Begin setup with unavailable token-claim store
Input · payload.json:
{
"userId": "usr_claim_error"
}
CLI:
topolo actions validate app_topolo_auth.user_mfa.setup --data @payload.json --json
topolo actions plan app_topolo_auth.user_mfa.setup --data @payload.json --confirm --json
topolo actions call app_topolo_auth.user_mfa.setup --data @payload.json --confirm --json
MCP call after validation and plan review:
{
"tool": "topolo_call_action",
"arguments": {
"actionId": "app_topolo_auth.user_mfa.setup",
"input": {
"userId": "usr_claim_error"
},
"confirm": true
}
}
Captured local handler response · HTTP 200:
{
"success": true,
"message": "MFA setup initiated - verification required to complete",
"data": {
"userId": "usr_claim_error",
"secret": "JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP",
"qrCodeUrl": "otpauth://totp/TopoloAuth:usr_claim_error%40example.test?secret=JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP&issuer=TopoloAuth",
"backupCodes": [
"00000136",
"00000137",
"00000138",
"00000139",
"0000013a",
"0000013b",
"0000013c",
"0000013d",
"0000013e",
"0000013f"
],
"setupToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0b3BvbG8tYXV0aCIsImF1ZCI6InRvcG9sby1zZXJ2aWNlcyIsInN1YiI6InVzcl9jbGFpbV9lcnJvciIsImFjdG9yIjoidXNyX2FkbWluIiwic2VjcmV0IjoiSkJTV1kzRFBFSFBLM1BYUEpCU1dZM0RQRUhQSzNQWFAiLCJiYWNrdXBDb2RlcyI6WyIwMDAwMDEzNiIsIjAwMDAwMTM3IiwiMDAwMDAxMzgiLCIwMDAwMDEzOSIsIjAwMDAwMTNhIiwiMDAwMDAxM2IiLCIwMDAwMDEzYyIsIjAwMDAwMTNkIiwiMDAwMDAxM2UiLCIwMDAwMDEzZiJdLCJpYXQiOjE3OTA4OTkyMDAsImV4cCI6MTc5MDkwMDEwMCwidHlwZSI6Im1mYV9zZXR1cCIsImp0aSI6ImU0ZDdmYzhiLTg1YzMtNDI5MS1hYWRkLWE3OTAwNGM5OWJkZiJ9.VBy3fE_RTkwG5hDCTd8m4F2ZWNNs88smumYJMusyXdQ",
"setupTokenExpiresIn": 900,
"instructions": "Scan the QR code with your authenticator app, then verify with a TOTP code to complete setup"
},
"timestamp": "2026-10-02T00:00:00.000Z"
}
Begin enrollment used for an isolated competing-write race
Input · payload.json:
{
"userId": "usr_claim_error"
}
CLI:
topolo actions validate app_topolo_auth.user_mfa.setup --data @payload.json --json
topolo actions plan app_topolo_auth.user_mfa.setup --data @payload.json --confirm --json
topolo actions call app_topolo_auth.user_mfa.setup --data @payload.json --confirm --json
MCP call after validation and plan review:
{
"tool": "topolo_call_action",
"arguments": {
"actionId": "app_topolo_auth.user_mfa.setup",
"input": {
"userId": "usr_claim_error"
},
"confirm": true
}
}
Captured local handler response · HTTP 200:
{
"success": true,
"message": "MFA setup initiated - verification required to complete",
"data": {
"userId": "usr_claim_error",
"secret": "JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP",
"qrCodeUrl": "otpauth://totp/TopoloAuth:usr_claim_error%40example.test?secret=JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP&issuer=TopoloAuth",
"backupCodes": [
"00000136",
"00000137",
"00000138",
"00000139",
"0000013a",
"0000013b",
"0000013c",
"0000013d",
"0000013e",
"0000013f"
],
"setupToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ0b3BvbG8tYXV0aCIsImF1ZCI6InRvcG9sby1zZXJ2aWNlcyIsInN1YiI6InVzcl9jbGFpbV9lcnJvciIsImFjdG9yIjoidXNyX2FkbWluIiwic2VjcmV0IjoiSkJTV1kzRFBFSFBLM1BYUEpCU1dZM0RQRUhQSzNQWFAiLCJiYWNrdXBDb2RlcyI6WyIwMDAwMDEzNiIsIjAwMDAwMTM3IiwiMDAwMDAxMzgiLCIwMDAwMDEzOSIsIjAwMDAwMTNhIiwiMDAwMDAxM2IiLCIwMDAwMDEzYyIsIjAwMDAwMTNkIiwiMDAwMDAxM2UiLCIwMDAwMDEzZiJdLCJpYXQiOjE3OTA4OTkyMDAsImV4cCI6MTc5MDkwMDEwMCwidHlwZSI6Im1mYV9zZXR1cCIsImp0aSI6ImI5YmUxYjZkLWNmZTktNDc2My1iZmFiLWJlOTVmMWUwMzkzNSJ9.3v1M-oxM9WLC1VRejIRqI07wf6oAiEBIf5K5UBcgKxE",
"setupTokenExpiresIn": 900,
"instructions": "Scan the QR code with your authenticator app, then verify with a TOTP code to complete setup"
},
"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 setup 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 setup 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"
}
}
}
Setup refuses an already-enabled account
Captured local refusal · HTTP 400:
{
"input": {
"userId": "usr_self"
},
"response": {
"success": false,
"error": {
"message": "MFA is already enabled for this user",
"code": "VALIDATION_ERROR",
"details": {
"mfa": true
},
"timestamp": "2026-10-02T00:00:00.000Z"
}
}
}
Cookie identity precedes conflicting Bearer identity and cannot manage its target
Captured local refusal · HTTP 401:
{
"input": {
"userId": "usr_self"
},
"response": {
"success": false,
"error": {
"message": "Access denied: You can only setup MFA for yourself",
"code": "AUTH_ERROR",
"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
Discard unused enrollment material in the intended private interface. Release protected local references after the journey; local release is not server-token revocation. No setup-token cancellation action is published here. Read status before a separately approved fresh enrollment; do not expose or reuse pending credentials.
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.