KMS factors add steps after principal password authentication. A principal can have zero or more enabled factors.
If no factors are enabled, KMS.create_session/3 returns an authenticated session.
If factors are enabled, KMS.create_session/3 returns a pending session plus challenge metadata. The application collects factor input from the user and calls KMS.advance_session/2 until the session is authenticated.
Built-in providers
webauthn: passkeys/WebAuthn using server-side FIDO2 verification.totp: production-usable RFC 6238 authenticator-app codes.one_time_code: development/test provider that returns the code in the challenge response.
Factor records
A factor belongs to a principal and has:
provider: provider name, e.g."totp"or"webauthn".label: user-facing label, e.g."authenticator".position: order used when multiple factors exist.enabled: disabled factors are ignored.config: provider-specific configuration. This field is redacted from inspect output and omitted from HTTP factor responses.
Create a factor:
{:ok, factor} =
KMS.create_principal_factor("users", "alice", %{
provider: "totp",
label: "authenticator",
position: 1,
config: config
})Remote HTTP API:
POST /api/principal_factors/createAuthentication flow
Create a session with password:
{:ok, session} = KMS.create_session("users", "alice", %{password: password})If factors are enabled, the session is pending:
session.state == "pending"
session.challenge.provider == "totp"Advance with user input:
{:ok, session} = KMS.advance_session(session.secret, %{code: "123456"})When all factors complete, the session becomes authenticated.
Rate limits
KMS applies factor controls:
config :kms, :session_factor_attempt_limit, 5
config :kms, :session_factor_rate_limit, [limit: 5, window_ms: :timer.minutes(5)]session_factor_attempt_limit caps failed factor attempts per pending session.
session_factor_rate_limit is optional fixed-window throttling. Enable it for remote deployments.
Custom providers
Custom providers implement KMS.Principals.FactorProvider.
Provider callbacks:
issue/3: creates challenge metadata and a factor secret used to protect pending session private-key material.- optional
verify/4: verifies user input and returns the factor secret.
Register providers:
config :kms, :factor_providers, %{
"sms" => MyApp.KMS.SMSFactor
}Production providers should deliver codes out of band and must not return secret codes in challenge responses.