mfa

package
v0.0.11 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 3, 2026 License: Apache-2.0 Imports: 26 Imported by: 0

README

MFA Plugin

Status:Implementation Complete

Multi-Factor Authentication (MFA) plugin for AuthSome. Provides orchestration of multiple authentication factors with risk-based adaptive authentication, step-up auth, and comprehensive policy management.

Features

Core Capabilities
  • Multiple Factors Per User - Support 2, 3, or more authentication factors
  • Factor Types - TOTP, SMS, Email, WebAuthn (experimental), Backup Codes
  • Policy Engine - Require N of M factors, organization-specific policies
  • Risk-Based Authentication - Adaptive MFA based on location, device, velocity
  • Step-Up Authentication - Require recent verification for sensitive operations
  • Trusted Devices - Skip MFA on recognized devices
  • Rate Limiting - Brute force protection with lockout
  • Comprehensive Audit Trail - All MFA operations logged
Integration
  • Adapter Pattern - Reuses existing plugins (twofa, emailotp, phone, passkey)
  • No Code Duplication - Delegates to specialized plugins
  • Backward Compatible - Legacy 2FA routes supported
  • Middleware Support - RequireMFA, StepUpAuth, AdaptiveMFA

Quick Start

1. Installation

Add to your AuthSome initialization:

import (
    "github.com/xraph/authsome"
    "github.com/xraph/authsome/plugins/mfa"
    "github.com/xraph/authsome/plugins/twofa"
    "github.com/xraph/authsome/plugins/emailotp"
    "github.com/xraph/authsome/plugins/phone"
)

func main() {
    auth := authsome.New(
        db,
        authsome.WithPlugins(
            twofa.NewPlugin(),    // Provides TOTP and backup codes
            emailotp.NewPlugin(), // Provides email verification
            phone.NewPlugin(),    // Provides SMS verification
            mfa.NewPlugin(),      // MFA orchestration
        ),
    )
}
2. Configuration
auth:
  mfa:
    enabled: true
    required_factor_count: 2  # Require at least 2 factors
    allowed_factor_types:
      - totp
      - sms
      - email
      - backup
    
    # TOTP Configuration
    totp:
      enabled: true
      issuer: "MyApp"
      period: 30
      digits: 6
    
    # SMS Configuration  
    sms:
      enabled: true
      provider: "twilio"
      code_length: 6
      code_expiry_minutes: 5
    
    # Email Configuration
    email:
      enabled: true
      code_length: 6
      code_expiry_minutes: 10
    
    # Backup Codes
    backup_codes:
      enabled: true
      count: 10
      length: 8
    
    # Trusted Devices
    trusted_devices:
      enabled: true
      default_expiry_days: 30
      max_expiry_days: 90
    
    # Rate Limiting
    rate_limit:
      enabled: true
      max_attempts: 5
      window_minutes: 15
      lockout_minutes: 30
    
    # Adaptive MFA (Risk-Based)
    adaptive_mfa:
      enabled: true
      risk_threshold: 50.0
      factor_location_change: true
      factor_new_device: true
      factor_velocity: true
3. Enroll a Factor
// Enroll TOTP
const response = await fetch('/auth/mfa/factors/enroll', {
    method: 'POST',
    headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        type: 'totp',
        priority: 'primary',
        name: 'My Authenticator App'
    })
});

const { factor_id, provisioning_data } = await response.json();
const { qr_uri, secret } = provisioning_data;

// Show QR code to user
displayQRCode(qr_uri);

// After user scans, verify enrollment
await fetch(`/auth/mfa/factors/${factor_id}/verify`, {
    method: 'POST',
    body: JSON.stringify({
        code: userEnteredCode
    })
});
4. Verify MFA
// Step 1: Initiate challenge
const challengeResp = await fetch('/auth/mfa/challenge', {
    method: 'POST',
    headers: { 'Authorization': `Bearer ${token}` }
});

const { session_id, available_factors } = await challengeResp.json();

// Step 2: User selects factor and provides code
const verifyResp = await fetch('/auth/mfa/verify', {
    method: 'POST',
    body: JSON.stringify({
        challenge_id: session_id,
        factor_id: selectedFactorId,
        code: userProvidedCode,
        remember_device: true,
        device_info: {
            device_id: deviceFingerprint,
            name: 'My Device'
        }
    })
});

const { success, session_complete, token: mfaToken } = await verifyResp.json();

// Use mfaToken for subsequent requests

API Endpoints

Factor Management
Method Endpoint Description
POST /auth/mfa/factors/enroll Enroll new factor
GET /auth/mfa/factors List user's factors
GET /auth/mfa/factors/:id Get factor details
PUT /auth/mfa/factors/:id Update factor
DELETE /auth/mfa/factors/:id Delete factor
POST /auth/mfa/factors/:id/verify Verify enrollment
Challenge & Verification
Method Endpoint Description
POST /auth/mfa/challenge Initiate MFA challenge
POST /auth/mfa/verify Verify challenge response
GET /auth/mfa/challenge/:id Get challenge status
Trusted Devices
Method Endpoint Description
POST /auth/mfa/devices/trust Trust a device
GET /auth/mfa/devices List trusted devices
DELETE /auth/mfa/devices/:id Revoke device trust
Status & Policy
Method Endpoint Description
GET /auth/mfa/status Get user's MFA status
GET /auth/mfa/policy Get organization policy
Admin Endpoints

Requires admin role and mfa:admin permission.

Method Endpoint Description
GET /mfa/admin/policy Get MFA policy for app
PUT /mfa/admin/policy Update MFA policy
POST /mfa/admin/bypass Grant temporary MFA bypass
POST /mfa/admin/users/:id/reset Reset user's MFA factors
Get MFA Policy
GET /mfa/admin/policy
Authorization: Bearer <admin-token>

Response:

{
  "appId": "app_123",
  "requiredFactors": 1,
  "allowedTypes": ["totp", "sms", "email", "webauthn", "backup"],
  "gracePeriod": 86400,
  "enabled": true
}
Update MFA Policy
PUT /mfa/admin/policy
Authorization: Bearer <admin-token>
Content-Type: application/json

{
  "requiredFactors": 2,
  "allowedTypes": ["totp", "webauthn"],
  "gracePeriod": 3600,
  "enabled": true
}
Grant Temporary MFA Bypass
POST /mfa/admin/bypass
Authorization: Bearer <admin-token>
Content-Type: application/json

{
  "userId": "usr_123",
  "duration": 86400,
  "reason": "User lost device, temporary bypass for 24h"
}

Response:

{
  "message": "MFA bypass granted successfully",
  "userId": "usr_123",
  "expiresAt": "+86400 seconds"
}
Reset User's MFA
POST /mfa/admin/users/usr_123/reset
Authorization: Bearer <admin-token>

Response:

{
  "message": "MFA reset successfully",
  "userId": "usr_123",
  "appId": "app_123"
}

Note: Admin endpoints are currently placeholders. Full implementation requires:

  • Database schema for app-specific MFA policies
  • RBAC integration for permission checks
  • MFA bypass storage with expiry
  • Audit logging for administrative actions

See Plugin Admin Endpoint Guidelines for implementation details.

Middleware Usage

Require MFA
import "github.com/xraph/authsome/plugins/mfa"

// Require MFA for all requests
router.Use(mfa.RequireMFA(mfaService))

// Require specific factor type
router.Use(mfa.RequireFactorType(mfaService, mfa.FactorTypeTOTP))
Step-Up Authentication
// Require MFA within last 5 minutes for sensitive operations
router.POST("/transfer", 
    mfa.StepUpAuth(mfaService, 5*time.Minute),
    transferHandler)

router.DELETE("/account",
    mfa.StepUpAuth(mfaService, 1*time.Minute),
    deleteAccountHandler)
Adaptive MFA
// Apply risk-based MFA requirements
router.Use(mfa.AdaptiveMFA(mfaService))

// Low risk: no MFA required
// Medium risk: 1 factor required
// High risk: 2+ factors required
// Critical risk: recent step-up required

Factor Types

TOTP (Time-based One-Time Password)
  • Uses authenticator apps (Google Authenticator, Authy, 1Password)
  • Generates 6-digit codes every 30 seconds
  • Most secure, doesn't require SMS/email
  • Backed by twofa plugin
SMS (Text Message)
  • Sends verification code via SMS
  • Requires phone number
  • Subject to SMS interception risks
  • Backed by phone plugin
Email
  • Sends verification code via email
  • Requires email address
  • Convenient but less secure than TOTP
  • Backed by emailotp plugin
