A simple, flexible, and production-ready OTP (One-Time Password) management package for Laravel applications. This package provides multiple storage drivers (Cache, Database, Redis) and delivery methods (SMS, Email, Log) with comprehensive configuration options.
- 🚀 Multiple Storage Drivers: Cache, Database, Redis
- 📱 Multiple Delivery Methods: SMS (Twilio, Nexmo), Email, Log
- ⚡ High Performance: Optimized for production use
- đź”’ Security Features: Rate limiting, attempt tracking, automatic cleanup
- 🎨 Flexible Configuration: Customizable code types, lengths, TTL, and messages
- đź§Ş Well Tested: Comprehensive test coverage
- 📦 Laravel Integration: Service provider, facades, and artisan commands
composer require aslnbxrz/simple-otpphp artisan vendor:publish --provider="Aslnbxrz\\SimpleOTP\\SimpleOTPServiceProvider" --tag="simple-otp-config"php artisan vendor:publish --provider="Aslnbxrz\\SimpleOTP\\SimpleOTPServiceProvider" --tag="simple-otp-migrations"
php artisan migratephp artisan vendor:publish --provider="Aslnbxrz\\SimpleOTP\\SimpleOTPServiceProvider" --tag="simple-otp-views"The package configuration file will be published to config/simple-otp.php. Here's an overview of the main configuration options:
// config/simple-otp.php
return [
'default' => env('SIMPLE_OTP_STORAGE', 'cache'),
'drivers' => [
'cache' => [
'driver' => 'cache',
'store' => env('SIMPLE_OTP_CACHE_STORE', null), // null means use default cache store
],
'database' => [
'driver' => 'database',
'table' => 'simple_otp_codes',
'connection' => env('SIMPLE_OTP_DB_CONNECTION', null),
],
'redis' => [
'driver' => 'redis',
'connection' => env('SIMPLE_OTP_REDIS_CONNECTION', 'default'),
'prefix' => env('SIMPLE_OTP_REDIS_PREFIX', 'simple_otp:'),
],
],
];'delivery' => [
'default' => env('SIMPLE_OTP_DELIVERY', 'log'),
'drivers' => [
'sms' => [
'driver' => 'sms',
'provider' => env('SIMPLE_OTP_SMS_PROVIDER', 'twilio'),
'config' => [
'twilio' => [
'account_sid' => env('TWILIO_ACCOUNT_SID'),
'auth_token' => env('TWILIO_AUTH_TOKEN'),
'from' => env('TWILIO_FROM_NUMBER'),
],
'nexmo' => [
'api_key' => env('NEXMO_API_KEY'),
'api_secret' => env('NEXMO_API_SECRET'),
'from' => env('NEXMO_FROM_NUMBER'),
],
'eskiz' => [
'email' => env('ESKIZ_EMAIL'),
'password' => env('ESKIZ_PASSWORD'),
'from' => env('ESKIZ_FROM', '4546'),
'base_url' => env('ESKIZ_BASE_URL', 'https://notify.eskiz.uz/api'),
],
'telegram' => [
'bot_token' => env('TELEGRAM_BOT_TOKEN'),
'parse_mode' => env('TELEGRAM_PARSE_MODE', 'HTML'),
'disable_web_page_preview' => env('TELEGRAM_DISABLE_PREVIEW', true),
],
],
],
'email' => [
'driver' => 'email',
'mailable' => \Aslnbxrz\SimpleOTP\Mail\OTPMailable::class,
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
'name' => env('MAIL_FROM_NAME', 'Example'),
],
],
],
],'otp' => [
'type' => env('SIMPLE_OTP_TYPE', 'numeric'), // numeric, alphanumeric, alpha
'length' => (int) env('SIMPLE_OTP_LENGTH', 6),
'ttl' => (int) env('SIMPLE_OTP_TTL', 5), // minutes
'max_attempts' => (int) env('SIMPLE_OTP_MAX_ATTEMPTS', 3),
'rate_limit' => [
'enabled' => env('SIMPLE_OTP_RATE_LIMIT_ENABLED', true),
'max_attempts' => (int) env('SIMPLE_OTP_RATE_LIMIT_ATTEMPTS', 5),
'decay_minutes' => (int) env('SIMPLE_OTP_RATE_LIMIT_DECAY', 60),
],
],Add these variables to your .env file:
# Storage Configuration
SIMPLE_OTP_STORAGE=cache
SIMPLE_OTP_CACHE_STORE=null
SIMPLE_OTP_DB_CONNECTION=mysql
SIMPLE_OTP_REDIS_CONNECTION=default
SIMPLE_OTP_REDIS_PREFIX=simple_otp:
# Delivery Configuration
SIMPLE_OTP_DELIVERY=log
SIMPLE_OTP_SMS_PROVIDER=twilio
# Twilio Configuration
TWILIO_ACCOUNT_SID=your_account_sid
TWILIO_AUTH_TOKEN=your_auth_token
TWILIO_FROM_NUMBER=+1234567890
# Nexmo Configuration (alternative)
NEXMO_API_KEY=your_api_key
NEXMO_API_SECRET=your_api_secret
NEXMO_FROM_NUMBER=1234567890
# Eskiz Configuration (Uzbekistan)
ESKIZ_EMAIL=your_email
ESKIZ_PASSWORD=your_password
ESKIZ_FROM=4546
ESKIZ_BASE_URL=https://notify.eskiz.uz/api
# Telegram Configuration
TELEGRAM_BOT_TOKEN=your_bot_token
TELEGRAM_PARSE_MODE=HTML
TELEGRAM_DISABLE_PREVIEW=true
# OTP Configuration
SIMPLE_OTP_TYPE=numeric
SIMPLE_OTP_LENGTH=6
SIMPLE_OTP_TTL=5
SIMPLE_OTP_MAX_ATTEMPTS=3
SIMPLE_OTP_RATE_LIMIT_ENABLED=true
SIMPLE_OTP_RATE_LIMIT_ATTEMPTS=5
SIMPLE_OTP_RATE_LIMIT_DECAY=60use Aslnbxrz\SimpleOTP\Facades\SimpleOTP;
// Generate and send OTP
$result = SimpleOTP::generate('user-123', 'user@example.com');
// Returns: ['success' => true, 'message' => 'OTP code has been sent successfully.', ...]
// Verify OTP
$verified = SimpleOTP::verify('user-123', '123456');
// Returns: true or throws OTPException
// Check if OTP exists
$exists = SimpleOTP::exists('user-123');
// Returns: true/false
// Get OTP information
$info = SimpleOTP::info('user-123');
// Returns: ['identifier' => 'user-123', 'recipient' => 'user@example.com', ...]
// Resend OTP
$result = SimpleOTP::resend('user-123', 'user@example.com');
// Delete OTP
$deleted = SimpleOTP::delete('user-123');<?php
namespace App\Http\Controllers;
use Aslnbxrz\SimpleOTP\Facades\SimpleOTP;
use Aslnbxrz\SimpleOTP\Exceptions\OTPException;
use Illuminate\Http\Request;
class AuthController extends Controller
{
public function sendOTP(Request $request)
{
try {
$identifier = 'user-' . $request->user()->id;
$recipient = $request->user()->phone; // or email
$result = SimpleOTP::generate($identifier, $recipient);
return response()->json([
'success' => true,
'message' => $result['message'],
'expires_at' => $result['expires_at'],
]);
} catch (OTPException $e) {
return response()->json([
'success' => false,
'message' => $e->getMessage(),
], 400);
}
}
public function verifyOTP(Request $request)
{
try {
$identifier = 'user-' . $request->user()->id;
$code = $request->input('code');
$verified = SimpleOTP::verify($identifier, $code);
if ($verified) {
// Mark user as verified or perform other actions
$request->user()->markAsVerified();
return response()->json([
'success' => true,
'message' => 'Phone number verified successfully.',
]);
}
} catch (OTPException $e) {
return response()->json([
'success' => false,
'message' => $e->getMessage(),
], 400);
}
}
}// Custom SMS message
$result = SimpleOTP::generate('user-123', '+1234567890', [
'message' => 'Your verification code for MyApp is: {code}'
]);
// Custom email subject and message
$result = SimpleOTP::generate('user-123', 'user@example.com', [
'subject' => 'Verify Your Account',
'message' => 'Please use the following code to verify your account: {code}'
]);- Uses Laravel's cache system
- Automatic expiration
- High performance
- Suitable for most applications
- Persistent storage
- Better for distributed applications
- Requires migration
- Automatic cleanup available
- High performance
- Distributed caching
- Automatic expiration
- Best for high-traffic applications
Currently supports:
- Twilio: Popular SMS service provider
- Nexmo (Vonage): International SMS service
- Eskiz: Uzbekistan SMS service provider (using native cURL)
- Telegram: Telegram Bot API for instant messaging
- Uses Laravel's mail system
- Customizable templates
- HTML and text versions
- For development and testing
- Logs OTP codes to Laravel logs
- No external dependencies
The package includes built-in rate limiting to prevent abuse:
'rate_limit' => [
'enabled' => true,
'max_attempts' => 5, // Max OTP requests per time window
'decay_minutes' => 60, // Time window in minutes
],Generate different types of OTP codes:
// Numeric (default): 123456
'type' => 'numeric'
// Alphanumeric: A1B2C3
'type' => 'alphanumeric'
// Alpha only: ABCDEF
'type' => 'alpha'Expired OTP codes are automatically cleaned up based on configuration:
'cleanup' => [
'enabled' => true,
'interval_hours' => 24, // Run cleanup every 24 hours
],The package includes comprehensive tests. To run them:
composer testThe package throws specific exceptions that you can catch and handle:
use Aslnbxrz\SimpleOTP\Exceptions\OTPException;
use Aslnbxrz\SimpleOTP\Exceptions\OTPDeliveryException;
try {
SimpleOTP::generate('user-123', 'user@example.com');
} catch (OTPDeliveryException $e) {
// Handle delivery failure (SMS/Email service down, etc.)
Log::error('OTP delivery failed: ' . $e->getMessage());
} catch (OTPException $e) {
// Handle other OTP-related errors
Log::error('OTP error: ' . $e->getMessage());
}- Rate Limiting: Always enable rate limiting in production
- TTL: Use appropriate TTL values (5-10 minutes recommended)
- Max Attempts: Limit verification attempts (3-5 recommended)
- Storage: Use Redis or Database for production (not cache)
- HTTPS: Always use HTTPS in production
- Cleanup: Enable automatic cleanup of expired codes
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for your changes
- Run the test suite
- Submit a pull request
This package is open-sourced software licensed under the MIT license.
For support, please open an issue on the GitHub repository.
- Initial release
- Multiple storage drivers (Cache, Database, Redis)
- Multiple delivery methods (SMS, Email, Log)
- Rate limiting and security features
- Comprehensive test coverage
- Laravel integration with facades and service providers