The “Totp” class provides Time-based One-Time Password (TOTP) functionality for two-factor authentication (2FA) according to RFC 6238. It generates and verifies OTP codes that change every 30 seconds.

Generate Secret

Totp::generateSecret(): string

Generate a random base32-encoded secret key for TOTP authentication. This secret should be stored securely for each user enabling 2FA.

Returns a base32-encoded string (default 16 characters).

Example:

$secret = Totp::generateSecret();
// Store this secret for the user
Auth::updateTotpSecret($username, $secret);

Generate URI

Totp::generateURI(string $company, string $username, string $secret): string

Generate a TOTP URI that can be used to create a QR code for authenticator apps like Google Authenticator, Authy, or Microsoft Authenticator.

Parameters:

  • $company - Company/application name shown in the authenticator app
  • $username - User identifier (typically email or username)
  • $secret - The base32-encoded secret key

Returns an otpauth:// URI string.

Example:

$secret = Totp::generateSecret();
$uri = Totp::generateURI('MyCompany', 'user@example.com', $secret);
// $uri = 'otpauth://totp/MyCompany:user@example.com?issuer=MyCompany&secret=JDDK4U6G3BJLEZ7Y'

// Generate QR code from URI (using a QR code library)
$qrCodeUrl = "https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=" . urlencode($uri);

Display the QR code to the user so they can scan it with their authenticator app:

<img src="<?php e($qrCodeUrl); ?>" alt="Scan with authenticator app">
<p>Secret: <?php e($secret); ?></p>

Verify

Totp::verify(string $secret, string $otp): bool

Verify a TOTP code against a secret. Returns true if the OTP is valid for the current time period.

Parameters:

  • $secret - The base32-encoded secret key (empty string returns true)
  • $otp - The 6-digit OTP code to verify

Returns true if the code is valid, false otherwise.

Example:

$user = DB::selectOne('SELECT * FROM users WHERE username = ?', $username);
$secret = $user['totp_secret'];
$otp = $_POST['totp_code'];

if (Totp::verify($secret, $otp)) {
  // TOTP code is valid
  Auth::login($username, $password, $otp);
} else {
  // TOTP code is invalid
  $error = "Invalid two-factor authentication code";
}

Configuration

The Totp class can be configured by setting static properties in config/config.php:

Totp::$period = 30;           // Time period in seconds (default: 30)
Totp::$algorithm = 'sha1';    // Hash algorithm: sha1, sha256, sha512
Totp::$digits = 6;            // Number of digits in OTP code
Totp::$secretLength = 10;     // Length of generated secret (bytes)

Period

The time period in seconds for TOTP generation. Standard is 30 seconds.

Totp::$period = 60; // Generate codes valid for 60 seconds

Algorithm

The hash algorithm used for TOTP generation. Supported values: ‘sha1’, ‘sha256’, ‘sha512’. Default is ‘sha1’.

Totp::$algorithm = 'sha256'; // Use SHA-256 instead of SHA-1

Digits

The number of digits in the generated OTP code. Standard is 6 digits.

Totp::$digits = 8; // Generate 8-digit codes instead of 6

Secret Length

The length in bytes of generated secrets. Default is 10 bytes (16 characters in base32).

Totp::$secretLength = 20; // Generate longer secrets

Complete 2FA Setup Example

Here’s a complete example of setting up two-factor authentication:

// 1. Generate secret when user enables 2FA
$secret = Totp::generateSecret();
Auth::updateTotpSecret($username, $secret);

// 2. Show QR code to user
$uri = Totp::generateURI('MyApp', $username, $secret);
$qrUrl = "https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=" . urlencode($uri);
?>
<h2>Enable Two-Factor Authentication</h2>
<p>Scan this QR code with your authenticator app:</p>
<img src="<?php e($qrUrl); ?>" alt="QR Code">
<p>Or enter this code manually: <code><?php e($secret); ?></code></p>

<?php
// 3. Verify setup with a test code
if (isset($_POST['verify_code'])) {
  if (Totp::verify($secret, $_POST['verify_code'])) {
    echo "Two-factor authentication successfully enabled!";
  } else {
    echo "Invalid code. Please try again.";
  }
}
?>

<form method="post">
  <input type="text" name="verify_code" placeholder="Enter 6-digit code">
  <button type="submit">Verify</button>
</form>

<?php
// 4. During login, verify TOTP code
$user = Auth::login($username, $password, $_POST['totp_code']);
if ($user) {
  // Login successful with valid TOTP
} else {
  // Login failed - wrong password or TOTP code
}

Security Notes

  • Store TOTP secrets securely in your database
  • Never expose secrets to users after initial setup
  • TOTP codes are valid for 30 seconds by default (±1 period for clock skew)
  • Empty secrets ('') will always verify as true, so check for non-empty secrets
  • Use HTTPS to protect codes in transit
  • Implement backup codes in case users lose access to their authenticator device