Create a challenge
Start a verification challenge for an enrolled MFA factor.
Create a short-lived challenge token for an enrolled factor. The challenge is the second step of the MFA flow: enroll → challenge → verify. For phone factors, this is the call that actually sends the SMS / WhatsApp message containing the one-time code.
A live session is required — challenge calls get_session internally and raises Supabase::Auth::Errors::AuthSessionMissing if the user is signed out.
Signature
supabase.auth.mfa.challenge(params)params is a hash. Pass it as a literal ({ factor_id: "..." }) or use Ruby's hash-literal shorthand (factor_id: "...").
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
factor_id | String | Required | The ID of an enrolled factor (returned by mfa.enroll). |
channel | String | Optional | Phone factors only. Either "sms" or "whatsapp" — selects the delivery channel for the OTP. Ignored for TOTP factors. |
Returns
A Struct with :id (the challenge ID, required when calling mfa.verify), :factor_type (echoed back from the factor — "totp" or "phone"), and :expires_at (Unix timestamp when this challenge becomes invalid).
Example — challenge a TOTP factor
challenge = supabase.auth.mfa.challenge(factor_id: factor_id)
challenge.id # => pass this to mfa.verify
challenge.factor_type # => "totp"
challenge.expires_at # => 1735689600Example — phone factor with explicit channel
# Sends an SMS to the phone number registered with the factor.
challenge = supabase.auth.mfa.challenge(
factor_id: phone_factor_id,
channel: "sms"
)
# Or use WhatsApp if your project is configured for it:
challenge = supabase.auth.mfa.challenge(
factor_id: phone_factor_id,
channel: "whatsapp"
)Example — handling an expired/no-session caller
begin
challenge = supabase.auth.mfa.challenge(factor_id: factor_id)
rescue Supabase::Auth::Errors::AuthSessionMissing
# No live session — send the user back through sign-in first.
redirect_to_sign_in
end