supabase-rb-rb
Auth

Enroll a factor

Enroll a new TOTP or phone MFA factor.

Register a new multi-factor authentication factor for the currently signed-in user. After enrolling, the factor stays in unverified status until a challenge is solved with mfa.verify (or mfa.challenge_and_verify).

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

Signature

supabase.auth.mfa.enroll(params)

params is a hash. Pass it as a literal ({ factor_type: "totp" }) or use Ruby's hash-literal shorthand (factor_type: "totp").

Parameters

NameTypeRequiredDescription
factor_typeStringRequiredEither "totp" or "phone". Determines which sub-fields are honored and what payload is returned.
friendly_nameStringOptionalOptional human-readable label for the factor (e.g. "iPhone Authenticator"). Shown in factor listings.
issuerStringOptionalTOTP only. The "issuer" string embedded in the otpauth:// URI — most authenticator apps display it next to the code. Defaults to the project hostname.
phoneStringOptionalPhone factors only. The recipient phone number in E.164 format.

Returns

Returns
Supabase::Auth::Types::AuthMFAEnrollResponse

A Struct with :id, :type, :friendly_name, :totp, and :phone. For TOTP factors :totp is itself a Struct with :qr_code (a data:image/svg+xml;utf-8,... URL ready to embed in an <img> tag), :secret (the raw base32 seed), and :uri (the full otpauth:// URI). For phone factors :phone carries the enrolled number; :totp is nil.

Example — TOTP full enroll → challenge → verify cycle

# Step 1 — enroll a TOTP factor
enroll = supabase.auth.mfa.enroll(
  factor_type: "totp",
  friendly_name: "Ada's iPhone",
  issuer: "Example App"
)

factor_id = enroll.id
qr_code   = enroll.totp.qr_code     # data:image/svg+xml;utf-8,...
secret    = enroll.totp.secret      # base32 seed for manual entry

# Show qr_code in your UI; ask the user to scan it with their authenticator app.

# Step 2 — start a challenge once the user is ready to enter a code
challenge = supabase.auth.mfa.challenge(factor_id: factor_id)
challenge_id = challenge.id

# Step 3 — collect the 6-digit code from the user and verify
verify = supabase.auth.mfa.verify(
  factor_id: factor_id,
  challenge_id: challenge_id,
  code: "123456"
)

verify.access_token   # => upgraded JWT (aal2)
verify.user.factors   # => includes the now-verified factor

Example — phone factor

response = supabase.auth.mfa.enroll(
  factor_type: "phone",
  friendly_name: "Personal SMS",
  phone: "+15555550123"
)

response.id      # => factor id, status is "unverified" until challenge + verify
response.phone   # => "+15555550123"
response.totp    # => nil

Example — minimal TOTP enrollment

# Most fields are optional; only factor_type is required.
response = supabase.auth.mfa.enroll(factor_type: "totp")

response.totp.uri   # otpauth://totp/...?secret=...&issuer=...

The :qr_code returned by GoTrue is a raw SVG string; enroll rewrites it as a data:image/svg+xml;utf-8,... URL so it can be dropped straight into an <img src> without a base64 step. Passing factor_type: "phone" without phone: surfaces as a GoTrue 4xx, not a client-side error.

On this page