Multi-factor authentication against Supabase Auth.
Supports TOTP (authenticator apps), phone (SMS / WhatsApp) and WebAuthn
factors. Every function acts on behalf of the signed-in user identified
by access-token — the JWT from an active session.
The usual TOTP flow:
(require '[supabase.auth.mfa :as mfa])
;; 1. enroll — body carries the QR code / secret to show the user
(mfa/enroll client token {:factor-type "totp" :friendly-name "authenticator"})
;; 2. user scans the QR and types the 6-digit code
(mfa/challenge-and-verify client token factor-id "123456")
Phone and WebAuthn factors need the explicit challenge + verify
round-trip since the response arrives out of band.
Recovery codes (experimental server-side) give users a fallback when their usual factor is unavailable:
(mfa/generate-recovery-codes client token) ;; show codes once
(mfa/get-recovery-codes-status client token) ;; remaining count
(mfa/verify-recovery-code client token {:code "K4M9-X7QP-2AB8-HT3Z"})
Each function returns {:status :body :headers} on success or an anomaly
map on failure. See https://supabase.com/docs/guides/auth/auth-mfa
Multi-factor authentication against Supabase Auth.
Supports TOTP (authenticator apps), phone (SMS / WhatsApp) and WebAuthn
factors. Every function acts on behalf of the signed-in user identified
by `access-token` — the JWT from an active session.
The usual TOTP flow:
(require '[supabase.auth.mfa :as mfa])
;; 1. enroll — body carries the QR code / secret to show the user
(mfa/enroll client token {:factor-type "totp" :friendly-name "authenticator"})
;; 2. user scans the QR and types the 6-digit code
(mfa/challenge-and-verify client token factor-id "123456")
Phone and WebAuthn factors need the explicit `challenge` + `verify`
round-trip since the response arrives out of band.
Recovery codes (experimental server-side) give users a fallback when
their usual factor is unavailable:
(mfa/generate-recovery-codes client token) ;; show codes once
(mfa/get-recovery-codes-status client token) ;; remaining count
(mfa/verify-recovery-code client token {:code "K4M9-X7QP-2AB8-HT3Z"})
Each function returns `{:status :body :headers}` on success or an anomaly
map on failure. See https://supabase.com/docs/guides/auth/auth-mfa(challenge client access-token factor-id)(challenge client access-token factor-id params)Creates a challenge for the factor factor-id. The returned body carries
the challenge :id to pass to verify.
params is factor-type specific and optional:
:channel ("sms" or "whatsapp"):webauthn map (rp_id, optional rp_origins)(challenge client "<access-token>" "<factor-id>")
(challenge client "<access-token>" "<factor-id>" {:channel "sms"})
Creates a challenge for the factor `factor-id`. The returned body carries
the challenge `:id` to pass to `verify`.
`params` is factor-type specific and optional:
* TOTP — none
* phone — `:channel` (`"sms"` or `"whatsapp"`)
* WebAuthn — `:webauthn` map (`rp_id`, optional `rp_origins`)
## Example
(challenge client "<access-token>" "<factor-id>")
(challenge client "<access-token>" "<factor-id>" {:channel "sms"})(challenge-and-verify client access-token factor-id code)Challenge + verify in one call, for TOTP factors where the code is already at hand. Phone and WebAuthn factors need the separate calls.
(challenge-and-verify client "<access-token>" "<factor-id>" "123456")
Challenge + verify in one call, for TOTP factors where the code is
already at hand. Phone and WebAuthn factors need the separate calls.
## Example
(challenge-and-verify client "<access-token>" "<factor-id>" "123456")(enroll client access-token params)Enrolls a new MFA factor for the user. The factor starts unverified;
complete a challenge + verify round-trip to activate it.
client — Supabase clientaccess-token — the user's access tokenparams:
:factor-type — "totp", "phone" or "webauthn" (required):friendly-name — label shown in factor lists:issuer — TOTP issuer domain (totp only):phone — E.164 phone number (required for phone factors)For TOTP the response body carries :totp with the QR code SVG, secret
and provisioning URI to present to the user.
When a "webauthn" enrollment with a :friendly-name fails, the stale
unverified factor a previous failed registration left under that name is
unenrolled first, so a retry with the same name can succeed. Verified
factors are never touched. Mirrors auth-js #2641.
(enroll client "<access-token>" {:factor-type "totp"})
Enrolls a new MFA factor for the user. The factor starts `unverified`;
complete a `challenge` + `verify` round-trip to activate it.
## Parameters
* `client` — Supabase client
* `access-token` — the user's access token
* `params`:
* `:factor-type` — `"totp"`, `"phone"` or `"webauthn"` (required)
* `:friendly-name` — label shown in factor lists
* `:issuer` — TOTP issuer domain (totp only)
* `:phone` — E.164 phone number (required for phone factors)
For TOTP the response body carries `:totp` with the QR code SVG, secret
and provisioning URI to present to the user.
When a `"webauthn"` enrollment with a `:friendly-name` fails, the stale
unverified factor a previous failed registration left under that name is
unenrolled first, so a retry with the same name can succeed. Verified
factors are never touched. Mirrors auth-js #2641.
## Example
(enroll client "<access-token>" {:factor-type "totp"})(generate-recovery-codes client access-token)(generate-recovery-codes client access-token params)Generates the user's set of recovery codes. The plaintext :codes in the
response body are returned exactly once and cannot be retrieved again;
present them to the user for safe-keeping.
params:
:friendly-name — label for the recovery codes factor, as shown in
factor lists. Must be unique among the user's factors; the server
defaults it to "Recovery codes" when omitted. No request body is
sent unless a name is given.Experimental: requires recovery codes to be enabled on the server.
(generate-recovery-codes client "<access-token>")
(generate-recovery-codes client "<access-token>" {:friendly-name "backup"})
Generates the user's set of recovery codes. The plaintext `:codes` in the
response body are returned exactly once and cannot be retrieved again;
present them to the user for safe-keeping.
`params`:
* `:friendly-name` — label for the recovery codes factor, as shown in
factor lists. Must be unique among the user's factors; the server
defaults it to `"Recovery codes"` when omitted. No request body is
sent unless a name is given.
Experimental: requires recovery codes to be enabled on the server.
## Example
(generate-recovery-codes client "<access-token>")
(generate-recovery-codes client "<access-token>" {:friendly-name "backup"})(get-authenticator-assurance-level client access-token)Returns the user's current and next possible authenticator assurance levels.
The current level and authentication methods come from the JWT claims
(decoded locally); the next achievable level requires the user's factor
list, so this makes one get-user call.
Returns {:current-level :next-level :current-authentication-methods}
on success — levels are "aal1" / "aal2", methods the raw amr
claim entries — or an anomaly on failure.
(get-authenticator-assurance-level client "<access-token>")
Returns the user's current and next possible authenticator assurance
levels.
* AAL1 — single factor (password, magic link, OAuth)
* AAL2 — at least one verified MFA factor was used
The current level and authentication methods come from the JWT claims
(decoded locally); the next achievable level requires the user's factor
list, so this makes one `get-user` call.
Returns `{:current-level :next-level :current-authentication-methods}`
on success — levels are `"aal1"` / `"aal2"`, methods the raw `amr`
claim entries — or an anomaly on failure.
## Example
(get-authenticator-assurance-level client "<access-token>")(get-recovery-codes-status client access-token)Returns the enrollment status of the user's recovery codes.
On success the body is a map with :id (the recovery codes factor id),
:type (always "recovery_code"), :total (codes in the current set)
and :remaining (codes not yet consumed). It never contains code values.
Experimental: requires recovery codes to be enabled on the server.
(get-recovery-codes-status client "<access-token>")
Returns the enrollment status of the user's recovery codes.
On success the body is a map with `:id` (the recovery codes factor id),
`:type` (always `"recovery_code"`), `:total` (codes in the current set)
and `:remaining` (codes not yet consumed). It never contains code values.
Experimental: requires recovery codes to be enabled on the server.
## Example
(get-recovery-codes-status client "<access-token>")(list-factors client access-token)Lists the user's MFA factors, grouped for convenience.
On success the body is a map with:
:all — every factor, verified or not:totp / :phone / :webauthn — verified factors of that type(list-factors client "<access-token>")
Lists the user's MFA factors, grouped for convenience.
On success the body is a map with:
* `:all` — every factor, verified or not
* `:totp` / `:phone` / `:webauthn` — verified factors of that type
## Example
(list-factors client "<access-token>")(unenroll client access-token factor-id)Removes the factor factor-id from the user's account. Permanent.
(unenroll client "<access-token>" "<factor-id>")
Removes the factor `factor-id` from the user's account. Permanent.
## Example
(unenroll client "<access-token>" "<factor-id>")(verify client access-token factor-id challenge-id params)Verifies the challenge challenge-id for factor factor-id. On success
the body carries a new session with elevated assurance level (AAL2).
params is factor-type specific:
{:code "123456"}{:webauthn {...credential response...}}(verify client "<access-token>" "<factor-id>" "<challenge-id>"
{:code "123456"})
Verifies the challenge `challenge-id` for factor `factor-id`. On success
the body carries a new session with elevated assurance level (AAL2).
`params` is factor-type specific:
* TOTP / phone — `{:code "123456"}`
* WebAuthn — `{:webauthn {...credential response...}}`
## Example
(verify client "<access-token>" "<factor-id>" "<challenge-id>"
{:code "123456"})(verify-recovery-code client access-token params)Verifies one of the user's recovery codes and upgrades the session to AAL2. Each code can be used only once.
params: {:code "K4M9-X7QP-2AB8-HT3Z"}. Letter case, whitespace and
- separators are ignored by the server, so the code can be passed
exactly as the user typed it.
On success the body carries a fresh session (new access/refresh tokens) that replaces the current one: adopt it as the active session, as the user's other AAL1 sessions are signed out server-side.
Experimental: requires recovery codes to be enabled on the server.
(verify-recovery-code client "<access-token>" {:code "K4M9-X7QP-2AB8-HT3Z"})
Verifies one of the user's recovery codes and upgrades the session to
AAL2. Each code can be used only once.
`params`: `{:code "K4M9-X7QP-2AB8-HT3Z"}`. Letter case, whitespace and
`-` separators are ignored by the server, so the code can be passed
exactly as the user typed it.
On success the body carries a fresh session (new access/refresh tokens)
that replaces the current one: adopt it as the active session, as the
user's other AAL1 sessions are signed out server-side.
Experimental: requires recovery codes to be enabled on the server.
## Example
(verify-recovery-code client "<access-token>" {:code "K4M9-X7QP-2AB8-HT3Z"})cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |