Create and verify a challenge
Challenge and verify an MFA factor in one call.
Shortcut that combines mfa.challenge and mfa.verify into a single call. Useful for TOTP, where you already have the user's code in hand and don't need a separate "send the SMS" step. The challenge ID is generated internally and threaded into verify automatically.
A live session is required — the inner challenge call raises Supabase::Auth::Errors::AuthSessionMissing if the user is signed out.
Signature
supabase.auth.mfa.challenge_and_verify(params)params is a hash. Pass it as a literal or use Ruby's hash-literal shorthand.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
factor_id | String | Required | The ID of the enrolled factor to challenge + verify. |
code | String | Required | The one-time code the user supplied — for TOTP, the 6-digit code from their authenticator app. |
channel | String | Optional | Phone factors only. Forwarded to the inner challenge — "sms" or "whatsapp". |
Returns
Same shape as mfa.verify: :access_token, :token_type, :expires_in, :refresh_token, :user. The local session is updated and MFA_CHALLENGE_VERIFIED is dispatched.
Example — TOTP verify in one call
# Most common use: user already has a 6-digit code from their authenticator app.
verify = supabase.auth.mfa.challenge_and_verify(
factor_id: factor_id,
code: "123456"
)
verify.access_token # => upgraded JWT (aal2)Example — equivalent two-step form
# These two snippets are equivalent:
# Combined:
supabase.auth.mfa.challenge_and_verify(factor_id: factor_id, code: code)
# Explicit:
challenge = supabase.auth.mfa.challenge(factor_id: factor_id)
supabase.auth.mfa.verify(
factor_id: factor_id,
challenge_id: challenge.id,
code: code
)Example — phone factor with channel
# Note: this still makes TWO HTTP calls (challenge then verify), so the user
# must already have received the SMS/WhatsApp code from a prior challenge —
# or the inner challenge will send a new one and you'll need to wait for it.
verify = supabase.auth.mfa.challenge_and_verify(
factor_id: phone_factor_id,
channel: "sms",
code: user_supplied_code
):channel is forwarded to the inner challenge call. If you want the channel selection, pass it; otherwise leave it off and the default is "sms".