litepie / otp
A comprehensive Laravel package for generating, signing and managing OTP codes with multiple channels support
Requires
- php: ^8.2
- illuminate/cache: ^10.0|^11.0|^12.0
- illuminate/console: ^10.0|^11.0|^12.0
- illuminate/database: ^10.0|^11.0|^12.0
- illuminate/mail: ^10.0|^11.0|^12.0
- illuminate/notifications: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- mockery/mockery: ^1.6
- orchestra/testbench: ^8.0|^9.0|^10.0
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.0|^11.0
This package is not auto-updated.
Last update: 2025-08-31 02:47:10 UTC
README
A comprehensive Laravel package for generating, signing and managing OTP (One-Time Password) codes with multiple channels support.
๐ Requirements
- PHP 8.2 or higher
- Laravel 10.0, 11.0, or 12.0
๐ Features
- โ Secure OTP Generation - Generate cryptographically secure OTP codes
- โ Digital Signing - Sign OTP codes for enhanced security and verification
- โ Multiple Delivery Channels - Email, SMS, Database, and custom channels
- โ Flexible Configuration - Customizable length, format, and expiration
- โ Rate Limiting - Built-in protection against abuse
- โ Multiple OTP Types - Login, email verification, password reset, 2FA, etc.
- โ Event System - Complete lifecycle events for monitoring and logging
- โ Queue Support - Background processing for sending OTPs
- โ Auto-cleanup - Automatic removal of expired OTPs
- โ Laravel 12 Ready - Full compatibility with the latest Laravel versions
- โ Production Ready - Thoroughly tested and optimized for production use
๐ฆ Installation
You can install the package via Composer:
composer require litepie/otp
Publish Configuration
Publish the configuration file:
php artisan vendor:publish --provider="Litepie\Otp\OtpServiceProvider" --tag="config"
Run Migrations
Run the migrations to create the OTPs table:
php artisan migrate
Set Up Automatic Cleanup (Optional)
Add the following to your app/Console/Kernel.php
file to automatically clean up expired OTPs:
protected function schedule(Schedule $schedule) { $schedule->command('otp:cleanup')->daily(); }
๐ง Configuration
The configuration file config/otp.php
allows you to customize:
- Default OTP Settings - Length, format, expiration, channels
- OTP Types - Specific settings for different use cases
- Rate Limiting - Prevent abuse with configurable limits
- Digital Signing - Secure OTP verification
- Channel Configuration - Email, SMS, and custom channel settings
- Automatic Cleanup - Keep your database clean
Environment Variables
Add these to your .env
file:
# OTP Signing Secret (defaults to APP_KEY) OTP_SIGNING_SECRET=your-secret-key # SMS Provider Configuration OTP_SMS_PROVIDER=log # Options: log, nexmo, twilio # Nexmo/Vonage NEXMO_KEY=your-nexmo-key NEXMO_SECRET=your-nexmo-secret NEXMO_FROM=YourApp # Twilio TWILIO_SID=your-twilio-sid TWILIO_TOKEN=your-twilio-token TWILIO_FROM=your-twilio-number
๐ Usage
Quick Start
use Litepie\Otp\Facades\Otp; // Generate and send OTP $otp = Otp::generate() ->for('user@example.com') ->type('login') ->send(); // Verify OTP $isValid = Otp::verify('123456', 'user@example.com', 'login'); if ($isValid) { // OTP is valid, proceed with authentication return response()->json(['message' => 'Login successful']); }
Advanced Usage
// Custom OTP with specific settings $otp = Otp::generate() ->for('user@example.com') ->type('password_reset') ->length(8) // 8 digits ->format('alphanumeric') // Letters and numbers ->expiresIn(900) // 15 minutes ->via(['email', 'sms']) // Multiple channels ->with(['user_id' => 123]) // Additional data ->send(); // Check if OTP exists before generating new one if (!Otp::exists('user@example.com', 'login')) { $otp = Otp::generate() ->for('user@example.com') ->type('login') ->send(); } // Invalidate existing OTP Otp::invalidate('user@example.com', 'login');
Exception Handling
use Litepie\Otp\Exceptions\TooManyAttemptsException; use Litepie\Otp\Exceptions\RateLimitExceededException; try { $isValid = Otp::verify($code, $email, 'login'); } catch (TooManyAttemptsException $e) { return response()->json(['error' => 'Too many failed attempts'], 429); } catch (RateLimitExceededException $e) { return response()->json(['error' => 'Rate limit exceeded'], 429); }
๐ฏ OTP Types
The package supports multiple OTP types with individual configurations:
Built-in Types
Type | Use Case | Default Length | Default Expiry |
---|---|---|---|
login |
User authentication | 6 digits | 5 minutes |
email_verification |
Email verification | 6 digits | 10 minutes |
password_reset |
Password reset | 8 characters | 15 minutes |
two_factor |
2FA authentication | 6 digits | 3 minutes |
phone_verification |
Phone verification | 6 digits | 5 minutes |
Custom Types
Define custom OTP types in your configuration:
// config/otp.php 'types' => [ 'transaction_verify' => [ 'length' => 8, 'format' => 'alphanumeric', 'expires_in' => 600, // 10 minutes 'max_attempts' => 3, 'channels' => ['email', 'sms'], 'rate_limit' => [ 'max_attempts' => 2, 'decay_minutes' => 30, ], ], ],
๐ก Delivery Channels
Email Channel
Sends OTP via email using Laravel's notification system or traditional mail.
Otp::generate() ->for('user@example.com') ->via('email') ->send();
SMS Channel
Send OTPs via SMS using various providers:
Otp::generate() ->for('+1234567890') ->via('sms') ->send();
Supported SMS Providers:
- Log (for testing)
- Nexmo/Vonage
- Twilio
- Custom providers (extensible)
Database Channel
Store OTP in database for manual retrieval:
Otp::generate() ->for('user@example.com') ->via('database') ->send(); // Retrieve from database $otpRecord = \Litepie\Otp\Otp::where('identifier', 'user@example.com') ->where('type', 'login') ->valid() ->first();
Multiple Channels
Send via multiple channels simultaneously:
Otp::generate() ->for('user@example.com') ->via(['email', 'sms', 'database']) ->send();
Custom Channels
Create custom delivery channels:
use Litepie\Otp\Contracts\OtpChannelInterface; class SlackChannel implements OtpChannelInterface { public function send(string $identifier, string $code, array $data = []): bool { // Implementation for Slack delivery return true; } public function canHandle(string $identifier): bool { return str_starts_with($identifier, '@slack:'); } } // Register the custom channel Otp::extend('slack', function () { return new SlackChannel(); });
๐ Events
The package fires comprehensive events for monitoring and logging:
Available Events
OtpGenerated
- When an OTP is generatedOtpSent
- When an OTP is sent via a channelOtpVerified
- When an OTP is successfully verifiedOtpFailed
- When OTP verification fails
Event Listeners
// In EventServiceProvider protected $listen = [ \Litepie\Otp\Events\OtpGenerated::class => [ \App\Listeners\LogOtpGenerated::class, ], \Litepie\Otp\Events\OtpVerified::class => [ \App\Listeners\LogOtpVerified::class, \App\Listeners\SendWelcomeEmail::class, ], \Litepie\Otp\Events\OtpFailed::class => [ \App\Listeners\LogFailedOtpAttempt::class, ], ];
Example Listener
class LogOtpGenerated { public function handle(\Litepie\Otp\Events\OtpGenerated $event) { Log::info('OTP generated', [ 'identifier' => $event->otp->identifier, 'type' => $event->otp->type, 'expires_at' => $event->otp->expires_at, ]); } }
๐ ๏ธ Artisan Commands
Cleanup Expired OTPs
# Clean up expired OTPs (default: 7 days) php artisan otp:cleanup # Clean up OTPs older than specific days php artisan otp:cleanup --days=3 # Force cleanup without confirmation php artisan otp:cleanup --force
๐ Security Features
- Digital Signing - All OTPs are digitally signed using HMAC-SHA256
- Rate Limiting - Configurable rate limiting per identifier and type
- Secure Generation - Cryptographically secure random code generation
- Attempt Tracking - Track and limit verification attempts
- Automatic Cleanup - Remove expired OTPs automatically
- Timing Attack Protection - Use
hash_equals()
for secure comparisons
๐งช Testing
Running Tests
# Run all tests composer test # Run tests with coverage composer test-coverage # Run specific test vendor/bin/phpunit tests/Unit/OtpTest.php
Test Example
use Litepie\Otp\Facades\Otp; use Illuminate\Support\Facades\Mail; public function test_otp_generation_and_verification() { Mail::fake(); // Generate OTP $otp = Otp::generate() ->for('test@example.com') ->type('login') ->send(); // Verify OTP $this->assertTrue( Otp::verify($otp->code, 'test@example.com', 'login') ); // Assert mail was sent Mail::assertSent(\Litepie\Otp\Notifications\OtpNotification::class); }
๐ Documentation
- Examples - Comprehensive usage examples
- Contributing - How to contribute
- Security - Security policy
- Changelog - Version history
๐ค Contributing
We welcome contributions! Please see CONTRIBUTING.md for details.
Development Setup
git clone https://github.com/litepie/otp.git cd otp composer install composer test
๐ Security
If you discover a security vulnerability, please send an email to security@litepie.com. All security vulnerabilities will be promptly addressed.
๐ License
The MIT License (MIT). Please see License File for more information.
๐ Support
- โญ Star this repo if you find it helpful
- ๐ Report issues on GitHub Issues
- ๐ก Request features via GitHub Discussions
- ๐ง Contact us at support@litepie.com
Made with โค๏ธ by Litepie