WebAuthn (Security Keys)
  • Hardware security keys (YubiKey, etc.)
  • Biometric authentication (Touch ID, Face ID)
  • Most secure option
  • Backed by passkey plugin (experimental)
Backup Codes
  • One-time use recovery codes
  • Generated during enrollment
  • Used when primary factors unavailable
  • Backed by twofa plugin

Risk-Based Authentication

The MFA plugin includes a risk assessment engine that evaluates:

Risk Factors
  • Location Change - New city/country
  • New Device - Unrecognized device
  • Velocity - Rapid login attempts
  • IP Reputation - Known malicious IPs
Risk Levels
  • Low (0-25) - Normal behavior, minimal MFA
  • Medium (25-50) - Some anomalies, require 1 factor
  • High (50-75) - Suspicious, require 2+ factors
  • Critical (75-100) - Very suspicious, require recent step-up
Configuration
adaptive_mfa:
  enabled: true
  risk_threshold: 50.0
  require_step_up_threshold: 75.0
  
  # Risk factor weights
  location_change_risk: 30.0
  new_device_risk: 40.0
  velocity_risk: 50.0

Rate Limiting

Protects against brute force attacks:

rate_limit:
  enabled: true
  max_attempts: 5        # Max failed attempts
  window_minutes: 15     # Within this window
  lockout_minutes: 30    # Lockout duration
Lockout Behavior
  • After 5 failed attempts in 15 minutes → 30-minute lockout
  • Exponential backoff between attempts
  • Per-user and per-factor-type limits
  • Automatic cleanup of old attempts

Trusted Devices

Allow users to skip MFA on recognized devices:

trusted_devices:
  enabled: true
  default_expiry_days: 30    # Trust duration
  max_expiry_days: 90        # Maximum trust period
  max_devices_per_user: 5    # Limit trusted devices
Device Fingerprinting
// Generate device fingerprint
const deviceFingerprint = await generateFingerprint();

// Trust device during verification
await fetch('/auth/mfa/verify', {
    method: 'POST',
    body: JSON.stringify({
        // ... other fields
        remember_device: true,
        device_info: {
            device_id: deviceFingerprint,
            name: 'MacBook Pro',
            metadata: {
                browser: 'Chrome 120',
                os: 'macOS 14',
                screen: '1920x1080'
            }
        }
    })
});

Migration from 2FA

The MFA plugin includes backward-compatible routes for the old twofa plugin:

Old (2FA) New (MFA) Status
POST /auth/2fa/enable POST /auth/mfa/factors/enroll Compatible
POST /auth/2fa/verify POST /auth/mfa/verify Compatible
POST /auth/2fa/status GET /auth/mfa/status Compatible
POST /auth/2fa/disable DELETE /auth/mfa/factors/:id Deprecated

See MIGRATION_GUIDE.md for detailed migration instructions.

Architecture

The MFA plugin acts as an orchestration layer:

MFA Plugin (Orchestrator)
├── Factor Registry & Adapters
├── Policy Engine
├── Risk Assessment
├── Challenge Orchestration
├── Rate Limiting
└── Integrates with:
    ├── twofa (TOTP, backup codes)
    ├── emailotp (email verification)
    ├── phone (SMS verification)
    └── passkey (WebAuthn - when ready)

Key Design Decisions:

  • No Code Duplication - Delegates to existing plugins
  • Adapter Pattern - Clean integration with factor providers
  • Policy-Driven - Flexible enforcement rules
  • Risk-Aware - Adaptive security based on context

See ARCHITECTURE.md for detailed architecture documentation.

Security Considerations

Best Practices
  1. Always encrypt factor secrets at rest
  2. Hash challenge codes before storage
  3. Use HTTPS for all MFA operations
  4. Rate limit all verification endpoints
  5. Audit all MFA events
  6. Rotate backup codes after use
  7. Expire MFA sessions appropriately
Security Features
  • ✅ Encrypted secret storage
  • ✅ Hashed verification codes
  • ✅ Rate limiting with lockout
  • ✅ Brute force protection
  • ✅ Audit trail for compliance
  • ✅ Session expiry
  • ✅ Device fingerprinting

Performance

Optimization Strategies
  • Caching - Cache user factors (5 min TTL)
  • Indexing - Database indexes on user_id, status
  • Async - SMS/email sending is async
  • Cleanup - Background job for expired records
Expected Performance
  • Factor enrollment: < 200ms
  • Challenge initiation: < 100ms
  • Verification: < 150ms
  • Risk assessment: < 100ms

Testing

Unit Tests
go test ./plugins/mfa/...
Integration Tests
go test ./plugins/mfa/... -tags=integration
Manual Testing
# Start example app
go run examples/mfa/main.go

# Test enrollment
curl -X POST http://localhost:8080/auth/mfa/factors/enroll \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"type":"totp","name":"My App"}'

# Test verification
curl -X POST http://localhost:8080/auth/mfa/verify \
  -d '{"challenge_id":"...","factor_id":"...","code":"123456"}'

Troubleshooting

Common Issues

Issue: "Factor type not supported"
Solution: Ensure the dependent plugin (twofa, emailotp, phone) is loaded and configured

Issue: "Rate limit exceeded"
Solution: Wait for lockout period to expire or reduce max_attempts in config

Issue: "MFA session expired"
Solution: Increase session_expiry_minutes in config or re-authenticate

Issue: "Invalid code"
Solution: Check time synchronization for TOTP, verify code delivery for SMS/email

Dependencies

Required Plugins
  • twofa - Provides TOTP and backup codes
  • emailotp - Provides email verification (optional)
  • phone - Provides SMS verification (optional)
Optional Plugins
  • passkey - Provides WebAuthn (experimental, not production-ready)
Core Services
  • Database (via bun ORM)
  • Configuration system
  • Audit logging
  • Rate limiting

Roadmap

Completed ✅
  • Core factor management
  • TOTP, Email, SMS, Backup code adapters
  • Risk-based authentication
  • Rate limiting
  • Trusted devices
  • Step-up authentication
  • Middleware support
  • Legacy 2FA compatibility
In Progress 🚧
  • WebAuthn adapter (waiting for passkey plugin stability)
  • Organization-level policies
  • Admin UI integration
  • Comprehensive test coverage
Future 📋
  • Push notification factor
  • Security question factor
  • Biometric factor (via WebAuthn)
  • MFA analytics dashboard
  • Compliance reporting
  • Multi-device sync

Contributing

Contributions welcome! Please:

  1. Review ARCHITECTURE.md
  2. Write tests for new features
  3. Update documentation
  4. Follow Go coding standards

License

Same as main AuthSome project.

Support

  • Documentation: See main AuthSome docs
  • GitHub Issues: Report bugs or request features
  • Security: Email security@example.com for security issues

Version: 1.0.0
Status: Production Ready
Last Updated: October 30, 2025

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AdaptiveMFA

func AdaptiveMFA(service *Service) func(func(forge.Context) error) func(forge.Context) error

AdaptiveMFA applies risk-based MFA requirements

func CalculateDeviceFingerprint

func CalculateDeviceFingerprint(userAgent, ipAddress string, additionalData map[string]string) string

CalculateDeviceFingerprint generates a device fingerprint from user agent and other data

func OptionalMFA

func OptionalMFA(service *Service) func(func(forge.Context) error) func(forge.Context) error

OptionalMFA suggests MFA but doesn't require it

func RegisterRoutes

func RegisterRoutes(router forge.Router, handler *Handler)

RegisterRoutes registers all MFA routes with OpenAPI documentation

func RequireFactorType

func RequireFactorType(service *Service, factorType FactorType) func(func(forge.Context) error) func(forge.Context) error

RequireFactorType ensures the user has a specific factor type enrolled

func RequireMFA

func RequireMFA(service *Service) func(func(forge.Context) error) func(forge.Context) error

RequireMFA ensures the user has completed MFA verification

func StepUpAuth

func StepUpAuth(service *Service, maxAge time.Duration) func(func(forge.Context) error) func(forge.Context) error

StepUpAuth requires recent MFA verification for sensitive operations

Types

type AdaptiveMFAConfig

type AdaptiveMFAConfig struct {
	Enabled                bool    `json:"enabled" default:"false"`
	RiskThreshold          float64 `json:"risk_threshold" default:"50.0"` // 0-100
	FactorLocationChange   bool    `json:"factor_location_change" default:"true"`
	FactorNewDevice        bool    `json:"factor_new_device" default:"true"`
	FactorVelocity         bool    `json:"factor_velocity" default:"true"`
	FactorIPReputation     bool    `json:"factor_ip_reputation" default:"false"`
	RequireStepUpThreshold float64 `json:"require_step_up_threshold" default:"75.0"`
	LocationChangeRisk     float64 `json:"location_change_risk" default:"30.0"`
	NewDeviceRisk          float64 `json:"new_device_risk" default:"40.0"`
	VelocityRisk           float64 `json:"velocity_risk" default:"50.0"`
}

