MFA Plugin
Multi-factor authentication with TOTP, SMS OTP, recovery codes, and challenge flows.
The mfa plugin adds multi-factor authentication to Authsome. It supports TOTP (Time-based One-Time Passwords) for authenticator apps, SMS-based OTP as a second factor, and recovery codes for account recovery when the primary MFA method is unavailable.
Setup
import (
"github.com/xraph/authsome"
"github.com/xraph/authsome/plugins/mfa"
)
eng, err := authsome.NewEngine(
authsome.WithStore(store),
authsome.WithSMSSender(smsBridge), // Required for SMS MFA
authsome.WithPlugin(mfa.New(mfa.Config{
Issuer: "My App",
})),
)Configuration
| Option | Type | Default | Description |
|---|---|---|---|
Issuer | string | "AuthSome" | Name displayed in authenticator apps (Google Authenticator, Authy, etc.) |
Implemented interfaces
| Interface | Purpose |
|---|---|
Plugin | Base plugin identity ("mfa") |
OnInit | Captures SMS sender, Chronicle, Relay, hooks, logger, and ceremony store |
RouteProvider | Registers all MFA endpoints under /v1/auth/mfa |
MigrationProvider | Contributes mfa_enrollments and mfa_recovery_codes tables |
TOTP enrollment
TOTP (RFC 6238) is the recommended MFA method. It works with any authenticator app that supports the otpauth:// URI scheme.
Step 1: Start enrollment
POST /v1/auth/mfa/enroll
{"method": "totp"}Response:
{
"id": "amfa_01j9...",
"method": "totp",
"secret": "JBSWY3DPEHPK3PXP",
"otpauth_url": "otpauth://totp/My%20App:alice@example.com?secret=JBSWY3DPEHPK3PXP&issuer=My%20App"
}The otpauth_url can be rendered as a QR code for the user to scan with their authenticator app. The secret is displayed as a manual entry fallback.
Step 2: Verify enrollment
POST /v1/auth/mfa/verify
{"code": "123456"}The user enters the 6-digit code from their authenticator app. On first successful verification, the enrollment is marked as verified and recovery codes are generated.
Response:
{
"verified": true,
"method": "totp",
"recovery_codes": [
"a1b2c3d4e5",
"f6g7h8i9j0",
"k1l2m3n4o5"
]
}Recovery codes are shown only once. The user must store them securely.
SMS MFA
SMS OTP provides a second factor using text messages. It requires an SMS bridge to be configured.
Enroll via SMS
POST /v1/auth/mfa/enroll
{"method": "sms", "phone": "+14155551234"}The plugin sends an initial verification code to the provided phone number.
Send SMS code
POST /v1/auth/mfa/sms/send
{"phone": "+14155551234"}Response:
{
"sent": true,
"expires_in_seconds": 300,
"phone_masked": "***1234"
}Verify SMS code
POST /v1/auth/mfa/sms/verify
{"code": "123456"}MFA challenge flow
During sign-in, if the user has MFA enrolled, the engine requires an MFA challenge before issuing a full session.
POST /v1/auth/mfa/challenge
{"code": "123456"}Response:
{
"challenge_passed": true,
"method": "totp"
}The challenge validates the TOTP code against the user's enrolled secret. On success, the session is upgraded to full access.
Recovery codes
Recovery codes are generated when MFA is first verified. Each code is one-time use and bcrypt-hashed before storage.
Verify a recovery code
POST /v1/auth/mfa/recovery/verify
{"code": "a1b2c3d4e5"}Response:
{
"challenge_passed": true,
"codes_remaining": 7
}Regenerate recovery codes
POST /v1/auth/mfa/recovery/regenerate
Generates a fresh set of recovery codes, replacing all existing codes. Requires an active MFA enrollment.
Response:
{
"codes": ["x1y2z3...", "a4b5c6...", "..."]
}Disable MFA
DELETE /v1/auth/mfa/enrollment
Removes the MFA enrollment for the authenticated user. Recovery codes are not automatically deleted.
Checking MFA status
The plugin exposes a HasMFA method for programmatic checks:
mfaPlugin := mfa.New(mfa.Config{Issuer: "My App"})
// ... register and start engine ...
hasMFA := mfaPlugin.HasMFA(ctx, userID)This checks both TOTP and SMS enrollments.
API routes
| Method | Path | Description |
|---|---|---|
POST | /v1/auth/mfa/enroll | Start MFA enrollment (TOTP or SMS) |
POST | /v1/auth/mfa/verify | Verify TOTP code to confirm enrollment |
POST | /v1/auth/mfa/challenge | Verify TOTP code during sign-in challenge |
DELETE | /v1/auth/mfa/enrollment | Disable MFA for the authenticated user |
POST | /v1/auth/mfa/recovery/verify | Verify a recovery code |
POST | /v1/auth/mfa/recovery/regenerate | Generate new recovery codes |
POST | /v1/auth/mfa/sms/send | Send an SMS verification code |
POST | /v1/auth/mfa/sms/verify | Verify an SMS code |
Observability
The MFA plugin emits audit events to Chronicle, webhook events to Relay, and hook events on the global bus for:
auth.mfa.enrolled-- MFA enrollment startedauth.mfa.verified-- MFA enrollment confirmedauth.mfa.challenged-- MFA challenge passedauth.mfa.disabled-- MFA enrollment removedauth.mfa.recovery_used-- Recovery code consumedauth.mfa.recovery_regenerated-- Recovery codes regenerated