OTP login
OTP login using SMS, Email, and other methods.
Author:
Taha Moghaddam
Documentation
- Features
- Requirements
- Installation
- Configuration (env)
- Custom OTP sender
- Translations
- Flow
- Security
- Changelog
- License
- Publish to Packagist and GitHub
- Filament
OTP (One-Time Password) login for Filament v5: mobile/phone number login with OTP code, no password, no session (mobile passed in URL). Users can “register” by logging in with OTP for the first time.
#Features
- Mobile + OTP only – No password, no email. User enters mobile → receives OTP → enters code on next page.
- No session for pending state – Mobile number is passed in the URL (e.g.
?mobile=...) to the OTP verification page. - Configurable request block –
request_block_seconds: time (in seconds) before the same mobile can request another OTP. - Same OTP resend – Up to N times (configurable, default 3) the same code is resent; after that a new OTP is generated.
- Pluggable OTP delivery – Implement
OtpSenderInterface(SMS, email, log, etc.) and set it in config. - Filament 5 – Built for Filament v5 panels.
#Requirements
- PHP 8.2+
- Laravel 11 or 12
- Filament 4 or 5
- Livewire 3 or 4
#Installation
composer require taha-moghaddam/filament-otp-login
#1. User model and table
Your user model (e.g. App\Models\AdminUser) must have at least:
- A mobile column (name configurable, default:
mobile) – unique, stored as string or bigInteger. - OTP fields:
otp_code(nullable int),otp_expires_at(nullable timestamp),otp_sent_count(int, default 0),mobile_verified_at(nullable timestamp).
Example migration for admin_users:
Schema::create('admin_users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->unsignedBigInteger('mobile')->unique();
$table->timestamp('mobile_verified_at')->nullable();
$table->unsignedSmallInteger('otp_code')->nullable();
$table->timestamp('otp_expires_at')->nullable();
$table->unsignedTinyInteger('otp_sent_count')->default(0);
$table->rememberToken();
$table->timestamps();
});
#2. Publish and run migrations (OTP logs table)
php artisan vendor:publish --tag="filament-otp-login-migrations"
php artisan migrate
#3. Publish config (optional)
php artisan vendor:publish --tag="filament-otp-login-config"
Then edit config/filament-otp-login.php:
- user_model – Your user model class (e.g.
App\Models\AdminUser). - otp_log.table – Table name for OTP logs (default:
admin_otp_logs). - otp_log.user_foreign_key – Foreign key to users (default:
admin_user_id). - mobile_column – Column name for mobile number (default:
mobile). - sender – Class implementing
FilamentOtpLogin\Contracts\OtpSenderInterface(default:LogOtpSender– logs to Laravel log). - otp_length, otp_expires_seconds, request_block_seconds, same_otp_max_sends – Behaviour and limits.
#4. Register the plugin on your panel
In your Filament panel provider (e.g. AdminPanelProvider):
use FilamentOtpLogin\FilamentOtpLoginPlugin;
public function panel(Panel $panel): Panel
{
return $panel
->id('admin')
->path('admin')
->authGuard('admin') // use the guard that uses your user model
->plugins([
FilamentOtpLoginPlugin::make(),
])
// ... rest of panel config
;
}
Do not call ->login() or ->registration() on the panel; the plugin sets the login page and disables registration.
#5. Auth guard (optional)
Ensure config/auth.php has a guard that uses your user model, e.g.:
'guards' => [
'admin' => [
'driver' => 'session',
'provider' => 'admin_users',
],
],
'providers' => [
'admin_users' => [
'driver' => 'eloquent',
'model' => App\Models\AdminUser::class,
],
],
#Configuration (env)
| Key | Description | Default |
|---|---|---|
FILAMENT_OTP_LOGIN_USER_MODEL |
User model class | App\Models\AdminUser |
FILAMENT_OTP_LOGIN_OTP_LOG_TABLE |
OTP logs table name | admin_otp_logs |
FILAMENT_OTP_LOGIN_USER_FOREIGN_KEY |
FK column in OTP logs | admin_user_id |
FILAMENT_OTP_LOGIN_MOBILE_COLUMN |
Mobile column on user | mobile |
FILAMENT_OTP_LOGIN_SENDER |
OTP sender class | FilamentOtpLogin\Services\LogOtpSender |
FILAMENT_OTP_LOGIN_OTP_LENGTH |
OTP code length | 6 |
FILAMENT_OTP_LOGIN_OTP_EXPIRES_SECONDS |
OTP validity (seconds) | 120 |
FILAMENT_OTP_LOGIN_REQUEST_BLOCK_SECONDS |
Block before next request (seconds) | 60 |
FILAMENT_OTP_LOGIN_SAME_OTP_MAX_SENDS |
Same code resend limit | 3 |
#Custom OTP sender
Implement FilamentOtpLogin\Contracts\OtpSenderInterface:
use FilamentOtpLogin\Contracts\OtpSenderInterface;
use Illuminate\Contracts\Auth\Authenticatable;
class MySmsOtpSender implements OtpSenderInterface
{
public function send(Authenticatable $user, string $code): void
{
$mobile = $user->mobile; // or $user->{config('filament-otp-login.mobile_column')}
// Send SMS to $mobile with $code
}
}
Register it in config:
'sender' => \App\Services\MySmsOtpSender::class,
#Translations
Publish and edit translations:
php artisan vendor:publish --tag="filament-otp-login-translations"
Keys are under resources/lang/vendor/filament-otp-login/ (e.g. en/filament-otp-login.php). English and Persian (fa) are included in the package.
#Flow
- User opens panel login → sees mobile field only.
- Submits mobile → rate limit checked (
request_block_seconds), user found or created, OTP generated or same code resent (up tosame_otp_max_sends), OTP sent viaOtpSenderInterface, redirect to phone-verification-prompt?mobile=.... - User enters OTP on verification page → code validated, user logged in, redirect to panel.
No session is used for the “pending” user; the mobile in the URL identifies the user on the OTP page.
#Security
- Use HTTPS in production.
- Rate limiting is applied per mobile (
request_block_seconds). - OTP logs table can be pruned (e.g. scheduled job) to avoid bloat.
#Changelog
See Releases.
#License
MIT. See LICENSE.md.
#Publish to Packagist and GitHub
- Create a new repository on GitHub (e.g.
taha-moghaddam/filament-otp-login). - Push the
filament-otp-loginfolder to GitHub. - Register the package on Packagist and link your GitHub repo.
- Submit to the Filament plugin directory so it appears on the Filament site.
#Filament
This plugin is built for Filament v5.
Featured Plugins
A selection of plugins curated by the Filament team
Custom Dashboards
Let your users build and share their own dashboards with a drag-and-drop interface. Define your data sources in PHP and let them do the rest.
Filament
Custom Fields
Eliminate custom field migrations forever. Let your users create and manage form fields directly in Filament admin panels with 20+ built-in field types, validation, and zero database changes.
Relaticle
Data Lens
Advanced Data Visualization for Laravel Filament - a premium reporting solution enabling custom column creation, sophisticated filtering, and enterprise-grade data insights within admin panels.
Padmission