AdaptiveMFAConfig configures risk-based authentication

type AdminBypassRequest

type AdminBypassRequest struct {
	UserID   xid.ID `json:"userId"`
	Duration int    `json:"duration"` // Bypass duration in seconds
	Reason   string `json:"reason"`   // Reason for bypass
}

AdminBypassRequest represents a request to grant temporary MFA bypass

type AdminPolicyRequest

type AdminPolicyRequest struct {
	RequiredFactors int      `json:"requiredFactors"` // Number of factors required
	AllowedTypes    []string `json:"allowedTypes"`    // e.g., ["totp", "sms", "email", "webauthn", "backup"]
	GracePeriod     int      `json:"gracePeriod"`     // Grace period in seconds for new users
	Enabled         bool     `json:"enabled"`         // Enable/disable MFA requirement
}

AdminPolicyRequest represents a request to update MFA policy

type BackupCodeFactorAdapter

type BackupCodeFactorAdapter struct {
	BaseFactorAdapter
	// contains filtered or unexported fields
}

BackupCodeFactorAdapter integrates twofa plugin's backup codes as an MFA factor

func NewBackupCodeFactorAdapter

func NewBackupCodeFactorAdapter(twofaService *twofa.Service, enabled bool) *BackupCodeFactorAdapter

NewBackupCodeFactorAdapter creates a new backup code factor adapter

func (*BackupCodeFactorAdapter) Challenge

func (a *BackupCodeFactorAdapter) Challenge(ctx context.Context, factor *Factor, metadata map[string]any) (*Challenge, error)

Challenge creates a backup code verification challenge

func (*BackupCodeFactorAdapter) Enroll

func (a *BackupCodeFactorAdapter) Enroll(ctx context.Context, userID xid.ID, metadata map[string]any) (*FactorEnrollmentResponse, error)

Enroll generates backup codes for a user

func (*BackupCodeFactorAdapter) Verify

func (a *BackupCodeFactorAdapter) Verify(ctx context.Context, challenge *Challenge, response string, data map[string]any) (bool, error)

Verify verifies a backup code

func (*BackupCodeFactorAdapter) VerifyEnrollment

func (a *BackupCodeFactorAdapter) VerifyEnrollment(ctx context.Context, enrollmentID xid.ID, proof string) error

VerifyEnrollment is not needed for backup codes (immediately active)

type BackupCodesConfig

type BackupCodesConfig struct {
	Enabled    bool   `json:"enabled" default:"true"`
	Count      int    `json:"count" default:"10"`
	Length     int    `json:"length" default:"8"`
	Format     string `json:"format" default:"XXXX-XXXX"` // Code format
	AllowReuse bool   `json:"allow_reuse" default:"false"`
}

BackupCodesConfig configures backup recovery codes

type BaseFactorAdapter

type BaseFactorAdapter struct {
	// contains filtered or unexported fields
}

BaseFactorAdapter provides common functionality for adapters

func (*BaseFactorAdapter) IsAvailable

func (b *BaseFactorAdapter) IsAvailable() bool

IsAvailable checks if the factor is available

func (*BaseFactorAdapter) Type

func (b *BaseFactorAdapter) Type() FactorType

Type returns the factor type

type Challenge

type Challenge struct {
	ID          xid.ID          `json:"id"`
	UserID      xid.ID          `json:"userId"`
	FactorID    xid.ID          `json:"factorId"`
	Type        FactorType      `json:"type"`
	Status      ChallengeStatus `json:"status"`
	Code        string          `json:"-"` // Hashed verification code
	Metadata    map[string]any  `json:"metadata"`
	Attempts    int             `json:"attempts"`
	MaxAttempts int             `json:"maxAttempts"`
	IPAddress   string          `json:"ipAddress"`
	UserAgent   string          `json:"userAgent"`
	CreatedAt   time.Time       `json:"createdAt"`
	ExpiresAt   time.Time       `json:"expiresAt"`
	VerifiedAt  *time.Time      `json:"verifiedAt,omitempty"`
}

Challenge represents an active MFA challenge

type ChallengeRequest

type ChallengeRequest struct {
	UserID      xid.ID         `json:"userId"`
	FactorTypes []FactorType   `json:"factorTypes,omitempty"` // Specific factor types to use
	Context     string         `json:"context,omitempty"`     // "login", "transaction", "step-up"
	Metadata    map[string]any `json:"metadata,omitempty"`
}

ChallengeRequest initiates an MFA challenge

type ChallengeResponse

type ChallengeResponse struct {
	ChallengeID      xid.ID       `json:"challengeId"`
	SessionID        xid.ID       `json:"sessionId"`
	FactorsRequired  int          `json:"factorsRequired"`
	AvailableFactors []FactorInfo `json:"availableFactors"`
	ExpiresAt        time.Time    `json:"expiresAt"`
}

ChallengeResponse contains challenge details

type ChallengeStatus

type ChallengeStatus string

ChallengeStatus represents the state of an MFA challenge

const (
	ChallengeStatusPending   ChallengeStatus = "pending"
	ChallengeStatusVerified  ChallengeStatus = "verified"
	ChallengeStatusFailed    ChallengeStatus = "failed"
	ChallengeStatusExpired   ChallengeStatus = "expired"
	ChallengeStatusCancelled ChallengeStatus = "cancelled"
)

type ChallengeStatusResponse

type ChallengeStatusResponse struct {
	SessionID        xid.ID     `json:"sessionId"`
	Status           string     `json:"status"` // pending, completed, expired
	FactorsRequired  int        `json:"factorsRequired"`
	FactorsVerified  int        `json:"factorsVerified"`
	FactorsRemaining int        `json:"factorsRemaining"`
	ExpiresAt        time.Time  `json:"expiresAt"`
	CompletedAt      *time.Time `json:"completedAt,omitempty"`
}

ChallengeStatusResponse contains the current status of an MFA challenge

type Config

type Config struct {
	// Global settings
	Enabled            bool `json:"enabled" default:"true"`
	RequireForAllUsers bool `json:"require_for_all_users" default:"false"`
	GracePeriodDays    int  `json:"grace_period_days" default:"7"`

	// Factor settings
	AllowedFactorTypes  []FactorType `json:"allowed_factor_types"`
	RequiredFactorCount int          `json:"required_factor_count" default:"1"`

	// TOTP settings
	TOTP TOTPConfig `json:"totp"`

	// SMS settings
	SMS SMSConfig `json:"sms"`

	// Email settings
	Email EmailConfig `json:"email"`

	// WebAuthn settings
	WebAuthn WebAuthnConfig `json:"webauthn"`

	// Backup codes settings
	BackupCodes BackupCodesConfig `json:"backup_codes"`

	// Trusted device settings
	TrustedDevices TrustedDevicesConfig `json:"trusted_devices"`

	// Challenge settings
	ChallengeExpiryMinutes int `json:"challenge_expiry_minutes" default:"5"`
	MaxAttempts            int `json:"max_attempts" default:"3"`

	// Rate limiting
	RateLimit RateLimitConfig `json:"rate_limit"`

	// Adaptive MFA
	AdaptiveMFA AdaptiveMFAConfig `json:"adaptive_mfa"`

	// Session settings
	SessionExpiryMinutes int `json:"session_expiry_minutes" default:"15"`
}

Config holds MFA plugin configuration

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig returns default MFA configuration

func (*Config) GetFactorConfig

func (c *Config) GetFactorConfig(factorType FactorType) interface{}

GetFactorConfig returns configuration for a specific factor type

func (*Config) IsFactorAllowed

func (c *Config) IsFactorAllowed(factorType FactorType) bool

IsFactorAllowed checks if a factor type is allowed

func (*Config) Validate

func (c *Config) Validate() error

Validate validates the configuration

type DeleteFactorRequest

type DeleteFactorRequest struct {
	// Path parameters
	ID string `path:"id" validate:"required" description:"Factor ID to delete"`
}

DeleteFactorRequest represents the request to delete a factor

type DeviceInfo

type DeviceInfo struct {
	DeviceID string         `json:"deviceId"`
	Name     string         `json:"name,omitempty"`
	Metadata map[string]any `json:"metadata,omitempty"`
}

DeviceInfo contains device identification data

type DevicesResponse

type DevicesResponse struct {
	Devices interface{} `json:"devices"`
	Count   int         `json:"count"`
}

type EmailConfig

type EmailConfig struct {
	Enabled           bool             `json:"enabled" default:"true"`
	Provider          string           `json:"provider"` // Email provider
	CodeLength        int              `json:"code_length" default:"6"`
	CodeExpiryMinutes int              `json:"code_expiry_minutes" default:"10"`
	TemplateID        string           `json:"template_id"`
	RateLimit         *RateLimitConfig `json:"rate_limit,omitempty"`
}

EmailConfig configures email verification settings

type EmailFactorAdapter

type EmailFactorAdapter struct {
	BaseFactorAdapter
	// contains filtered or unexported fields
}

EmailFactorAdapter integrates emailotp plugin as an MFA factor (not primary auth)

func NewEmailFactorAdapter

func NewEmailFactorAdapter(emailOTPService *emailotp.Service, notifAdapter *notificationPlugin.Adapter, enabled bool) *EmailFactorAdapter

NewEmailFactorAdapter creates a new email factor adapter

func (*EmailFactorAdapter) Challenge

func (a *EmailFactorAdapter) Challenge(ctx context.Context, factor *Factor, metadata map[string]any) (*Challenge, error)

Challenge sends an email OTP code for MFA verification

func (*EmailFactorAdapter) Enroll

func (a *EmailFactorAdapter) Enroll(ctx context.Context, userID xid.ID, metadata map[string]any) (*FactorEnrollmentResponse, error)

Enroll registers an email address for MFA

func (*EmailFactorAdapter) Verify

func (a *EmailFactorAdapter) Verify(ctx context.Context, challenge *Challenge, response string, data map[string]any) (bool, error)

Verify verifies an email OTP code

func (*EmailFactorAdapter) VerifyEnrollment

func (a *EmailFactorAdapter) VerifyEnrollment(ctx context.Context, enrollmentID xid.ID, proof string) error

VerifyEnrollment sends a test code to verify email works

type EnrollFactorRequest

type EnrollFactorRequest struct {
	// Body fields
	Type     FactorType     `` /* 146-byte string literal not displayed */
	Priority FactorPriority `json:"priority,omitempty" validate:"omitempty,oneof=primary backup optional" description:"Priority level of the factor"`
	Name     string         `json:"name,omitempty" validate:"omitempty,min=1,max=100" description:"User-friendly name for the factor"`
	Metadata map[string]any `json:"metadata,omitempty" description:"Additional factor-specific metadata"`
}

EnrollFactorRequest represents the request to enroll a new MFA factor

type ErrorResponse

type ErrorResponse struct {
	Error   string         `json:"error" description:"Error message"`
	Code    string         `json:"code,omitempty" description:"Error code for programmatic handling"`
	Details map[string]any `json:"details,omitempty" description:"Additional error details"`
}

ErrorResponse represents a standard error response

type Factor

type Factor struct {
	ID         xid.ID         `json:"id"`
	UserID     xid.ID         `json:"userId"`
	Type       FactorType     `json:"type"`
	Status     FactorStatus   `json:"status"`
	Priority   FactorPriority `json:"priority"`
	Name       string         `json:"name"`     // User-friendly name
	Secret     string         `json:"-"`        // Encrypted secret data
	Metadata   map[string]any `json:"metadata"` // Factor-specific metadata
	LastUsedAt *time.Time     `json:"lastUsedAt"`
	VerifiedAt *time.Time     `json:"verifiedAt"`
	CreatedAt  time.Time      `json:"createdAt"`
	UpdatedAt  time.Time      `json:"updatedAt"`
	ExpiresAt  *time.Time     `json:"expiresAt,omitempty"`
}

Factor represents an enrolled authentication factor

type FactorAdapter

type FactorAdapter interface {
	// Type returns the factor type this adapter handles
	Type() FactorType

	// Enroll initiates factor enrollment for a user
	// Returns provisioning data needed to complete enrollment
	Enroll(ctx context.Context, userID xid.ID, metadata map[string]any) (*FactorEnrollmentResponse, error)

	// VerifyEnrollment verifies the enrollment (e.g., user scanned QR code and provides first TOTP)
	VerifyEnrollment(ctx context.Context, enrollmentID xid.ID, proof string) error

	// Challenge initiates a verification challenge (sends code, displays options, etc.)
	Challenge(ctx context.Context, factor *Factor, metadata map[string]any) (*Challenge, error)

	// Verify verifies the challenge response
	Verify(ctx context.Context, challenge *Challenge, response string, data map[string]any) (bool, error)

	// IsAvailable checks if this factor type is available/configured
	IsAvailable() bool
}

FactorAdapter defines the interface for integrating authentication factors Each adapter wraps an existing plugin (twofa, emailotp, phone, passkey)

type FactorAdapterRegistry

type FactorAdapterRegistry struct {
	// contains filtered or unexported fields
}

FactorAdapterRegistry manages available factor adapters

func NewFactorAdapterRegistry

func NewFactorAdapterRegistry() *FactorAdapterRegistry

NewFactorAdapterRegistry creates a new adapter registry

func (*FactorAdapterRegistry) Get

func (r *FactorAdapterRegistry) Get(factorType FactorType) (FactorAdapter, error)

Get retrieves a factor adapter by type

func (*FactorAdapterRegistry) GetAvailable

func (r *FactorAdapterRegistry) GetAvailable() []FactorType

GetAvailable returns only available factor types

func (*FactorAdapterRegistry) List

func (r *FactorAdapterRegistry) List() []FactorType

List returns all available factor types

func (*FactorAdapterRegistry) Register

func (r *FactorAdapterRegistry) Register(adapter FactorAdapter)

Register registers a factor adapter

type FactorEnrollmentRequest

type FactorEnrollmentRequest struct {
	Type     FactorType     `json:"type"`
	Priority FactorPriority `json:"priority,omitempty"`
	Name     string         `json:"name,omitempty"`
	Metadata map[string]any `json:"metadata,omitempty"`
}

FactorEnrollmentRequest represents a request to enroll a new factor

type FactorEnrollmentResponse

type FactorEnrollmentResponse struct {
	FactorID         xid.ID         `json:"factorId"`
	Type             FactorType     `json:"type"`
	Status           FactorStatus   `json:"status"`
	ProvisioningData map[string]any `json:"provisioningData"` // Type-specific setup data

}

FactorEnrollmentResponse contains data needed to complete enrollment

type FactorInfo

type FactorInfo struct {
	FactorID xid.ID         `json:"factorId"`
	Type     FactorType     `json:"type"`
	Name     string         `json:"name"`
	Metadata map[string]any `json:"metadata,omitempty"` // Masked phone, email, etc.
}

FactorInfo provides minimal factor information for challenge selection

type FactorPriority

type FactorPriority string

FactorPriority defines the priority of a factor

const (
	FactorPriorityPrimary  FactorPriority = "primary"  // Primary authentication factor
	FactorPriorityBackup   FactorPriority = "backup"   // Backup/fallback factor
	FactorPriorityOptional FactorPriority = "optional" // Optional additional security
)

type FactorStatus

type FactorStatus string

FactorStatus represents the state of an authentication factor

const (
	FactorStatusPending  FactorStatus = "pending"  // Enrolled but not verified
	FactorStatusActive   FactorStatus = "active"   // Verified and active
	FactorStatusDisabled FactorStatus = "disabled" // Temporarily disabled
	FactorStatusRevoked  FactorStatus = "revoked"  // Permanently revoked
)

type FactorType

type FactorType string

FactorType represents different authentication factor types

const (
	FactorTypeTOTP      FactorType = "totp"      // Time-based One-Time Password (Google Authenticator)
	FactorTypeSMS       FactorType = "sms"       // SMS verification code
	FactorTypeEmail     FactorType = "email"     // Email verification code
	FactorTypeWebAuthn  FactorType = "webauthn"  // FIDO2/WebAuthn (security keys, biometrics)
	FactorTypePush      FactorType = "push"      // Push notification approval
	FactorTypeBackup    FactorType = "backup"    // Backup recovery codes
	FactorTypeQuestion  FactorType = "question"  // Security questions
	FactorTypeBiometric FactorType = "biometric" // Biometric authentication
)

type FactorVerificationRequest

type FactorVerificationRequest struct {
	FactorID xid.ID         `json:"factorId"`
	Code     string         `json:"code,omitempty"` // For OTP-based factors
	Data     map[string]any `json:"data,omitempty"` // For complex factors (WebAuthn, etc.)
}

FactorVerificationRequest verifies an enrolled factor

type FactorsResponse

type FactorsResponse struct {
	Factors interface{} `json:"factors"`
	Count   int         `json:"count"`
}

type GetChallengeStatusRequest

type GetChallengeStatusRequest struct {
	// Path parameters
	ID string `path:"id" validate:"required" description:"Challenge ID"`
}

GetChallengeStatusRequest represents the request to get challenge status

type GetChallengeStatusResponse

type GetChallengeStatusResponse struct {
	ChallengeID      xid.ID          `json:"challengeId" description:"Unique challenge identifier"`
	Status           ChallengeStatus `json:"status" description:"Current status of the challenge"`
	FactorsRequired  int             `json:"factorsRequired" description:"Number of factors required"`
	FactorsVerified  int             `json:"factorsVerified" description:"Number of factors verified"`
	Attempts         int             `json:"attempts" description:"Number of verification attempts"`
	MaxAttempts      int             `json:"maxAttempts" description:"Maximum allowed attempts"`
	AvailableFactors []FactorInfo    `json:"availableFactors" description:"Available factors for this challenge"`
}

GetChallengeStatusResponse represents the challenge status response

type GetFactorRequest

type GetFactorRequest struct {
	// Path parameters
	ID string `path:"id" validate:"required" description:"Factor ID"`
}

GetFactorRequest represents the request to get a specific factor

type GetStatusRequest

type GetStatusRequest struct {
	// Query parameters
	DeviceID string `query:"deviceId" description:"Device ID to check trust status"`
}

GetStatusRequest represents the request to get MFA status

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

Handler provides HTTP endpoints for MFA operations

func NewHandler

func NewHandler(service *Service) *Handler

NewHandler creates a new MFA handler

func (*Handler) AdminGetPolicy

func (h *Handler) AdminGetPolicy(c forge.Context) error

AdminGetPolicy handles GET /mfa/admin/policy Gets the current MFA policy for an app

func (*Handler) AdminGrantBypass

func (h *Handler) AdminGrantBypass(c forge.Context) error

AdminGrantBypass handles POST /mfa/admin/bypass Grants temporary MFA bypass for a user (admin only)

func (*Handler) AdminResetUserMFA

func (h *Handler) AdminResetUserMFA(c forge.Context) error

AdminResetUserMFA handles POST /mfa/admin/users/:id/reset Resets all MFA factors for a user (admin only)

func (*Handler) AdminUpdatePolicy

func (h *Handler) AdminUpdatePolicy(c forge.Context) error

AdminUpdatePolicy handles PUT /mfa/admin/policy Updates the MFA policy for an app (admin only)

func (*Handler) DeleteFactor

func (h *Handler) DeleteFactor(c forge.Context) error

DeleteFactor handles DELETE /mfa/factors/:id

func (*Handler) EnrollFactor

func (h *Handler) EnrollFactor(c forge.Context) error

EnrollFactor handles POST /mfa/factors/enroll

func (*Handler) GetChallengeStatus

func (h *Handler) GetChallengeStatus(c forge.Context) error

GetChallengeStatus handles GET /mfa/challenge/:id

func (*Handler) GetFactor

func (h *Handler) GetFactor(c forge.Context) error

GetFactor handles GET /mfa/factors/:id

func (*Handler) GetPolicy

func (h *Handler) GetPolicy(c forge.Context) error

GetPolicy handles GET /mfa/policy

func (*Handler) GetStatus

func (h *Handler) GetStatus(c forge.Context) error

GetStatus handles GET /mfa/status

func (*Handler) InitiateChallenge

func (h *Handler) InitiateChallenge(c forge.Context) error

InitiateChallenge handles POST /mfa/challenge

func (*Handler) ListFactors

func (h *Handler) ListFactors(c forge.Context) error

ListFactors handles GET /mfa/factors

func (*Handler) ListTrustedDevices

func (h *Handler) ListTrustedDevices(c forge.Context) error

ListTrustedDevices handles GET /mfa/devices

func (*Handler) RevokeTrustedDevice

func (h *Handler) RevokeTrustedDevice(c forge.Context) error

RevokeTrustedDevice handles DELETE /mfa/devices/:id

func (*Handler) TrustDevice

func (h *Handler) TrustDevice(c forge.Context) error

TrustDevice handles POST /mfa/devices/trust

func (*Handler) UpdateFactor

func (h *Handler) UpdateFactor(c forge.Context) error

UpdateFactor handles PUT /mfa/factors/:id

func (*Handler) VerifyChallenge

func (h *Handler) VerifyChallenge(c forge.Context) error

VerifyChallenge handles POST /mfa/verify

func (*Handler) VerifyFactor

func (h *Handler) VerifyFactor(c forge.Context) error

VerifyFactor handles POST /mfa/factors/:id/verify

type InitiateChallengeRequest

type InitiateChallengeRequest struct {
	// Body fields
	FactorTypes []FactorType   `json:"factorTypes,omitempty" description:"Specific factor types to use for this challenge"`
	Context     string         `` /* 144-byte string literal not displayed */
	Metadata    map[string]any `json:"metadata,omitempty" description:"Additional context metadata"`
}

InitiateChallengeRequest represents the request to start an MFA challenge

type LimitResult

type LimitResult struct {
	Allowed      bool
	RetryAfter   *time.Duration
	AttemptsLeft int
	LockoutEnds  *time.Time
}

LimitResult represents the result of a rate limit check

type ListFactorsRequest

type ListFactorsRequest struct {
	// Query parameters
	ActiveOnly bool `query:"activeOnly" description:"Return only active factors"`
}

ListFactorsRequest represents the request to list factors

type ListFactorsResponse

type ListFactorsResponse struct {
	Factors []Factor `json:"factors" description:"List of enrolled factors"`
	Count   int      `json:"count" description:"Total number of factors"`
}

ListFactorsResponse represents the response containing factors list

type ListTrustedDevicesResponse

type ListTrustedDevicesResponse struct {
	Devices []TrustedDevice `json:"devices" description:"List of trusted devices"`
	Count   int             `json:"count" description:"Total number of trusted devices"`
}

ListTrustedDevicesResponse represents the response containing trusted devices

type MFABypassResponse

type MFABypassResponse struct {
	ID        xid.ID    `json:"id"`
	UserID    xid.ID    `json:"userId"`
	ExpiresAt time.Time `json:"expiresAt"`
	Reason    string    `json:"reason"`
}

MFABypassResponse contains MFA bypass details

type MFAConfigResponse

type MFAConfigResponse struct {
	Enabled             bool     `json:"enabled"`
	RequiredFactorCount int      `json:"required_factor_count"`
	AllowedFactorTypes  []string `json:"allowed_factor_types"`
}

type MFAPolicy

type MFAPolicy struct {
	ID                     xid.ID       `json:"id"`
	OrganizationID         xid.ID       `json:"organizationId"`
	RequiredFactorCount    int          `json:"requiredFactorCount"` // Number of factors required
	AllowedFactorTypes     []FactorType `json:"allowedFactorTypes"`  // Permitted factor types
	RequiredFactorTypes    []FactorType `json:"requiredFactorTypes"` // Mandatory factor types
	GracePeriodDays        int          `json:"gracePeriodDays"`     // Days before MFA is enforced
	TrustedDeviceDays      int          `json:"trustedDeviceDays"`   // Days device is trusted
	StepUpRequired         bool         `json:"stepUpRequired"`      // Require step-up for sensitive ops
	AdaptiveMFAEnabled     bool         `json:"adaptiveMfaEnabled"`  // Enable risk-based MFA
	MaxFailedAttempts      int          `json:"maxFailedAttempts"`
	LockoutDurationMinutes int          `json:"lockoutDurationMinutes"`
	CreatedAt              time.Time    `json:"createdAt"`
	UpdatedAt              time.Time    `json:"updatedAt"`
}

MFAPolicy defines organization-level MFA requirements

type MFAPolicyResponse

type MFAPolicyResponse struct {
	ID                  xid.ID   `json:"id"`
	AppID               xid.ID   `json:"appId"`
	OrganizationID      *xid.ID  `json:"organizationId,omitempty"`
	Enabled             bool     `json:"enabled"`
	RequiredFactorCount int      `json:"requiredFactorCount"`
	AllowedFactorTypes  []string `json:"allowedFactorTypes"`
	GracePeriodDays     int      `json:"gracePeriodDays"`
}

MFAPolicyResponse contains MFA policy details

type MFASession

