aslnbxrz/simple-otp

A simple and flexible OTP (One-Time Password) management package for Laravel with multiple storage drivers and delivery methods

Installs: 4

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/aslnbxrz/simple-otp

v1.0.0 2025-10-09 16:17 UTC

This package is auto-updated.

Last update: 2025-10-09 16:33:41 UTC


README

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.

Features

  • ๐Ÿš€ 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

Installation

Via Composer

composer require aslnbxrz/simple-otp

Publish Configuration

php artisan vendor:publish --provider="Aslnbxrz\\SimpleOTP\\SimpleOTPServiceProvider" --tag="simple-otp-config"

Publish Migrations (for database storage)

php artisan vendor:publish --provider="Aslnbxrz\\SimpleOTP\\SimpleOTPServiceProvider" --tag="simple-otp-migrations"
php artisan migrate

Publish Email Views (optional)

php artisan vendor:publish --provider="Aslnbxrz\\SimpleOTP\\SimpleOTPServiceProvider" --tag="simple-otp-views"

Configuration

The package configuration file will be published to config/simple-otp.php. Here's an overview of the main configuration options:

Storage Configuration

// 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 Configuration

'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 Configuration

'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),
    ],
],

Environment Variables

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=60

Usage

Basic Usage with Facade

use 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');

Usage in Controllers

<?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/Email Messages

// 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}'
]);

Storage Drivers

Cache Storage (Default)

  • Uses Laravel's cache system
  • Automatic expiration
  • High performance
  • Suitable for most applications

Database Storage

  • Persistent storage
  • Better for distributed applications
  • Requires migration
  • Automatic cleanup available

Redis Storage

  • High performance
  • Distributed caching
  • Automatic expiration
  • Best for high-traffic applications

Delivery Drivers

SMS Driver

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

Email Driver

  • Uses Laravel's mail system
  • Customizable templates
  • HTML and text versions

Log Driver

  • For development and testing
  • Logs OTP codes to Laravel logs
  • No external dependencies

Advanced Features

Rate Limiting

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
],

Custom Code Types

Generate different types of OTP codes:

// Numeric (default): 123456
'type' => 'numeric'

// Alphanumeric: A1B2C3
'type' => 'alphanumeric'

// Alpha only: ABCDEF
'type' => 'alpha'

Automatic Cleanup

Expired OTP codes are automatically cleaned up based on configuration:

'cleanup' => [
    'enabled' => true,
    'interval_hours' => 24, // Run cleanup every 24 hours
],

Testing

The package includes comprehensive tests. To run them:

composer test

Error Handling

The 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());
}

Security Considerations

  1. Rate Limiting: Always enable rate limiting in production
  2. TTL: Use appropriate TTL values (5-10 minutes recommended)
  3. Max Attempts: Limit verification attempts (3-5 recommended)
  4. Storage: Use Redis or Database for production (not cache)
  5. HTTPS: Always use HTTPS in production
  6. Cleanup: Enable automatic cleanup of expired codes

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests for your changes
  5. Run the test suite
  6. Submit a pull request

License

This package is open-sourced software licensed under the MIT license.

Support

For support, please open an issue on the GitHub repository.

Changelog

v1.0.0

  • 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