VOOZH about

URL: https://deepwiki.com/mathsgod/light/5.2.3-two-factor-authentication-(2fa)

⇱ Two-Factor Authentication (2FA) | mathsgod/light | DeepWiki


Loading...
Last indexed: 31 January 2026 (cf9511)
Menu

Two-Factor Authentication (2FA)

The Light framework implements Time-based One-Time Password (TOTP) authentication via Light\Security\TwoFactorAuthentication. TOTP secrets are stored in User.secret src/Model/User.php24 and validated during login. The system supports both per-user enablement and system-wide enforcement through Config::Value("two_factor_authentication").

Related pages: Password Authentication (5.2.1), WebAuthn (5.2.4), JWT Token System (5.2.2), User Model and Sessions (5.3)

Overview

2FA implementation involves three distinct phases:

PhaseGraphQL Entry PointKey ActionsCode Reference
SetupgetMy2FA()Generate TOTP secret, create QR codesrc/Controller/AuthController.php515-530
ActivationupdateMy2FA() or updateTwoFactorAuthentication()Validate code, store secret in User.secretsrc/Controller/AuthController.php31-61 498-508
Loginlogin()Verify TOTP code before JWT issuancesrc/Controller/AuthController.php420-443

Rate Limiting: Cache key two_factor_authentication_setup_{username} limits unauthenticated setup to 5 attempts per 10 minutes src/Controller/AuthController.php33-39

Sources: src/Controller/AuthController.php31-530 src/Model/User.php24

TOTP Architecture

2FA Phase Flow and Code Mapping


Sources: src/Controller/AuthController.php31-61 src/Controller/AuthController.php374-456 src/Controller/AuthController.php498-530 src/Model/User.php24

Setup Flow

Generating 2FA Credentials

GraphQL Mutation: getMy2FA(): mixed

Annotations: #[Mutation] #[Logged]

Implementation: src/Controller/AuthController.php515-530

Process Flow:

StepMethod/FunctionOutput
1TwoFactorAuthentication::generateSecret()Base32-encoded random secret (e.g., "JBSWY3DPEHPK3PXP")
2sprintf("otpauth://totp/%s@%s?secret=%s", ...)TOTP URI string
3QrCode::create($url)QR code object
4PngWriter::write($qrCode)PNG result object
5$png->getDataUri()Base64 data URI (data:image/png;base64,...)

Return Structure:


TOTP URI Format: otpauth://totp/{username}@{hostname}?secret={base32_secret}

The QR code is compatible with standard authenticator apps (Google Authenticator, Authy, Microsoft Authenticator). The client must retain the secret value for the activation phase.

Duplicate Implementation: User::getMy2FA() src/Model/User.php128-144 provides identical functionality as a GraphQL field on the User type.

Sources: src/Controller/AuthController.php515-530 src/Model/User.php128-144

Activating 2FA

Users activate 2FA by providing the secret from getMy2FA() plus a valid TOTP code from their authenticator app.

Authenticated User Activation

GraphQL Mutation: updateMy2FA(secret: String!, code: String!): bool

Annotations: #[Mutation] #[Logged] #[InjectUser]

Implementation: src/Controller/AuthController.php498-508

ValidationMethodError Message
TOTP code verificationTwoFactorAuthentication::checkCode($secret, $code)"two factor authentication error"

On success: Sets $user->secret = $secret and calls $user->save().

Unauthenticated Setup

GraphQL Mutation: updateTwoFactorAuthentication(username: String!, password: String!, secret: String!, code: String!): bool

Implementation: src/Controller/AuthController.php31-61

Validation Steps:

StepLineCheckFailure Action
1. Rate Limit33-39$cache->get("two_factor_authentication_setup_{username}") < 5Error: max attempts reached
2. User Lookup41User::Get(["username" => $username]) existsError: update failed
3. Prevent Re-Setup46-48$user->secret is emptyError: update failed
4. Password Verify50password_verify($password, $user->password)Error: update failed
5. Code Verify54TwoFactorAuthentication::checkCode($secret, $code)Error: setup failed
6. Store Secret58-59$user->secret = $secret; $user->save()-

Rate Limiting Details:

  • Cache Key: "two_factor_authentication_setup_" . $username
  • TTL: 600 seconds (10 minutes)
  • Limit: 5 attempts
  • Scope: Per username

Sources: src/Controller/AuthController.php31-61 src/Controller/AuthController.php498-508