type MFASession struct {
	ID              xid.ID         `json:"id"`
	UserID          xid.ID         `json:"userId"`
	SessionToken    string         `json:"sessionToken"`
	FactorsRequired int            `json:"factorsRequired"`
	FactorsVerified int            `json:"factorsVerified"`
	VerifiedFactors []xid.ID       `json:"verifiedFactors"`
	RiskLevel       RiskLevel      `json:"riskLevel"`
	IPAddress       string         `json:"ipAddress"`
	UserAgent       string         `json:"userAgent"`
	Metadata        map[string]any `json:"metadata"`
	CreatedAt       time.Time      `json:"createdAt"`
	ExpiresAt       time.Time      `json:"expiresAt"`
	CompletedAt     *time.Time     `json:"completedAt,omitempty"`
}

MFASession represents an MFA verification session

type MFAStatus

type MFAStatus struct {
	Enabled         bool         `json:"enabled"`
	EnrolledFactors []FactorInfo `json:"enrolledFactors"`
	RequiredCount   int          `json:"requiredCount"`
	PolicyActive    bool         `json:"policyActive"`
	GracePeriod     *time.Time   `json:"gracePeriod,omitempty"`
	TrustedDevice   bool         `json:"trustedDevice"`
}

MFAStatus represents overall MFA status for a user

type MessageResponse

type MessageResponse = responses.MessageResponse

Response types - use shared responses from core

type Plugin

type Plugin struct {
	// contains filtered or unexported fields
}

Plugin implements the plugins.Plugin interface for Multi-Factor Authentication

func NewPlugin

func NewPlugin(opts ...PluginOption) *Plugin

NewPlugin creates a new MFA plugin with optional configuration

func (*Plugin) Config

func (p *Plugin) Config() *Config

Config returns the plugin configuration

func (*Plugin) ID

func (p *Plugin) ID() string

ID returns the plugin identifier

func (*Plugin) Init

func (p *Plugin) Init(authInstance core.Authsome) error

Init initializes the plugin with dependencies

func (*Plugin) Migrate

func (p *Plugin) Migrate() error

Migrate creates required database tables

func (*Plugin) RegisterHooks

func (p *Plugin) RegisterHooks(_ *hooks.HookRegistry) error

RegisterHooks registers MFA-related hooks

func (*Plugin) RegisterRoutes

func (p *Plugin) RegisterRoutes(router forge.Router) error

RegisterRoutes registers MFA endpoints

func (*Plugin) RegisterServiceDecorators

func (p *Plugin) RegisterServiceDecorators(_ *registry.ServiceRegistry) error

RegisterServiceDecorators allows MFA to enhance core services

func (*Plugin) Service

func (p *Plugin) Service() *Service

Service returns the MFA service (for use by middleware and other components)

func (*Plugin) WithConfig

func (p *Plugin) WithConfig(config *Config) *Plugin

WithConfig sets custom configuration

type PluginOption

type PluginOption func(*Plugin)

PluginOption is a functional option for configuring the MFA plugin

func WithAdaptiveMFA

func WithAdaptiveMFA(enabled bool, threshold float64) PluginOption

WithAdaptiveMFA sets the adaptive MFA configuration

func WithBackupCodes

func WithBackupCodes(enabled bool, count, length int) PluginOption

WithBackupCodes sets the backup codes configuration

func WithDefaultConfig

func WithDefaultConfig(cfg *Config) PluginOption

WithDefaultConfig sets the default configuration for the plugin

func WithEmail

func WithEmail(enabled bool, codeLength, expiryMinutes int) PluginOption

WithEmail sets the email configuration

func WithEnabled

func WithEnabled(enabled bool) PluginOption

WithEnabled sets whether MFA is enabled

func WithGracePeriodDays

func WithGracePeriodDays(days int) PluginOption

WithGracePeriodDays sets the grace period in days

func WithRequireForAllUsers

func WithRequireForAllUsers(required bool) PluginOption

WithRequireForAllUsers sets whether MFA is required for all users

func WithSMS

func WithSMS(enabled bool, codeLength, expiryMinutes int) PluginOption

WithSMS sets the SMS configuration

func WithTOTP

func WithTOTP(enabled bool, issuer string) PluginOption

WithTOTP sets the TOTP configuration

type RateLimitConfig

type RateLimitConfig struct {
	Enabled        bool `json:"enabled" default:"true"`
	MaxAttempts    int  `json:"max_attempts" default:"5"`
	WindowMinutes  int  `json:"window_minutes" default:"15"`
	LockoutMinutes int  `json:"lockout_minutes" default:"30"`
}

RateLimitConfig configures rate limiting

type RateLimiter

type RateLimiter struct {
	// contains filtered or unexported fields
}

RateLimiter provides rate limiting for MFA operations

func NewRateLimiter

func NewRateLimiter(config *RateLimitConfig, repo *repository.MFARepository) *RateLimiter

NewRateLimiter creates a new rate limiter

func (*RateLimiter) CheckFactorLimit

func (r *RateLimiter) CheckFactorLimit(ctx context.Context, userID xid.ID, factorType FactorType) (*LimitResult, error)

CheckFactorLimit checks if a specific factor has exceeded rate limits

func (*RateLimiter) CheckUserLimit

func (r *RateLimiter) CheckUserLimit(ctx context.Context, userID xid.ID) (*LimitResult, error)

CheckUserLimit checks if a user has exceeded rate limits

func (*RateLimiter) ClearLockout

func (r *RateLimiter) ClearLockout(ctx context.Context, userID xid.ID) error

ClearLockout clears the lockout for a user (admin function)

func (*RateLimiter) GetExponentialBackoff

func (r *RateLimiter) GetExponentialBackoff(attemptNumber int) time.Duration

GetExponentialBackoff calculates exponential backoff duration

func (*RateLimiter) IsLockedOut

func (r *RateLimiter) IsLockedOut(ctx context.Context, userID xid.ID) (bool, *time.Time, error)

IsLockedOut checks if a user is currently locked out

func (*RateLimiter) RecordAttempt

func (r *RateLimiter) RecordAttempt(ctx context.Context, userID xid.ID, factorID *xid.ID, factorType FactorType, success bool, metadata map[string]string) error

RecordAttempt records a verification attempt

type ResetUserMFARequest

type ResetUserMFARequest struct {
	// Path parameters
	ID string `path:"id" validate:"required" description:"User ID whose MFA should be reset"`

	// Body fields
	Reason string `json:"reason,omitempty" validate:"omitempty,min=1,max=500" description:"Reason for MFA reset (for audit trail)"`
}

ResetUserMFARequest represents the request to reset user's MFA (admin only)

type ResetUserMFAResponse

type ResetUserMFAResponse struct {
	Success        bool   `json:"success" description:"Whether the reset was successful"`
	Message        string `json:"message" description:"Human-readable message"`
	FactorsReset   int    `json:"factorsReset" description:"Number of factors that were reset"`
	DevicesRevoked int    `json:"devicesRevoked" description:"Number of trusted devices revoked"`
}

ResetUserMFAResponse represents the response after resetting user's MFA

type RevokeTrustedDeviceRequest

type RevokeTrustedDeviceRequest struct {
	// Path parameters
	ID string `path:"id" validate:"required" description:"Trusted device ID to revoke"`
}

RevokeTrustedDeviceRequest represents the request to revoke a trusted device

type RiskAssessment

type RiskAssessment struct {
	Level       RiskLevel      `json:"level"`
	Score       float64        `json:"score"`       // 0-100
	Factors     []string       `json:"factors"`     // Risk factors identified
	Recommended []FactorType   `json:"recommended"` // Recommended factor types
	Metadata    map[string]any `json:"metadata"`
}

RiskAssessment represents authentication risk evaluation

type RiskContext

type RiskContext struct {
	UserID    xid.ID
	IPAddress string
	UserAgent string
	Location  string
	DeviceID  string
	Timestamp time.Time
}

RiskContext contains contextual information for risk assessment

type RiskEngine

type RiskEngine struct {
	// contains filtered or unexported fields
}

RiskEngine assesses authentication risk and recommends factors

func NewRiskEngine

func NewRiskEngine(config *AdaptiveMFAConfig, repo *repository.MFARepository) *RiskEngine

NewRiskEngine creates a new risk assessment engine

func (*RiskEngine) AssessRisk

func (e *RiskEngine) AssessRisk(ctx context.Context, riskCtx *RiskContext) (*RiskAssessment, error)

AssessRisk performs a comprehensive risk assessment

func (*RiskEngine) GetRequiredFactorCount

func (e *RiskEngine) GetRequiredFactorCount(level RiskLevel) int

GetRequiredFactorCount returns the number of factors required based on risk

func (*RiskEngine) RequiresStepUp

