Two-Factor Authentication
AuthKit provides a driver-based, challenge-driven two-factor authentication (2FA) system designed to be flexible, extensible, and fully configuration-driven.
It supports:
- login-time two-factor challenges
- recovery code fallback
- optional resend flows (driver-dependent)
- pluggable drivers (TOTP, email, custom, etc.)
Overview
Two-factor authentication in AuthKit operates as a second-step authentication layer after primary credential validation.
The flow is built around a pending login challenge, which temporarily stores the context required to complete authentication.
Core responsibilities:
- create a pending login challenge
- present a challenge UI
- verify a submitted code or recovery code
- establish an authenticated session
Pending Login Challenge
AuthKit uses:
Xul\AuthKit\Support\PendingLoginResponsibilities
- generate a short-lived challenge token
- store challenge payload (user + metadata)
- allow peek (non-destructive read)
- allow consume (single-use validation)
- allow explicit deletion
Stored payload
A pending challenge contains:
user_id→ the user being authenticatedremember→ remember-me preferencemethods→ allowed 2FA methods (e.g. totp)created_at→ timestamp
Key behavior
- tokens are short-lived
- tokens are single-use when consumed
- invalid or expired tokens terminate the flow
Routes
Two-factor routes are resolved via:
authkit.route_names.web.two_factor_challenge
authkit.route_names.web.login
authkit.route_names.api.two_factor_challenge
authkit.route_names.api.two_factor_recovery
authkit.route_names.api.two_factor_resendThese routes power:
- the challenge page
- challenge submission
- recovery submission
- resend requests
Challenge Flow
1. Challenge creation
After successful primary authentication:
- AuthKit creates a pending challenge using:
Xul\AuthKit\Support\PendingLogin- The challenge token is stored (typically in session or request context)
2. Challenge page
The user is redirected to the two-factor page - the page:
- verifies the challenge exists (via peek)
- renders the configured form schema
- allows:
- code submission
- recovery code fallback
- resend (if supported)
3. Challenge completion
Handled by:
Xul\AuthKit\Http\Controllers\Api\Auth\TwoFactorChallengeControllerDelegates to:
Xul\AuthKit\Actions\Auth\TwoFactorChallengeActionFlow:
- validate request
- build mapped payload
- resolve challenge
- resolve user from provider
- verify code using active driver
- consume or invalidate challenge
- log user in
- dispatch events
- redirect
Recovery Flow
Recovery codes provide a fallback when the primary method is unavailable. Handled by:
Xul\AuthKit\Http\Controllers\Api\Auth\TwoFactorRecoveryControllerDelegates to:
Xul\AuthKit\Actions\Auth\TwoFactorRecoveryActionFlow:
- validate challenge + recovery code
- resolve pending challenge
- resolve user
- verify recovery code
- consume recovery code
- complete login
- clear challenge
- redirect
Resend Flow (Optional)
Some drivers support resending a challenge (e.g. email/SMS).
Handled by:
Xul\AuthKit\Http\Controllers\Api\Auth\TwoFactorResendControllerDelegates to:
Xul\AuthKit\Actions\Auth\TwoFactorResendActionRequirements
The active driver must implement:
Xul\AuthKit\Contracts\TwoFactorResendableContractIf not implemented:
- resend will fail gracefully
- no resend UI should be shown
Driver System
AuthKit uses a pluggable driver system:
Xul\AuthKit\Contracts\TwoFactorDriverContractResponsibilities
Every driver must:
- declare its key
- determine if 2FA is enabled for a user
- declare supported methods
- verify authentication codes
- verify recovery codes
- consume recovery codes
- generate recovery codes
Default Driver (TOTP)
AuthKit ships with:
Xul\AuthKit\Support\TwoFactor\Drivers\TotpTwoFactorDriverFeatures:
- RFC 6238 TOTP verification
- configurable digits, period, window
- Base32 secret support
- recovery code generation
- optional hashing of recovery codes
Secret-Based Drivers
Drivers that require secrets must implement:
Xul\AuthKit\Contracts\TwoFactorSecretProviderContractUsed for:
- TOTP
- any shared-secret-based systems
Model Requirements
To enable two-factor on your user model, include:
Xul\AuthKit\Concerns\Model\HasAuthKitTwoFactorResponsibilities of the trait
- manage enabled state
- store and resolve secrets
- manage recovery codes
- support hashing of recovery codes
- expose methods for driver interaction
Required columns (configurable)
authkit.two_factor.columns.enabled
authkit.two_factor.columns.secret
authkit.two_factor.columns.recovery_codes
authkit.two_factor.columns.methodsPayload Mapping
AuthKit uses mappers to normalize incoming data.
Challenge mapper
Xul\AuthKit\Support\Mappers\Auth\TwoFactorChallengePayloadMapperMaps:
- challenge → attributes.challenge
- code → attributes.code
Recovery mapper
Xul\AuthKit\Support\Mappers\Auth\TwoFactorRecoveryPayloadMapperMaps:
challengerecovery_code
Resend mapper
Xul\AuthKit\Support\Mappers\Auth\TwoFactorResendPayloadMapperMaps:
email
Session Handling
`` AuthKit stores the active challenge in session using:
Xul\AuthKit\Support\AuthKitSessionKeys::TWO_FACTOR_CHALLENGEBehavior
- set during login
- cleared on:
- success
- expiration
- invalid attempts (depending on strategy)
Challenge Strategy
Controlled via:
authkit.two_factor.challenge_strategyOptions
peek→ challenge remains valid until explicitly consumedconsume→ challenge is invalidated immediately on verification attempt
Setup Utilities (Optional)
AuthKit provides helper classes for setup flows.
Ensure secret exists
Xul\AuthKit\Support\TwoFactor\EnsureTwoFactorSecretForUserEnsures:
- user has a secret when required
- safe to call multiple times
Generate OTP URI
Xul\AuthKit\Support\TwoFactor\TwoFactorOtpUriFactoryGenerates:
- otpauth://totp/... Used for:
- QR codes
- manual setup
Render QR code
Xul\AuthKit\Support\TwoFactor\TwoFactorQrCodeRendererOutputs:
- inline SVG QR code
Configuration
'two_factor' => [
'enabled' => true,
'driver' => 'totp',
'challenge_strategy' => 'peek',
'columns' => [
'enabled' => 'two_factor_enabled',
'secret' => 'two_factor_secret',
'recovery_codes' => 'two_factor_recovery_codes',
'methods' => 'two_factor_methods',
],
'totp' => [
'digits' => 6,
'period' => 30,
'window' => 1,
'algo' => 'sha1',
],
'security' => [
'encrypt_secret' => true,
'hash_recovery_codes' => true,
'recovery_hash_driver' => 'bcrypt',
],
],Events
AuthKit emits events during two-factor flows:
- login completed via 2FA:
Xul\AuthKit\Events\AuthKitTwoFactorLoggedIn- login completed via recovery:
Xul\AuthKit\Events\AuthKitTwoFactorRecovered- resend triggered:
Xul\AuthKit\Events\AuthKitTwoFactorResent- standard login:
Xul\AuthKit\Events\AuthKitLoggedInCustomization
You can customize the system via:
Drivers
authkit.two_factor.driverMust implement:
Xul\AuthKit\Contracts\TwoFactorDriverContractResend capability (optional)
Xul\AuthKit\Contracts\TwoFactorResendableContractSecret providers (optional)
Xul\AuthKit\Contracts\TwoFactorSecretProviderContractMappers
authkit.mappers.contexts.two_factor_challenge.class
authkit.mappers.contexts.two_factor_recovery.class
authkit.mappers.contexts.two_factor_resend.classBest Practices
- always keep challenges short-lived
- prefer
peekstrategy unless strict single-attempt flows are required - hash recovery codes in production
- encrypt secrets when using secret-based drivers
- only expose resend UI when the driver supports it
- keep driver logic isolated and stateless where possible
Summary
AuthKit’s two-factor system is:
- challenge-driven (PendingLogin-based)
- driver-powered (pluggable verification logic)
- mapper-driven (normalized payload handling)
- configurable (no hardcoded flows)
This design allows you to:
- swap drivers without changing controllers
- customize validation and payloads
- extend into email/SMS/biometric flows
- maintain consistent UX across authentication flows