Authentication Flow with 2FA

Login Flow with Two-Tier 2FA Verification


Sources: src/Controller/AuthController.php374-456

Login Verification Logic

The login() mutation src/Controller/AuthController.php374-456 implements two-tier 2FA validation with defense-in-depth.

Tier 1: User-Level 2FA

Location: src/Controller/AuthController.php420-428

Trigger: if ($user->secret) - User has 2FA enabled

Logic:


Tier 2: System-Level 2FA

Location: src/Controller/AuthController.php430-443

Trigger: $app->isTwoFactorAuthentication() returns true (reads Config::Value("two_factor_authentication"))

Logic:


Error Messages

Error StringLinesTrigger Condition
"two factor authentication code is required"422User has secret but $code parameter is null
"two factor authentication error"426, 441checkCode() validation fails
"setup_2fa_required"433System enforces 2FA but user's secret is empty

Defense in Depth: Both tiers execute independently. If system-wide 2FA is enabled mid-session, Tier 2 catches users who bypassed Tier 1, preventing race conditions.

Sources: src/Controller/AuthController.php420-443

User Model Integration

Database Schema

Field: User.secret src/Model/User.php24



























AttributeValue
TypeVARCHAR
FormatBase32-encoded TOTP shared secret (e.g., "JBSWY3DPEHPK3PXP")
Empty StateEmpty string "" indicates 2FA is disabled
PurposeStores TOTP secret for validation

GraphQL Fields

The User type exposes three 2FA-related GraphQL fields:

Field NameReturn TypeLine ReferenceLogicAnnotation
has2FABoolean!162-165return $this->secret ? true : false#[Field]
isTwoFactorEnabledBoolean!255-261return $this->secret ? true : false#[Field]
getMy2FAmixed128-144Generates new TOTP credentials (QR code + secret)#[Field]

Implementation Notes:

  • has2FA() and isTwoFactorEnabled() are functionally identical
  • getMy2FA() duplicates AuthController::getMy2FA() logic but is accessible as a field on User queries
  • All three fields evaluate $this->secret to determine 2FA status

Sources: src/Model/User.php24 src/Model/User.php128-144 src/Model/User.php162-165 src/Model/User.php255-261

Administrative Functions

Resetting User 2FA

Administrators with user.reset2fa permission can clear a user's 2FA configuration.

GraphQL Mutation: reset2FA(id: Int!): bool

Implementation: src/Controller/AuthController.php108-118


Authorization Requirements:

  • #[Logged] - Authenticated user required
  • #[Right('user.reset2fa')] - Permission check via RBAC system

Use Case: User loses access to authenticator app and cannot provide TOTP codes. Administrator clears secret field, forcing user to reconfigure 2FA via getMy2FA() and updateMy2FA().

Sources: src/Controller/AuthController.php108-118

Configuration and Rate Limiting

System-Wide 2FA Enforcement

Configuration Key: two_factor_authentication (stored in Config model via Config::Value())

Check Implementation: src/Controller/AuthController.php430


Method Chain: App::isTwoFactorAuthentication()Config::Value("two_factor_authentication") → boolean

Enforcement Effects:

ConditionLineError
Config enabled AND user has no secret432-434"setup_2fa_required"
Config enabled AND user provides invalid code438-442"two factor authentication error"

When system-wide 2FA is enabled, all users must configure User.secret before login is permitted.

Rate Limiting

Scope: updateTwoFactorAuthentication mutation (unauthenticated setup only)

Implementation: src/Controller/AuthController.php33-39

ParameterValue
Cache Key"two_factor_authentication_setup_" . $username
Limit5 attempts
TTL600 seconds (10 minutes)
Error Message"You have reached the maximum number of attempts to set up two-factor authentication. Please try again later."

Code Logic:


Cache Backend: App::getCache() returns PSR-6/PSR-16 cache implementation (Symfony Cache).

Note: updateMy2FA (authenticated setup) has no rate limiting as it requires valid authentication.

Sources: src/Controller/AuthController.php33-39 src/Controller/AuthController.php430-434

Security Considerations

Secret Storage

Field: User.secret src/Model/User.php24

TOTP shared secrets are stored in plaintext (required for TOTP validation). Security recommendations:

MitigationDescription
Database EncryptionEnable encryption at rest for database storage
Access ControlRestrict database user permissions via principle of least privilege
Field-Level EncryptionConsider application-level encryption for high-security environments
Audit LoggingMonitor access to User table via EventLog system

