Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Otphp Laravel Package

spomky-labs/otphp

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require spomky-labs/otphp
    

    Add to composer.json if using Laravel's autoloader:

    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "OTPHP\\": "vendor/spomky-labs/otphp/src/"
        }
    }
    
  2. First Use Case: Generate a TOTP (Time-based OTP) for Google Authenticator:

    use OTPHP\TOTP;
    use OTPHP\InternalClock;
    
    $clock = new InternalClock();
    $totp = TOTP::generate($clock);
    $secret = $totp->getSecret();
    $provisioningUri = $totp->getProvisioningUri();
    

    Output the provisioningUri (e.g., otpauth://totp/...) to scan in Google Authenticator.


Where to Look First

  • Documentation: vendor/spomky-labs/otphp/doc/ (e.g., Factory.md, Customize.md).
  • Key Classes:
    • OTPHP\TOTP (Time-based OTP).
    • OTPHP\HOTP (Counter-based OTP).
    • OTPHP\Factory (for provisioning URI parsing).
  • Examples: Check tests/ for real-world usage patterns.

Implementation Patterns

Core Workflows

1. Generating and Verifying TOTP

// Generate
$totp = TOTP::generate(new InternalClock())
    ->withLabel('user@example.com')
    ->withIssuer('MyApp');

// Verify user input (e.g., from a form)
$userInput = request('otp_code');
$isValid = $totp->verify($userInput); // Uses default window (1)

2. Handling HOTP (Counter-Based)

$hotp = HOTP::createFromSecret('SECRET_BASE32')
    ->withCounter(0); // Start counter at 0

// Verify with a window (e.g., for resync)
$isValid = $hotp->verify('123456', 999, 5); // Checks counters 999-1003

3. Provisioning URIs

// Generate URI for QR code
$uri = $totp->getProvisioningUri();
// OR parse existing URI
$parsedOtp = Factory::loadFromProvisioningUri($uri, $clock);

4. Customizing OTPs

$customTotp = TOTP::createFromSecret('SECRET')
    ->withPeriod(15)       // 15-second window
    ->withDigits(8)        // 8-digit codes
    ->withDigest('sha512') // Stronger hash
    ->withParameter('image', 'https://app.com/logo.png');

Laravel-Specific Patterns

1. Storing Secrets in Database

// Migration
Schema::create('otp_secrets', function (Blueprint $table) {
    $table->id();
    $table->string('user_id');
    $table->string('secret'); // Base32-encoded
    $table->string('label')->nullable();
    $table->string('issuer')->nullable();
    $table->timestamps();
});

// Model
class OtpSecret extends Model {
    public function getOtpObject() {
        return TOTP::createFromSecret($this->secret)
            ->withLabel($this->label)
            ->withIssuer($this->issuer);
    }
}

2. Middleware for OTP Verification

public function handle($request, Closure $next) {
    $user = Auth::user();
    $otpSecret = $user->otpSecret;
    $otp = $otpSecret->getOtpObject();

    if (!$otp->verify($request->otp_code)) {
        return redirect()->back()->withErrors(['otp' => 'Invalid code.']);
    }
    return $next($request);
}

3. Generating QR Codes

use SimpleSoftwareIO\QrCode\Facades\QrCode;

$uri = $totp->getProvisioningUri();
$qrCode = QrCode::size(200)->generate($uri);
return response($qrCode)->header('Content-Type', 'image/png');

4. Caching OTP Objects

// Cache the OTP object for 1 hour
$otp = Cache::remember("otp_{$user->id}", 3600, function () use ($user) {
    return $user->otpSecret->getOtpObject();
});

Gotchas and Tips

Pitfalls

  1. Clock Dependency:

    • TOTP requires a Clock interface (PSR-20). Use InternalClock for testing, but replace with a custom clock (e.g., Symfony\Clock) in production for time adjustments.
    • Fix: Pass a clock to Factory::loadFromProvisioningUri() or TOTP::generate().
      $clock = new Symfony\Clock\MockClock(); // For testing
      $totp = TOTP::generate($clock);
      
  2. Window Size:

    • Default window is 1 (strict verification). For user-friendly apps, increase to 35 for TOTP.
    • HOTP: Window checks multiple counters (e.g., counter ± 5).
    • TOTP: Window accounts for time drift (e.g., ±10 seconds with a 30-second period).
      $totp->verify('123456', null, 3); // Checks ±3 periods
      
  3. Secret Handling:

    • Secrets are Base32-encoded. Ensure no trailing = or padding issues.
    • Never store raw secrets—always encode/decode with OTPHP\Utils\Base32.
      use OTPHP\Utils\Base32;
      $secret = Base32::encodeUpper(random_bytes(20)); // 20 bytes = 32 chars
      
  4. Epoch Misuse:

    • Setting a custom epoch (TOTP-only) shifts the time window. Use sparingly—prefer default (0).
    • Warning: Dynamic secrets are required if reusing passwords with custom epochs.
  5. Label/Issuer Conflicts:

    • Google Authenticator prefixes the label with the issuer (e.g., MyApp:user@example.com).
    • Use withIssuerIncludedAsParameter(false) to avoid duplication in URIs.

Debugging Tips

  1. Verify Secrets:

    • Test secrets with OTP Authenticator or online tools like OTP Generator.
    • Example:
      $secret = 'JBSWY3DPEHPK3PXP';
      $totp = TOTP::createFromSecret($secret);
      echo $totp->now(); // Current OTP code
      
  2. Time Drift:

    • If verify() fails, check the clock skew:
      $now = $clock->getTimestamp();
      $period = $totp->getPeriod();
      $expectedRange = [$now - $period, $now + $period];
      echo "Current time: $now | Expected range: " . implode('–', $expectedRange);
      
  3. Provisioning URI Issues:

    • Validate URIs with:
      try {
          $otp = Factory::loadFromProvisioningUri($uri, $clock);
      } catch (\OTPHP\Exception\InvalidArgumentException $e) {
          // Handle malformed URI
      }
      

Extension Points

  1. Custom Clock for Testing:

    class FixedClock implements ClockInterface {
        public function getTimestamp(): int {
            return 1625097600; // Fixed Unix timestamp
        }
    }
    
  2. Override Defaults:

    • Extend TOTP or HOTP to enforce app-specific rules:
      class AppTOTP extends TOTP {
          public function verify(string $code, ?int $timestamp = null, ?int $window = null): bool {
              return parent::verify($code, $timestamp, $window ?? 3); // Force window=3
          }
      }
      
  3. Add Custom Parameters:

    • Extend OTPInterface to support app-specific metadata:
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope