Supabase.Auth.MFA.Behaviour behaviour (supabase_auth v1.0.1)

View Source

Behaviour for MFA operations with type definitions.

This module defines all type specifications for Multi-Factor Authentication operations including factors, challenges, verification responses, and authenticator assurance levels.

Factor Types

Three types of MFA factors are supported:

  • TOTP (:totp) - Time-based One-Time Password using authenticator apps
  • Phone (:phone) - SMS or WhatsApp-based verification
  • WebAuthn (:webauthn) - Hardware security keys and biometric authentication

Type Hierarchy

All factor types share common fields (id, friendly_name, status, timestamps) but have type-specific additional fields:

  • totp_factor/0 includes totp field with QR code, secret, and URI
  • phone_factor/0 includes phone field with E.164 formatted number
  • webauthn_factor/0 has no additional type-specific fields

The factor/0 type is a union of all three specific factor types.

Summary

Types

aal_level()

@type aal_level() :: :aal1 | :aal2

aal_response()

@type aal_response() :: %{
  current_level: aal_level() | nil,
  next_level: aal_level() | nil,
  current_authentication_methods: [String.t()]
}

challenge_response()

@type challenge_response() :: %{
  id: String.t(),
  type: factor_type(),
  expires_at: integer()
}

enroll_response()

@type enroll_response() :: totp_factor() | phone_factor() | webauthn_factor()

factor()

@type factor() :: totp_factor() | phone_factor() | webauthn_factor()

factor_base()

@type factor_base() :: %{
  id: String.t(),
  friendly_name: String.t() | nil,
  factor_type: factor_type(),
  status: factor_status(),
  created_at: String.t(),
  updated_at: String.t(),
  last_challenged_at: String.t() | nil
}

factor_status()

@type factor_status() :: :verified | :unverified

factor_type()

@type factor_type() :: :totp | :phone | :webauthn

factors_list()

@type factors_list() :: %{
  all: [factor()],
  totp: [totp_factor()],
  phone: [phone_factor()],
  webauthn: [webauthn_factor()]
}

phone_factor()

@type phone_factor() :: %{
  id: String.t(),
  friendly_name: String.t() | nil,
  factor_type: :phone,
  status: factor_status(),
  phone: String.t(),
  created_at: String.t(),
  updated_at: String.t(),
  last_challenged_at: String.t() | nil
}

totp_data()

@type totp_data() :: %{qr_code: String.t(), secret: String.t(), uri: String.t()}

totp_factor()

@type totp_factor() :: %{
  id: String.t(),
  friendly_name: String.t() | nil,
  factor_type: :totp,
  status: factor_status(),
  totp: totp_data(),
  created_at: String.t(),
  updated_at: String.t(),
  last_challenged_at: String.t() | nil
}

webauthn_challenge_response()

@type webauthn_challenge_response() :: %{
  id: String.t(),
  type: :webauthn,
  expires_at: integer(),
  webauthn: %{type: String.t(), credential_options: map()}
}

webauthn_factor()

@type webauthn_factor() :: %{
  id: String.t(),
  friendly_name: String.t() | nil,
  factor_type: :webauthn,
  status: factor_status(),
  created_at: String.t(),
  updated_at: String.t(),
  last_challenged_at: String.t() | nil
}

Callbacks

challenge(t, t, t, map)

@callback challenge(Supabase.Client.t(), Supabase.Auth.Session.t(), String.t(), map()) ::
  {:ok, challenge_response() | webauthn_challenge_response()} | {:error, term()}

challenge_and_verify(t, t, t, t)

@callback challenge_and_verify(
  Supabase.Client.t(),
  Supabase.Auth.Session.t(),
  String.t(),
  String.t()
) ::
  {:ok, Supabase.Auth.Session.t()} | {:error, term()}

enroll(t, t, map)

@callback enroll(Supabase.Client.t(), Supabase.Auth.Session.t(), map()) ::
  {:ok, enroll_response()} | {:error, term()}

get_authenticator_assurance_level(t, t)

@callback get_authenticator_assurance_level(
  Supabase.Client.t(),
  Supabase.Auth.Session.t()
) ::
  {:ok, aal_response()} | {:error, term()}

list_factors(t, t)

@callback list_factors(Supabase.Client.t(), Supabase.Auth.Session.t()) ::
  {:ok, factors_list()} | {:error, term()}

unenroll(t, t, t)

@callback unenroll(Supabase.Client.t(), Supabase.Auth.Session.t(), String.t()) ::
  {:ok, %{id: String.t()}} | {:error, term()}

verify(t, t, t, t, map)

@callback verify(
  Supabase.Client.t(),
  Supabase.Auth.Session.t(),
  String.t(),
  String.t(),
  map()
) ::
  {:ok, Supabase.Auth.Session.t()} | {:error, term()}