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
    
  2. Basic TOTP Generation:
    use OTPHP\TOTP;
    
    $secret = TOTP::generate(); // Generates a random Base32 secret
    $totp = TOTP::createFromSecret($secret);
    $code = $totp->now(); // Current OTP code
    
  3. Basic HOTP Generation:
    use OTPHP\HOTP;
    
    $secret = HOTP::generate();
    $hotp = HOTP::createFromSecret($secret);
    $code = $hotp->at(1); // OTP code for counter=1
    

First Use Case: User Authentication

// Store this in your user model/database
$userSecret = 'JBSWY3DPEHPK3PXP'; // Base32 encoded

// During login
$totp = TOTP::createFromSecret($userSecret);
$isValid = $totp->verify($_POST['otp_code']);

Implementation Patterns

Common Workflows

1. User Provisioning

// Generate provisioning URI for QR code
$totp = TOTP::createFromSecret($userSecret)
    ->withLabel('user@example.com')
    ->withIssuer('MyApp');

$provisioningUri = $totp->getProvisioningUri();
$qrCodeUrl = $totp->getQrCodeUri(); // Requires GD library

2. Verification Pipeline

public function verifyOtp(Request $request)
{
    $user = User::find($request->user_id);
    $otp = Factory::loadFromProvisioningUri($user->provisioning_uri);

    return $otp->verify($request->otp_code)
        ? response()->json(['success' => true])
        : response()->json(['error' => 'Invalid OTP'], 401);
}

3. HOTP Counter Management

// Store counter in database
$user->hotp_counter = $hotp->getCounter();
$user->save();

// Next verification
$hotp = HOTP::createFromSecret($user->secret)
    ->withCounter($user->hotp_counter + 1);

Integration Tips

Laravel Service Provider

// app/Providers/OtpServiceProvider.php
public function register()
{
    $this->app->singleton(OTPFactory::class, function() {
        return new Factory();
    });
}

Configuration Management

// config/otp.php
return [
    'default' => [
        'digits' => 6,
        'algorithm' => 'sha256',
        'period' => 30,
    ],
    'admin' => [
        'digits' => 8,
        'algorithm' => 'sha512',
        'period' => 60,
    ]
];

// Usage
$totp = TOTP::createFromSecret($secret)
    ->withConfig(config('otp.admin'));

Migration Strategy

// For existing users upgrading security settings
public function upgradeOtp(User $user)
{
    $newSecret = TOTP::generate();
    $newOtp = TOTP::createFromSecret($newSecret)
        ->withLabel($user->email)
        ->withIssuer(config('app.name'))
        ->withDigits(config('otp.default.digits'))
        ->withAlgorithm(config('otp.default.algorithm'));

    $user->update([
        'otp_secret' => $newSecret,
        'provisioning_uri' => $newOtp->getProvisioningUri()
    ]);
}

Gotchas and Tips

Common Pitfalls

  1. Secret Storage:

    • Never store secrets in plain text - always use Base32 encoding
    • Consider encrypting secrets in your database if compliance requires it
  2. Time Synchronization:

    // For TOTP, ensure server time matches user devices
    $totp->verify($code, time() - 30, time() + 30); // 30s leeway
    
  3. HOTP Counter Management:

    • Always increment counters after successful verification
    • Never reuse counters or allow counter rollback
  4. Algorithm Selection:

    • SHA1 is deprecated - use SHA256 or SHA512
    • $totp->withAlgorithm('sha256'); // Recommended minimum
      

Debugging Tips

  1. Verification Debugging:

    try {
        $isValid = $totp->verify($code);
    } catch (\OTPHP\Exception\InvalidParameterException $e) {
        // Handle parameter errors
    } catch (\OTPHP\Exception\SecretDecodingException $e) {
        // Handle secret decoding issues
    }
    
  2. Time Window Issues:

    // Check current time window
    $currentTime = time();
    $windowStart = $currentTime - ($totp->getPeriod() * $totp->getWindow());
    $windowEnd = $currentTime + ($totp->getPeriod() * $totp->getWindow());
    
    // Log for debugging
    \Log::debug("Verification window: {$windowStart}-{$windowEnd}");
    
  3. QR Code Generation:

    • Ensure GD library is installed (php-gd)
    • Handle exceptions for image generation:
    try {
        $qrCode = $totp->getQrCode();
    } catch (\RuntimeException $e) {
        // Fallback to provisioning URI
        return response()->json(['uri' => $totp->getProvisioningUri()]);
    }
    

Configuration Quirks

  1. Default Values:

    • The library uses sensible defaults but may differ from auth apps
    • Always explicitly set required parameters:
    $totp->withDigits(6)
         ->withAlgorithm('sha256')
         ->withPeriod(30);
    
  2. Label Formatting:

    • Google Authenticator has specific requirements:
    • No colons (:) in labels
    • Format: issuer:account
    $totp->withIssuer('MyApp')
         ->withLabel('user@example.com');
    
  3. Provisioning URI Parsing:

    • Always validate URIs before use:
    try {
        $otp = Factory::loadFromProvisioningUri($uri);
    } catch (\OTPHP\Exception\InvalidProvisioningUriException $e) {
        // Handle invalid URI
    }
    

Extension Points

  1. Custom Verification Logic:

    $customVerifier = new class($totp) implements OTPVerifierInterface {
        public function verify($code, $timestamp = null, $window = null)
        {
            // Custom verification logic
            return $this->otp->verify($code, $timestamp, $window) &&
                   $this->additionalCheck($code);
        }
    };
    
  2. Event Dispatching:

    // Listen for OTP generation
    event(new OTPGenerated($totp, $code));
    
    // In your event handler
    public function handle(OTPGenerated $event)
    {
        // Log or notify about new OTP generation
    }
    
  3. Caching Strategy:

    // Cache OTP objects for performance
    $cacheKey = "otp:{$user->id}";
    $otp = Cache::remember($cacheKey, 60, function() use ($user) {
        return Factory::loadFromProvisioningUri($user->provisioning_uri);
    });
    
  4. Multi-Factor Integration:

    // Combine with Laravel's auth
    public function authenticate(Request $request)
    {
        $user = User::find($request->user_id);
        $otp = Factory::loadFromProvisioningUri($user->provisioning_uri);
    
        if (!$otp->verify($request->otp_code)) {
            return back()->withErrors(['otp' => 'Invalid code']);
        }
    
        Auth::login($user);
    }
    
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.
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
ecotone/kafka
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata