Two-factor authentication adds a second verification step after credentials are validated. The system is built around a TwoFactorProvider contract so that multiple methods can be supported. For launch, TOTP and recovery codes are in scope. Email OTP and SMS OTP are planned post-launch extensions.
TwoFactorProvider
TwoFactorProvider is a contract that all 2FA method implementations satisfy. It exposes:
verify(User $user, string $code): bool — verifies a submitted code against the user's configured state for this provider
isEnabled(User $user): bool — checks whether this provider is enabled for the given user
Challenge Flow
When Authenticator::fromCredentials() detects that the user has 2FA enabled, it does not issue a token. Instead it generates a TwoFactorChallenge and returns it to the caller. The challenge contains:
- A short-lived signed challenge token — single-use, tied to the user, IP address, and user agent, expires after a few minutes
- The URL of the 2FA validation endpoint
The client submits the challenge token alongside the 2FA code to the validation endpoint. The endpoint verifies the challenge token is valid, unexpired, and unused, then delegates code verification to the appropriate TwoFactorProvider. On success, the challenge token is consumed and a RawToken is issued.
Subissues
Engine: Auth — 2FA TOTP
Engine: Auth — 2FA recovery codes
Engine: Auth — 2FA email OTP (post-launch)
Engine: Auth — 2FA SMS OTP (post-launch)
Tasks
Two-factor authentication adds a second verification step after credentials are validated. The system is built around a
TwoFactorProvidercontract so that multiple methods can be supported. For launch, TOTP and recovery codes are in scope. Email OTP and SMS OTP are planned post-launch extensions.TwoFactorProvider
TwoFactorProvideris a contract that all 2FA method implementations satisfy. It exposes:verify(User $user, string $code): bool— verifies a submitted code against the user's configured state for this providerisEnabled(User $user): bool— checks whether this provider is enabled for the given userChallenge Flow
When
Authenticator::fromCredentials()detects that the user has 2FA enabled, it does not issue a token. Instead it generates aTwoFactorChallengeand returns it to the caller. The challenge contains:The client submits the challenge token alongside the 2FA code to the validation endpoint. The endpoint verifies the challenge token is valid, unexpired, and unused, then delegates code verification to the appropriate
TwoFactorProvider. On success, the challenge token is consumed and aRawTokenis issued.Subissues
Engine: Auth — 2FA TOTPEngine: Auth — 2FA recovery codesEngine: Auth — 2FA email OTP(post-launch)Engine: Auth — 2FA SMS OTP(post-launch)Tasks
TwoFactorProvidercontractTwoFactorChallengevalue objectRawToken