Installation:
composer require spykapps/passwordless-login
php artisan vendor:publish --tag=passwordless-login-config
php artisan vendor:publish --tag=passwordless-login-migrations
php artisan migrate
Add Trait to User Model:
use SpykApp\PasswordlessLogin\Traits\HasMagicLogin;
class User extends Authenticatable
{
use HasMagicLogin;
}
Send Magic Link in Controller:
use SpykApp\PasswordlessLogin\Facades\PasswordlessLogin;
public function sendMagicLink(Request $request)
{
$request->validate(['email' => 'required|email']);
$user = User::where('email', $request->email)->first();
if (!$user) {
return back()->with('status', __('passwordless-login::messages.link_sent_if_exists'));
}
try {
$user->sendMagicLink($request);
} catch (\SpykApp\PasswordlessLogin\Exceptions\ThrottleException $e) {
return back()->with('error', $e->getMessage());
}
return back()->with('status', __('passwordless-login::messages.link_sent'));
}
Configure Routes (auto-registered):
Ensure your routes/web.php includes the guest middleware for the /magic-login/{token} route.
Replace traditional login forms with a passwordless flow for user onboarding or guest access. Example:
// In a registration controller
$user = User::create($validatedData);
$user->sendMagicLink($request);
return redirect()->route('verify.email');
Magic Link Generation:
// Fluent API for full control
$result = PasswordlessLogin::forUser($user)
->guard('admin')
->expiresIn(30)
->maxUses(1)
->generate($request);
// Or via trait (simplest)
$user->sendMagicLink($request);
Multi-Channel Delivery:
// Generate without email for SMS/WhatsApp
$result = PasswordlessLogin::forUser($user)
->withoutNotification()
->generate($request);
// Send via Twilio/SMS
$this->smsService->send($user->phone, $result['url']);
Conditional Authentication:
// In config/passwordless-login.php
'conditions' => [
fn($user) => $user->is_active,
\App\Auth\CheckSubscription::class,
];
Post-Login Actions:
// In config/passwordless-login.php
'after_login_action' => \App\Actions\UpdateLastLogin::class;
API Integration:
Use withoutNotification() for API-based magic links, then manually send the URL via API response:
{ "magic_link": "https://your-app.com/magic-login/abc123" }
Custom Notifications: Extend the default notification by publishing views:
php artisan vendor:publish --tag=passwordless-login-views
Then override resources/views/vendor/passwordless-login/email.blade.php.
Rate Limiting: Customize throttling in config:
'throttle' => [
'max_attempts' => 3,
'decay_minutes' => 5,
],
Bot Mitigation:
Use the javascript strategy for auto-redirects (requires JS):
'bot_detection' => [
'strategy' => 'javascript',
],
Testing:
Mock the PasswordlessLogin facade in tests:
$this->partialMock(PasswordlessLogin::class, function ($mock) {
$mock->shouldReceive('forUser')
->andReturnSelf()
->shouldReceive('generate')
->andReturn(['url' => 'test-url']);
});
Token Expiry:
'expiry_minutes' => 60, // 1 hour
passwordless_login_tokens table for expires_at timestamps.Bot Detection False Positives:
strategy or disable temporarily:
'bot_detection' => ['enabled' => false],
Event::listen(\SpykApp\PasswordlessLogin\Events\BotDetected::class, function ($event) {
Log::debug('Bot detected:', $event->request->userAgent());
});
Rate Limiting:
try {
$user->sendMagicLink($request);
} catch (ThrottleException $e) {
return back()->with('error', 'Too many requests. Try again later.');
}
Token Security:
'security' => [
'ip_binding' => true,
'user_agent_binding' => true,
],
'token' => ['length' => 64],
Event Order:
MagicLinkClicked fires before MagicLinkAuthenticated. Use the former for bot checks:
Event::listen(MagicLinkClicked::class, function ($event) {
if ($event->isBotDetected) {
abort(403, 'Access denied.');
}
});
Token Validation:
Check the used_at and expires_at columns in the tokens table. Manually invalidate tokens with:
$token = \SpykApp\PasswordlessLogin\Models\MagicLoginToken::find($id);
$token->delete(); // Or set `used_at = now()`.
Route Issues:
Ensure the /magic-login/{token} route is not overridden. Verify with:
php artisan route:list | grep magic-login
Notification Failures:
Check the failed_jobs table if emails aren’t sent. Queue notifications explicitly:
'notification' => ['queue' => true],
Custom Guard:
If using a custom guard (e.g., admin), ensure it’s registered in auth.php:
'guards' => [
'admin' => ['driver' => 'session', 'provider' => 'admins'],
],
Custom Token Model:
Extend \SpykApp\PasswordlessLogin\Models\MagicLoginToken and bind it in config:
'model' => \App\Models\CustomMagicLoginToken::class,
Dynamic Conditions: Add runtime conditions via middleware:
public function handle($request, Closure $next)
{
if ($request->has('temp_access')) {
config(['passwordless-login.conditions' => [
fn($user) => $user->isTempAccessAllowed(),
]]);
}
return $next($request);
}
Token Metadata: Attach custom data to tokens for tracking:
$result = PasswordlessLogin::forUser($user)
->withMetadata(['campaign' => 'black_friday'])
->generate($request);
Access later via $token->metadata.
Override Views: Publish and customize:
php artisan vendor:publish --tag=passwordless-login-views
Override:
resources/views/vendor/passwordless-login/email.blade.phpresources/views/vendor/passwordless-login/confirmation.blade.php (for bot detection).Audit Logging:
Extend the audit log by implementing \SpykApp\PasswordlessLogin\Contracts\AuditLogger:
class SlackAuditLogger implements AuditLogger
{
public function log($event, $data)
{
// Send to Slack
}
}
Bind in config:
'audit_log' => \App\Services\SlackAuditLogger::class,
How can I help you explore Laravel packages today?