prasanth-j/otpify
Composer 安装命令:
composer require prasanth-j/otpify
包简介
A secure, flexible OTP package for Laravel. Supports database and cache drivers, multiple purposes, hashed storage, events, and validation rules.
README 文档
README
A secure, flexible OTP (One-Time Password) package for Laravel. Supports database and cache storage drivers, multiple purposes per identifier, hashed token storage, events, and a ready-made validation rule.
Requirements
- PHP 8.1+
- Laravel 9, 10, 11, 12, or 13
Each Laravel major has its own PHP range and support window (per Laravel's official support policy), so the actual floor depends on which Laravel version you're on:
| Laravel | PHP range | Security fixes until |
|---|---|---|
| 9 | 8.0 – 8.2 (this package requires 8.1+) | Feb 2024 — EOL |
| 10 | 8.1 – 8.3 | Feb 2025 — EOL |
| 11 | 8.2 – 8.4 | Mar 2026 — EOL |
| 12 | 8.2 – 8.5 | Feb 2027 |
| 13 | 8.3 – 8.5 | Mar 2028 |
Laravel 9, 10, and 11 no longer receive security fixes upstream. This package still works on them, but for new projects prefer Laravel 12 or 13.
Installation
composer require prasanth-j/otpify
Publish the config file:
php artisan vendor:publish --tag=otpify-config
If you plan to use the database driver (the default), publish and run the migration:
php artisan vendor:publish --tag=otpify-migrations php artisan migrate
Configuration
config/otpify.php:
return [ 'driver' => env('OTPIFY_DRIVER', 'database'), 'digits' => env('OTPIFY_DIGITS', 6), 'validity' => env('OTPIFY_VALIDITY', 10), // minutes 'type' => env('OTPIFY_TYPE', 'numeric'), // numeric|alpha|alphanumeric 'cache' => [ 'prefix' => 'otpify', 'store' => env('OTPIFY_CACHE_STORE', null), // null = default cache store ], ];
| Key | Description |
|---|---|
driver |
database or cache. |
digits |
Default OTP length (4-8). Can be overridden per call. |
validity |
Default expiry time in minutes. Can be overridden per call. |
type |
Default character set: numeric, alpha, or alphanumeric. |
cache.prefix |
Prefix used to build cache keys (otpify:{identifier}:{purpose}). |
cache.store |
Which cache store to use. null uses your app's default store. |
Security
- OTPs are generated with
random_int(), nevermt_rand(). - Only the SHA-256 hash of the OTP is stored (
hash('sha256', $token)) — the plaintext is never persisted. - Validation compares hashes with
hash_equals()for constant-time comparison. - The plaintext token is only ever returned once, from
generate()/resend(), so you can send it via email/SMS/etc.validate()never returns the token. - OTPs are single-use: a successful
validate()call immediately invalidates the OTP.
Usage
Generate an OTP
use PrasanthJ\Otpify\Facades\Otpify; $result = Otpify::generate('user@example.com'); // purpose defaults to "default" $result->token; // e.g. "482913" — send this to the user $result->expiresAt; // Carbon instance $result->status; // "generated"
With a purpose and custom options:
$result = Otpify::generate('user@example.com', 'login', [ 'digits' => 4, 'validity' => 5, // minutes 'type' => 'alphanumeric', ]);
Generating a new OTP for the same identifier + purpose deletes any previous one — only the latest OTP for a given identifier/purpose is ever valid.
Validate an OTP
$result = Otpify::validate('user@example.com', $token, 'login'); if ($result->isValid()) { // proceed }
OtpResult status values and helpers:
| Status | Helper | Meaning |
|---|---|---|
valid |
isValid() |
Token matched, unused, not expired. |
invalid |
isInvalid() |
Token does not match. |
expired |
isExpired() |
Token matched an OTP that has expired. |
already_used |
isAlreadyUsed() |
OTP was already validated once. |
not_found |
— | No OTP exists for that identifier/purpose. |
generated |
wasGenerated() |
Returned only by generate()/resend(). |
Invalidate an OTP
Otpify::invalidate('user@example.com', 'login'); // returns bool
Resend an OTP
Invalidates any existing OTP for the identifier/purpose and generates a fresh one:
$result = Otpify::resend('user@example.com', 'login', ['validity' => 10]);
Multiple purposes
Every method accepts a purpose so the same identifier can hold independent OTPs at once, e.g.
'login', '2fa', 'password-reset'.
Otpify::generate('user@example.com', 'login'); Otpify::generate('user@example.com', '2fa');
Validation rule
Use OtpRule to validate an OTP as part of a normal Laravel form request or validator:
use Illuminate\Support\Facades\Validator; use PrasanthJ\Otpify\Rules\OtpRule; $validator = Validator::make($request->all(), [ 'otp' => ['required', new OtpRule($request->input('email'), 'login')], ]);
Events
| Event | Fired when |
|---|---|
PrasanthJ\Otpify\Events\OtpGenerated |
An OTP is generated. Has identifier, purpose, token, expiresAt. |
PrasanthJ\Otpify\Events\OtpValidated |
An OTP passes validation. Has identifier, purpose. |
PrasanthJ\Otpify\Events\OtpFailed |
An OTP fails validation. Has identifier, purpose, reason (the failure status). |
use PrasanthJ\Otpify\Events\OtpGenerated; Event::listen(function (OtpGenerated $event) { // send $event->token to $event->identifier via SMS/email });
Drivers
Database (default)
Stores hashed OTPs in the otpify_tokens table. Requires the migration to be run. Good default
choice — durable, works with the otpify:clean command.
Cache
Stores hashed OTPs in your configured cache store under otpify:{identifier}:{purpose}, using the
OTP's expiry as the cache TTL. No migration needed; good fit if you already run Redis/Memcached and
don't need a durable audit trail.
OTPIFY_DRIVER=cache OTPIFY_CACHE_STORE=redis
Rate limiting
Otpify does not rate limit generation/validation attempts itself — pair it with Laravel's
RateLimiter:
use Illuminate\Support\Facades\RateLimiter; use PrasanthJ\Otpify\Facades\Otpify; $key = 'otp-generate:' . $request->ip() . ':' . $request->input('email'); if (RateLimiter::tooManyAttempts($key, 5)) { abort(429, 'Too many OTP requests. Please try again later.'); } RateLimiter::hit($key, 60); // 1 attempt per minute window, 5 max $result = Otpify::generate($request->input('email'));
Do the same around Otpify::validate() to slow down brute-force guessing attempts.
Cleaning up expired tokens
For the database driver, expired and used rows accumulate over time. Clean them up with:
php artisan otpify:clean
Schedule it in routes/console.php (Laravel 11+) or app/Console/Kernel.php (Laravel 9/10):
Schedule::command('otpify:clean')->daily();
This command is a no-op (with a warning) when the cache driver is active, since cache entries
expire on their own via TTL.
Migrating from v1
v2.0.0 is a full rewrite and breaking change:
- The
otpstable andOtpmodel are gone, replaced by theotpify_tokenstable (no model). Otpify::generate()/validate()now return anOtpResultobject instead of an array.Otpify::generate()signature changed fromgenerate(string $identifier, int $userId = null, string $otpType = null, int $digits = null, int $validity = null)togenerate(string $identifier, string $purpose = 'default', array $options = []).otpTypeis nowpurpose, and is a required-shaped concept everywhere (defaults to'default').- OTPs are now stored hashed (SHA-256) instead of in plaintext.
user_idis no longer stored — key OTPs by whatever identifier makes sense for you (email, phone, user ID as a string, etc.).
If you have existing data in the old otps table, write a one-off migration to hash and copy
identifier/otp_type/token/expiry data into otpify_tokens, or simply let outstanding OTPs
expire naturally and drop the old table.
Development
composer test # run the Pest test suite composer lint # fix code style with Laravel Pint composer lint:test # check code style without fixing composer analyse # run static analysis with Larastan
Larastan 3.x requires PHP 8.2+ locally to run static analysis, even though the package itself supports PHP 8.1+.
License
MIT
prasanth-j/otpify 适用场景与选型建议
prasanth-j/otpify 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 39 次下载、GitHub Stars 达 2, 最近一次更新时间为 2022 年 08 月 05 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「One Time Password」 「laravel otp」 「otpify」 「otp validation」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 prasanth-j/otpify 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 prasanth-j/otpify 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 prasanth-j/otpify 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Tools to convert between .NET and PHP date formats
Adds the EDTF data type to Wikibase
The PHP class PasswordGenerator serves as a password generator to create memorable passwords like the keychain of macOS ≤ 10.14 did.
Date component is a set of methods to help with the manipulation of dates.
Laravel middleware to restrict a site or specific routes using HTTP basic authentication
High-level cryptographic primitives and security utilities for Maatify systems including password hashing, reversible encryption, HKDF key derivation, and key rotation.
统计信息
- 总下载量: 39
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 2
- 点击次数: 11
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2022-08-05