supabase-rb-rb
Auth

Verify a challenge

Submit a code to satisfy an MFA challenge.

Complete the MFA flow by submitting the code the user supplied for an outstanding mfa.challenge. On success GoTrue returns a fresh access token with the elevated assurance level (aal2), the new tokens are saved to the local session, and a MFA_CHALLENGE_VERIFIED event is dispatched to any auth-state-change subscribers.

A live session is required — verify calls get_session internally and raises Supabase::Auth::Errors::AuthSessionMissing if the user is signed out.

Signature

supabase.auth.mfa.verify(params)

params is a hash. Pass it as a literal ({ factor_id: "...", challenge_id: "...", code: "..." }) or use Ruby's hash-literal shorthand.

Parameters

NameTypeRequiredDescription
factor_idStringRequiredThe ID of the enrolled factor that the challenge belongs to.
challenge_idStringRequiredThe challenge ID returned by mfa.challenge.
codeStringRequiredThe one-time code the user obtained — for TOTP, the 6-digit code from their authenticator app; for phone factors, the code from the SMS / WhatsApp message.

Returns

Returns
Supabase::Auth::Types::AuthMFAVerifyResponse

A Struct with :access_token, :token_type, :expires_in, :refresh_token, and :user. The new access token has its aal claim upgraded to aal2. The same tokens are also persisted in the client's session store and the bearer is rotated, so subsequent calls automatically use the upgraded JWT.

Example — verify a TOTP code

verify = supabase.auth.mfa.verify(
  factor_id: factor_id,
  challenge_id: challenge.id,
  code: "123456"
)

verify.access_token   # => new JWT with aal: "aal2"
verify.user.email

Example — listen for MFA_CHALLENGE_VERIFIED

supabase.auth.on_auth_state_change do |event, session|
  if event == "MFA_CHALLENGE_VERIFIED"
    puts "User upgraded to AAL2 at #{Time.now}"
  end
end

# Later, in the verify step:
supabase.auth.mfa.verify(
  factor_id: factor_id,
  challenge_id: challenge.id,
  code: code
)
# => block above fires with event = "MFA_CHALLENGE_VERIFIED"

Example — handling a bad code

begin
  supabase.auth.mfa.verify(
    factor_id: factor_id,
    challenge_id: challenge.id,
    code: user_supplied_code
  )
rescue Supabase::Auth::Errors::AuthApiError => e
  # GoTrue returns 4xx for invalid / expired codes.
  flash[:error] = "That code didn't work. Try again or request a new challenge."
end

Verify save-session edge case

If GoTrue ever returns a response without an access_token field, the local-session save is silently skipped and MFA_CHALLENGE_VERIFIED is NOT fired. In practice GoTrue always returns the full session on success, so this only matters if you're proxying responses through a custom layer.

On this page