func (e *RiskEngine) RequiresStepUp(score float64) bool

RequiresStepUp determines if step-up authentication is needed

type RiskFactor

type RiskFactor struct {
	Name        string
	Description string
	Score       float64 // 0-100
	Weight      float64 // 0-1
}

RiskFactor represents an identified risk factor

type RiskLevel

type RiskLevel string

RiskLevel represents authentication risk assessment

const (
	RiskLevelLow      RiskLevel = "low"
	RiskLevelMedium   RiskLevel = "medium"
	RiskLevelHigh     RiskLevel = "high"
	RiskLevelCritical RiskLevel = "critical"
)

type SMSConfig

type SMSConfig struct {
	Enabled           bool             `json:"enabled" default:"true"`
	Provider          string           `json:"provider"` // "twilio", "vonage", etc.
	CodeLength        int              `json:"code_length" default:"6"`
	CodeExpiryMinutes int              `json:"code_expiry_minutes" default:"5"`
	TemplateID        string           `json:"template_id"`
	RateLimit         *RateLimitConfig `json:"rate_limit,omitempty"`
}

SMSConfig configures SMS verification settings

type SMSFactorAdapter

type SMSFactorAdapter struct {
	BaseFactorAdapter
	// contains filtered or unexported fields
}

SMSFactorAdapter integrates phone plugin as an MFA factor (not primary auth)

func NewSMSFactorAdapter

func NewSMSFactorAdapter(phoneService *phone.Service, notifAdapter *notificationPlugin.Adapter, enabled bool) *SMSFactorAdapter

NewSMSFactorAdapter creates a new SMS factor adapter

func (*SMSFactorAdapter) Challenge

func (a *SMSFactorAdapter) Challenge(ctx context.Context, factor *Factor, metadata map[string]any) (*Challenge, error)

Challenge sends an SMS OTP code for MFA verification

func (*SMSFactorAdapter) Enroll

func (a *SMSFactorAdapter) Enroll(ctx context.Context, userID xid.ID, metadata map[string]any) (*FactorEnrollmentResponse, error)

Enroll registers a phone number for MFA

func (*SMSFactorAdapter) Verify

func (a *SMSFactorAdapter) Verify(ctx context.Context, challenge *Challenge, response string, data map[string]any) (bool, error)

Verify verifies an SMS OTP code

func (*SMSFactorAdapter) VerifyEnrollment

func (a *SMSFactorAdapter) VerifyEnrollment(ctx context.Context, enrollmentID xid.ID, proof string) error

VerifyEnrollment sends a test code to verify phone works

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service provides MFA orchestration and management

func NewService

func NewService(
	repo *repository.MFARepository,
	adapterRegistry *FactorAdapterRegistry,
	notifAdapter *notificationPlugin.Adapter,
	config *Config,
) *Service

NewService creates a new MFA service

func (*Service) DeleteFactor

func (s *Service) DeleteFactor(ctx context.Context, factorID xid.ID) error

DeleteFactor removes a factor

func (*Service) EnrollFactor

func (s *Service) EnrollFactor(ctx context.Context, userID xid.ID, req *FactorEnrollmentRequest) (*FactorEnrollmentResponse, error)

EnrollFactor initiates factor enrollment for a user

func (*Service) GetChallengeStatus

func (s *Service) GetChallengeStatus(ctx context.Context, sessionID xid.ID, userID xid.ID) (*ChallengeStatusResponse, error)

GetChallengeStatus retrieves the current status of an MFA challenge/session

func (*Service) GetFactor

func (s *Service) GetFactor(ctx context.Context, factorID xid.ID) (*Factor, error)

GetFactor retrieves a specific factor

func (*Service) GetMFAStatus

func (s *Service) GetMFAStatus(ctx context.Context, userID xid.ID, deviceID string) (*MFAStatus, error)

GetMFAStatus returns the MFA status for a user

func (*Service) GrantBypass

func (s *Service) GrantBypass(ctx context.Context, appID, userID, grantedBy xid.ID, durationSeconds int, reason string) (*MFABypassResponse, error)

GrantBypass grants temporary MFA bypass for a user

func (*Service) InitiateChallenge

func (s *Service) InitiateChallenge(ctx context.Context, req *ChallengeRequest) (*ChallengeResponse, error)

InitiateChallenge starts an MFA verification challenge

func (*Service) IsTrustedDevice

func (s *Service) IsTrustedDevice(ctx context.Context, userID xid.ID, deviceID string) (bool, error)

IsTrustedDevice checks if a device is trusted

func (*Service) ListFactors

func (s *Service) ListFactors(ctx context.Context, userID xid.ID, activeOnly bool) ([]*Factor, error)

ListFactors lists all factors for a user

func (*Service) ListTrustedDevices

func (s *Service) ListTrustedDevices(ctx context.Context, userID xid.ID) ([]*TrustedDevice, error)

ListTrustedDevices lists all trusted devices for a user

func (*Service) ResetUserMFA

func (s *Service) ResetUserMFA(ctx context.Context, appID, userID, adminID xid.ID) error

ResetUserMFA resets all MFA factors and devices for a user

func (*Service) RevokeTrustedDevice

func (s *Service) RevokeTrustedDevice(ctx context.Context, deviceID xid.ID) error

RevokeTrustedDevice removes trust from a device

func (*Service) TrustDevice

func (s *Service) TrustDevice(ctx context.Context, userID xid.ID, deviceInfo *DeviceInfo) error

TrustDevice marks a device as trusted

func (*Service) UpdateFactor

func (s *Service) UpdateFactor(ctx context.Context, factorID xid.ID, updates map[string]interface{}) error

UpdateFactor updates factor settings

func (*Service) UpdatePolicy

func (s *Service) UpdatePolicy(ctx context.Context, appID xid.ID, orgID *xid.ID, updatedBy xid.ID, req *AdminPolicyRequest) (*MFAPolicyResponse, error)

UpdatePolicy updates the MFA policy for an app/organization

func (*Service) VerifyChallenge

func (s *Service) VerifyChallenge(ctx context.Context, req *VerificationRequest) (*VerificationResponse, error)

VerifyChallenge verifies a challenge response

func (*Service) VerifyEnrollment

func (s *Service) VerifyEnrollment(ctx context.Context, factorID xid.ID, proof string) error

VerifyEnrollment completes factor enrollment verification

type SuccessResponse

type SuccessResponse struct {
	Message string         `json:"message" description:"Success message"`
	Data    map[string]any `json:"data,omitempty" description:"Additional response data"`
}

SuccessResponse represents a standard success response

type TOTPConfig

type TOTPConfig struct {
	Enabled    bool   `json:"enabled" default:"true"`
	Issuer     string `json:"issuer" default:"AuthSome"`
	Period     int    `json:"period" default:"30"` // Seconds
	Digits     int    `json:"digits" default:"6"`
	Algorithm  string `json:"algorithm" default:"SHA1"` // SHA1, SHA256, SHA512
	WindowSize int    `json:"window_size" default:"1"`  // Past/future periods to accept
}

TOTPConfig configures TOTP (Google Authenticator) settings

type TOTPFactorAdapter

type TOTPFactorAdapter struct {
	BaseFactorAdapter
	// contains filtered or unexported fields
}

TOTPFactorAdapter integrates twofa plugin's TOTP functionality as an MFA factor

func NewTOTPFactorAdapter

func NewTOTPFactorAdapter(twofaService *twofa.Service, enabled bool) *TOTPFactorAdapter

NewTOTPFactorAdapter creates a new TOTP factor adapter

func (*TOTPFactorAdapter) Challenge

func (a *TOTPFactorAdapter) Challenge(ctx context.Context, factor *Factor, metadata map[string]any) (*Challenge, error)

Challenge initiates a TOTP verification challenge For TOTP, there's no async challenge - user provides code directly

func (*TOTPFactorAdapter) Enroll

func (a *TOTPFactorAdapter) Enroll(ctx context.Context, userID xid.ID, metadata map[string]any) (*FactorEnrollmentResponse, error)

Enroll initiates TOTP enrollment

func (*TOTPFactorAdapter) Verify

func (a *TOTPFactorAdapter) Verify(ctx context.Context, challenge *Challenge, response string, data map[string]any) (bool, error)

Verify verifies a TOTP code

func (*TOTPFactorAdapter) VerifyEnrollment

func (a *TOTPFactorAdapter) VerifyEnrollment(ctx context.Context, enrollmentID xid.ID, proof string) error

VerifyEnrollment verifies TOTP enrollment by checking first code

type TrustDeviceRequest

