wotz/laravel-verification-code
Laravel package for generating and validating verification codes with configurable length, expiry, attempt limits, and rate limiting. Supports email or SMS delivery, custom storage drivers, and simple APIs for sending and checking codes.
Install the package and its migrations:
composer require wotz/laravel-verification-code
php artisan vendor:publish --provider="Wotz\VerificationCode\VerificationCodeServiceProvider"
php artisan migrate
Publishing typically exposes a verification.php config file and a migration for a verification_codes table. The first place to look is the config file—code length, expiry, and notification channel are usually defined there.
Add the package trait to any authenticatable model (usually App\Models\User):
use Wotz\VerificationCode\Traits\HasVerificationCodes;
class User extends Authenticatable
{
use HasVerificationCodes;
}
The first practical use case is passwordless login or phone verification: a user submits a phone number or email, your app sends a short code, the user submits the code, and you log them in (or mark the contact as verified). Start by wiring a single endpoint to send a code and another to verify it—then expand to 2FA or registration flows.
Sending and verifying codes
The package usually exposes methods on the trait or a facade. Typical day-to-day calls:
// Send a code over the configured channel (sms, email, etc.)
$user->sendVerificationCode();
// Verify a submitted code
if ($user->verifyCode($request->code)) {
Auth::login($user);
}
If the package provides a facade, you may also see VerificationCode::send($user) and VerificationCode::verify($user, $code). Keep the send and verify logic in a dedicated controller or action class so you can rate-limit and log attempts.
Passwordless login flow
POST /auth/code – validate identifier, find or create user, call sendVerificationCode().POST /auth/verify – validate code, call verifyCode(), then Auth::login($user) and regenerate the session.Two-factor authentication
After a successful password check, don’t log the user in immediately. Instead, store a temporary “2FA pending” session flag, send a code, and only call Auth::login() after verifyCode() succeeds. This keeps the package focused on code generation while you own the auth state.
Custom notifications
Most packages let you swap the notification class in config. Point it to your own class that extends the package’s notification or implements Laravel’s ShouldQueue and via() methods. This is where you integrate Twilio, Nexmo, or a local SMS gateway.
// config/verification.php
'notification' => App\Notifications\CustomVerificationCode::class,
Rate limiting and throttling
Wrap send and verify endpoints with Laravel’s throttle middleware. For finer control, use RateLimiter::attempt() keyed by user ID or IP. A common pattern is 1 send per minute and 5 verify attempts per 10 minutes.
Multi-guard and multi-model
Because the trait is model-agnostic, you can use it on User, Admin, or any authenticatable model. If you use multiple models, ensure the verification_codes table uses a morph relation (or separate tables) and that your verification logic scopes codes to the correct model instance.
Testing
Use Notification::fake() to assert the code notification was sent, and Carbon::setTestNow() to test expiry. If the package stores a hashed code, you can’t assert the plain code from the database—assert the notification payload instead.
config/verification.php, run php artisan config:clear. In production, always config:cache after deployment.VerificationCode::where('expires_at', '<', now())->delete()) in app/Console/Kernel.php.User model must have the correct routeNotificationForSms() or routeNotificationForMail() method. Missing this is the most common “code never arrives” bug.Carbon::setTestNow() to fast-forward expiry. If the package uses now() internally, this works; if it uses time(), you may need to freeze time differently.VerificationCode model or wrap the trait methods in your own service.composer.json constraints against your Laravel and PHP versions. If it lags behind a new Laravel release, you may need to fork or override the service provider.How can I help you explore Laravel packages today?