Authsome

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

OptionTypeDefaultDescription
Issuerstring"AuthSome"Name displayed in authenticator apps (Google Authenticator, Authy, etc.)

Implemented interfaces

InterfacePurpose
PluginBase plugin identity ("mfa")
OnInitCaptures SMS sender, Chronicle, Relay, hooks, logger, and ceremony store
RouteProviderRegisters all MFA endpoints under /v1/auth/mfa
MigrationProviderContributes 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

MethodPathDescription
POST/v1/auth/mfa/enrollStart MFA enrollment (TOTP or SMS)
POST/v1/auth/mfa/verifyVerify TOTP code to confirm enrollment
POST/v1/auth/mfa/challengeVerify TOTP code during sign-in challenge
DELETE/v1/auth/mfa/enrollmentDisable MFA for the authenticated user
POST/v1/auth/mfa/recovery/verifyVerify a recovery code
POST/v1/auth/mfa/recovery/regenerateGenerate new recovery codes
POST/v1/auth/mfa/sms/sendSend an SMS verification code
POST/v1/auth/mfa/sms/verifyVerify 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 started
  • auth.mfa.verified -- MFA enrollment confirmed
  • auth.mfa.challenged -- MFA challenge passed
  • auth.mfa.disabled -- MFA enrollment removed
  • auth.mfa.recovery_used -- Recovery code consumed
  • auth.mfa.recovery_regenerated -- Recovery codes regenerated

On this page