POST /v1/auth/mfa/disable
Disable TOTP MFA. Requires the user's current password as a second factor — defeats hijacked-session abuse.
::: tip Auth Required: cookie or Bearer. Body must include current password. :::
Request
POST /v1/auth/mfa/disable
| Header | Required | Notes |
|---|---|---|
Cookie: identsphere_at=... OR Authorization: Bearer ... | yes | — |
Content-Type: application/json | yes | — |
Body
{ "password": "correct-horse-battery-staple" }
| Field | Type | Required | Notes |
|---|---|---|---|
password | string | yes | The user's current password. |
Response
204 No Content
MFA is now disabled. All recovery codes are deleted. Active sessions for this user have their step-up assertions invalidated.
Error responses
| Status | Code | When |
|---|---|---|
| 400 | invalid_input | Password missing. |
| 401 | authentication_required | Password doesn't match. |
| 404 | not_found | User no longer exists. |
If MFA was already off, the endpoint returns 204 (idempotent) without checking the password.
Example: curl
curl -X POST https://auth.example.com/v1/auth/mfa/disable \
-H 'Content-Type: application/json' \
-b cookies.txt \
-d '{"password":"correct-horse-battery-staple"}'
Example: TypeScript (@identsphere/react)
import { useMfaDisable } from '@identsphere/react';
function MfaDisableForm() {
const disable = useMfaDisable();
return (
<form onSubmit={async (e) => {
e.preventDefault();
const pw = new FormData(e.currentTarget).get('password') as string;
await disable.mutateAsync({ password: pw });
}}>
<input type="password" name="password" />
<button type="submit">Disable MFA</button>
</form>
);
}
Notes
- An audit entry (
auth.mfa.disabled) is recorded asynchronously. - This endpoint also clears the TOTP secret — re-enrolling will generate a
fresh secret via
/setup. - Trusted-browser entries are NOT removed by this endpoint. If you also
want to clear them, hit
DELETE /v1/auth/trusted-browsersfor each.