Code Validation

Method: TwoFactorAuthentication::checkCode($secret, $code)

TOTP Properties:

PropertyValue
Time Window30-second intervals (TOTP standard)
Step Tolerance±1 step (90-second total window)
Replay PreventionCode expires after time window
Clock DriftTolerance accommodates minor synchronization issues

Setup Security

Mutation: updateTwoFactorAuthentication src/Controller/AuthController.php31-61

Multi-Factor Verification:

FactorValidation MethodPurpose
1. UsernameUser::Get(["username" => $username])User identification
2. Passwordpassword_verify($password, $user->password)Authentication
3. TOTP CodeTwoFactorAuthentication::checkCode($secret, $code)Proof of authenticator possession
4. Rate LimitCache-based attempt counterBrute-force prevention (5/10min)

Defense in Depth

Login Validation: src/Controller/AuthController.php420-443

Redundant Check Architecture:

TierTriggerLinesPurpose
User-Levelif ($user->secret)420-428Per-user 2FA enforcement
System-Levelif ($app->isTwoFactorAuthentication())430-443Organization-wide policy

Both tiers execute independently. If system-wide 2FA is enabled after a user's session starts, Tier 2 enforces the policy regardless of Tier 1's outcome. This prevents race conditions during configuration changes.

Sources: src/Controller/AuthController.php31-61 src/Controller/AuthController.php420-443 src/Model/User.php24

QR Code Generation

QR Code Generation Pipeline


Implementation: src/Controller/AuthController.php518-529

Code Flow:


TOTP URI Specification:

  • Format: otpauth://totp/{username}@{hostname}?secret={base32_secret}
  • Example: otpauth://totp/admin@localhost?secret=JBSWY3DPEHPK3PXP
  • Compatibility: Google Authenticator, Authy, Microsoft Authenticator

Dependencies (endroid/qr-code):

Class/MethodPurpose
Endroid\QrCode\QrCode::create($data)Creates QR code object from TOTP URI string
Endroid\QrCode\Writer\PngWriterRenders QR code as PNG image
$result->getDataUri()Returns data:image/png;base64,... string for direct embedding

Usage: The data URI can be embedded in HTML <img src="{dataUri}"> tags or transmitted to client applications.

Sources: src/Controller/AuthController.php518-529 src/Model/User.php134-142

GraphQL API Reference

Mutations

File: src/Controller/AuthController.php

MutationParametersReturnsAnnotationsLinesDescription
getMy2FA-mixed (object with secret, host, image)#[Mutation] #[Logged]515-530Generate new TOTP secret and QR code
updateMy2FAsecret: String! code: String!bool#[Mutation] #[Logged] #[InjectUser]498-508Activate 2FA for authenticated user
updateTwoFactorAuthenticationusername: String! password: String! secret: String! code: String!bool#[Mutation]31-61Setup 2FA without authentication (rate-limited)
reset2FAid: Int!bool#[Mutation] #[Logged] #[Right('user.reset2fa')]108-118Clear user's 2FA secret (admin operation)
loginusername: String! password: String! code: String (optional)bool#[Mutation]374-456Authenticate with optional 2FA validation

User Type Fields

File: src/Model/User.php

FieldReturn TypeLinesAnnotationLogic
has2FABoolean!162-165#[Field]Returns $this->secret ? true : false
isTwoFactorEnabledBoolean!255-261#[Field]Returns $this->secret ? true : false (alias of has2FA)
getMy2FAmixed128-144#[Field]Generates TOTP credentials (same as mutation, callable on User queries)

Sources: src/Controller/AuthController.php31-530 src/Model/User.php128-261

Implementation Dependencies

Required Packages

  • endroid/qr-code: QR code generation
    • Used for generating authenticator app QR codes
    • Provides PNG writer and data URI encoding

Light Framework Components

  • Light\Security\TwoFactorAuthentication: TOTP implementation

    • generateSecret(): Creates base32-encoded shared secret
    • checkCode($secret, $code): Validates 6-digit TOTP code
  • Light\App: Application context

    • isTwoFactorAuthentication(): Checks system-wide 2FA requirement
    • getCache(): Provides PSR-6 cache for rate limiting
  • Light\Model\Config: Configuration storage

    • Config::Value("two_factor_authentication"): System-wide toggle
  • Light\Model\User: User model

    • secret property: Stores TOTP shared secret

Sources: src/Controller/AuthController.php1-19