type TrustDeviceRequest struct {
	// Body fields
	DeviceID string         `json:"deviceId" validate:"required" description:"Unique device identifier"`
	Name     string         `json:"name,omitempty" validate:"omitempty,min=1,max=100" description:"User-friendly device name"`
	Metadata map[string]any `json:"metadata,omitempty" description:"Device metadata (OS, browser, etc.)"`
}

TrustDeviceRequest represents the request to trust a device

type TrustedDevice

type TrustedDevice struct {
	ID         xid.ID         `json:"id"`
	UserID     xid.ID         `json:"userId"`
	DeviceID   string         `json:"deviceId"` // Fingerprint/identifier
	Name       string         `json:"name"`     // User-friendly name
	Metadata   map[string]any `json:"metadata"` // Device info
	IPAddress  string         `json:"ipAddress"`
	UserAgent  string         `json:"userAgent"`
	LastUsedAt *time.Time     `json:"lastUsedAt"`
	CreatedAt  time.Time      `json:"createdAt"`
	ExpiresAt  time.Time      `json:"expiresAt"`
}

TrustedDevice represents a device that can skip MFA

type TrustedDevicesConfig

type TrustedDevicesConfig struct {
	Enabled           bool `json:"enabled" default:"true"`
	DefaultExpiryDays int  `json:"default_expiry_days" default:"30"`
	MaxExpiryDays     int  `json:"max_expiry_days" default:"90"`
	MaxDevicesPerUser int  `json:"max_devices_per_user" default:"5"`
}

TrustedDevicesConfig configures trusted device settings

type UpdateFactorRequest

type UpdateFactorRequest struct {
	// Path parameters
	ID string `path:"id" validate:"required" description:"Factor ID"`

	// Body fields
	Name     *string         `json:"name,omitempty" validate:"omitempty,min=1,max=100" description:"New name for the factor"`
	Priority *FactorPriority `json:"priority,omitempty" validate:"omitempty,oneof=primary backup optional" description:"New priority level"`
	Status   *FactorStatus   `` /* 126-byte string literal not displayed */
	Metadata map[string]any  `json:"metadata,omitempty" description:"Updated metadata"`
}

UpdateFactorRequest represents the request to update a factor

type UpdatePolicyRequest

type UpdatePolicyRequest struct {
	// Body fields
	RequiredFactorCount    *int         `json:"requiredFactorCount,omitempty" validate:"omitempty,min=0,max=5" description:"Number of factors required"`
	AllowedFactorTypes     []FactorType `json:"allowedFactorTypes,omitempty" description:"Permitted factor types"`
	RequiredFactorTypes    []FactorType `json:"requiredFactorTypes,omitempty" description:"Mandatory factor types"`
	GracePeriodDays        *int         `json:"gracePeriodDays,omitempty" validate:"omitempty,min=0,max=365" description:"Days before MFA is enforced"`
	TrustedDeviceDays      *int         `json:"trustedDeviceDays,omitempty" validate:"omitempty,min=1,max=365" description:"Days device remains trusted"`
	StepUpRequired         *bool        `json:"stepUpRequired,omitempty" description:"Require step-up authentication for sensitive operations"`
	AdaptiveMFAEnabled     *bool        `json:"adaptiveMfaEnabled,omitempty" description:"Enable risk-based MFA"`
	MaxFailedAttempts      *int         `json:"maxFailedAttempts,omitempty" validate:"omitempty,min=1,max=10" description:"Maximum failed verification attempts"`
	LockoutDurationMinutes *int         `json:"lockoutDurationMinutes,omitempty" validate:"omitempty,min=1,max=1440" description:"Account lockout duration in minutes"`
}

UpdatePolicyRequest represents the request to update MFA policy (admin only)

type VerificationRequest

type VerificationRequest struct {
	ChallengeID    xid.ID         `json:"challengeId"`
	FactorID       xid.ID         `json:"factorId"`
	Code           string         `json:"code,omitempty"`
	Data           map[string]any `json:"data,omitempty"`
	RememberDevice bool           `json:"rememberDevice,omitempty"`
	DeviceInfo     *DeviceInfo    `json:"deviceInfo,omitempty"`
}

VerificationRequest verifies a challenge

type VerificationResponse

type VerificationResponse struct {
	Success          bool       `json:"success"`
	SessionComplete  bool       `json:"sessionComplete"`
	FactorsRemaining int        `json:"factorsRemaining,omitempty"`
	Token            string     `json:"token,omitempty"` // MFA completion token
	ExpiresAt        *time.Time `json:"expiresAt,omitempty"`
}

VerificationResponse indicates verification result

type VerifyChallengeRequest

type VerifyChallengeRequest struct {
	// Body fields
	ChallengeID    xid.ID         `json:"challengeId" validate:"required" description:"ID of the challenge to verify"`
	FactorID       xid.ID         `json:"factorId" validate:"required" description:"ID of the factor being used"`
	Code           string         `json:"code,omitempty" validate:"required_without=Data" description:"Verification code for OTP-based factors"`
	Data           map[string]any `json:"data,omitempty" description:"Verification data for complex factors"`
	RememberDevice bool           `json:"rememberDevice,omitempty" description:"Whether to trust this device"`
	DeviceInfo     *DeviceInfo    `json:"deviceInfo,omitempty" description:"Device identification information"`
}

VerifyChallengeRequest represents the request to verify an MFA challenge

type VerifyEnrolledFactorRequest

type VerifyEnrolledFactorRequest struct {
	// Path parameters
	ID string `path:"id" validate:"required" description:"Factor ID to verify"`

	// Body fields
	Code string         `json:"code,omitempty" validate:"required_without=Data" description:"Verification code for OTP-based factors"`
	Data map[string]any `json:"data,omitempty" description:"Verification data for complex factors (WebAuthn, etc.)"`
}

VerifyEnrolledFactorRequest represents the request to verify an enrolled factor

type WebAuthnConfig

type WebAuthnConfig struct {
	Enabled                bool     `json:"enabled" default:"true"`
	RPDisplayName          string   `json:"rp_display_name" default:"AuthSome"`
	RPID                   string   `json:"rp_id"`                                 // e.g., "example.com"
	RPOrigins              []string `json:"rp_origins"`                            // Allowed origins
	AttestationPreference  string   `json:"attestation_preference" default:"none"` // none, indirect, direct
	AuthenticatorSelection struct {
		RequireResidentKey     bool   `json:"require_resident_key" default:"false"`
		ResidentKeyRequirement string `json:"resident_key_requirement" default:"preferred"` // discouraged, preferred, required
		UserVerification       string `json:"user_verification" default:"preferred"`        // discouraged, preferred, required
	} `json:"authenticator_selection"`
	Timeout int `json:"timeout" default:"60000"` // Milliseconds
}

WebAuthnConfig configures WebAuthn/FIDO2 settings

type WebAuthnFactorAdapter

type WebAuthnFactorAdapter struct {
	BaseFactorAdapter
	// contains filtered or unexported fields
}

WebAuthnFactorAdapter integrates passkey plugin as an MFA factor This adapter enables passkeys to be used as a second authentication factor while maintaining support for standalone passwordless authentication

func NewWebAuthnFactorAdapter

func NewWebAuthnFactorAdapter(passkeyService *passkey.Service, enabled bool) *WebAuthnFactorAdapter

NewWebAuthnFactorAdapter creates a new WebAuthn factor adapter

func (*WebAuthnFactorAdapter) Challenge

func (a *WebAuthnFactorAdapter) Challenge(ctx context.Context, factor *Factor, metadata map[string]any) (*Challenge, error)

Challenge initiates a WebAuthn authentication challenge for MFA verification

func (*WebAuthnFactorAdapter) Enroll

func (a *WebAuthnFactorAdapter) Enroll(ctx context.Context, userID xid.ID, metadata map[string]any) (*FactorEnrollmentResponse, error)

Enroll initiates WebAuthn credential registration for MFA

func (*WebAuthnFactorAdapter) IsAvailable

func (a *WebAuthnFactorAdapter) IsAvailable() bool

IsAvailable checks if WebAuthn factor is available

func (*WebAuthnFactorAdapter) Verify

func (a *WebAuthnFactorAdapter) Verify(ctx context.Context, challenge *Challenge, response string, data map[string]any) (bool, error)

Verify verifies the WebAuthn challenge response

func (*WebAuthnFactorAdapter) VerifyEnrollment

func (a *WebAuthnFactorAdapter) VerifyEnrollment(ctx context.Context, enrollmentID xid.ID, proof string) error

VerifyEnrollment completes WebAuthn credential registration

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL