spykapps/filament-passwordless-login

A highly customizable Filament 4 plugin for passwordless magic link authentication with login page, actions, resource management, and analytics widgets.

Maintainers

Package info

github.com/SpykApp/filament-passwordless-login

Homepage

pkg:composer/spykapps/filament-passwordless-login

Statistics

Installs: 139

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.1 2026-02-25 17:09 UTC

This package is auto-updated.

Last update: 2026-02-25 17:09:59 UTC


README

Screenshot

Packagist Version Total Downloads Laravel 12 PHP 8.3 License

Filament Passwordless Login

A highly customizable Filament 4 & 5 plugin for passwordless (magic link) authentication โ€” built on top of spykapps/passwordless-login.

Features

  • ๐Ÿ” Magic Link Login Page : Extends Filament's native login โ€” no custom views needed
  • โšก Reusable Action : Modal/slide-over action to send magic links from anywhere
  • ๐Ÿ’ก Login Action : Configurable as email field hint or button after login form
  • ๐Ÿ“Š Resource Widgets : Stats overview, line charts, and top users table on the resource page
  • ๐Ÿ—‚๏ธ Token Resource : Full Filament resource to manage, generate, and invalidate tokens
  • ๐ŸŒ Multilingual : 8 languages included (en, es, fr, de, nl, ar, hi, pt)
  • ๐Ÿ“ง Custom Mailable / Notification : Use your own email templates via fluent API
  • โš™๏ธ Fully Configurable : Everything via plugin fluent API, config file, or language files

Requirements

  • PHP 8.1+
  • Laravel 10, 11, or 12
  • Filament 4.x, 5.x
  • spykapps/passwordless-login ^1.0

Installation

1. Install the packages

composer require spykapps/filament-passwordless-login

This will also install spykapps/passwordless-login as a dependency.

2. Set up the base passwordless-login package

If you haven't already set up the base package, publish and run the migrations:

php artisan vendor:publish --tag=passwordless-login-config
php artisan vendor:publish --tag=passwordless-login-migrations
php artisan migrate

Important: The passwordless_login_tokens table must exist before using this plugin. If you've already run the migration, skip this step.

3. Add the trait to your User model

use SpykApp\PasswordlessLogin\Traits\HasMagicLogin;

class User extends Authenticatable
{
    use HasMagicLogin;
}

4. Register the plugin

use SpykApp\FilamentPasswordlessLogin\FilamentPasswordlessLoginPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugin(
            FilamentPasswordlessLoginPlugin::make()
        );
}

That's it! The plugin will replace the login page, register the token resource with widgets.

5. Publish plugin config (optional)

php artisan vendor:publish --tag=filament-passwordless-login-config

6. Publish language files (optional)

php artisan vendor:publish --tag=filament-passwordless-login-lang

Plugin Configuration (Full Reference)

All options can be set fluently in the plugin registration:

use SpykApp\FilamentPasswordlessLogin\FilamentPasswordlessLoginPlugin;
use SpykApp\FilamentPasswordlessLogin\Enums\FilamentPasswordlessLoginActionPosition;

FilamentPasswordlessLoginPlugin::make()

    // โ”€โ”€ Login Page โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    ->loginPage()                                 // Enable magic link login (default: true)
    ->loginPage(false)                            // Disable โ€” keep default Filament password login
    ->login(MyCustomLoginPage::class)             // Use your own custom login page class
    ->showPasswordLoginLink()                     // Show "Back to password login" link
    ->showPasswordLoginLink(false)                // Hide it
    
    // โ”€โ”€ Redirect URLs โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    ->redirectUrl('/admin/dashboard')             // Where to go after login (default: auto-detects panel URL)
    ->failureUrl('/admin/login')                  // Where to go on expired/invalid link (default: auto-detects panel login URL)

    // โ”€โ”€ Login Action โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    ->loginAction()                               // Enable login action (default: false)
    ->loginAction(false)                          // Disable
    ->loginActionPosition(                        // Where to show the action
        FilamentPasswordlessLoginActionPosition::EmailFieldHint      // As hint on email field
        // or
        FilamentPasswordlessLoginActionPosition::LoginFormEndButton  // As button after login form
    )
    ->loginActionIcon('heroicon-m-sparkles')      // Custom icon
    ->loginActionColor('warning')                 // Custom color

    // โ”€โ”€ Action Modal โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    ->slideover()                                 // Open action modal as slide-over
    ->slideover(false)                            // Open as centered modal (default)

    // โ”€โ”€ Custom Mailable / Notification โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    ->mailable(\App\Mail\MyMagicLinkMail::class)              // Custom mailable class
    ->notification(\App\Notifications\MyMagicLinkNotif::class) // Custom notification class

    // โ”€โ”€ Resource โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    ->resource()                                  // Enable token resource (default: true)
    ->resource(false)                             // Disable
    ->canCreateTokens()                           // Allow manual token creation (default: true)
    ->canCreateTokens(false)                      // Disable create
    ->canDeleteTokens()                           // Allow deletion (default: true)
    ->canDeleteTokens(false)                      // Disable delete
    ->resourceSlug('magic-links')                 // Custom URL slug
    ->navigationGroup('Security')                 // Custom nav group (or use lang file)
    ->navigationIcon('heroicon-o-key')            // Custom nav icon
    ->navigationSort(50)                          // Custom sort order

    // โ”€โ”€ Widgets (shown on resource page) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    ->statsWidget()                               // Enable stats widget (default: true)
    ->statsWidget(false)                          // Disable
    ->chartsWidget()                              // Enable chart widgets (default: true)
    ->chartsWidget(false)                         // Disable
    ->chartDays(60)                               // Custom chart time range in days

Configuration Priority

Settings follow this priority order: Plugin fluent API โ†’ Config file โ†’ Language file โ†’ Hardcoded default

Setting Plugin API Config Lang Default
Icon ->loginActionIcon() login_action.icon โ€” heroicon-m-link
Color ->loginActionColor() login_action.color โ€” primary
Slideover ->slideover() login_action.slideover โ€” false
Nav Group ->navigationGroup() โ€” navigation_group Authentication
Nav Label โ€” โ€” navigation_label Magic Links
Mailable ->mailable() โ€” โ€” Base package default
Notification ->notification() โ€” โ€” Base package default
Redirect URL ->redirectUrl() passwordless-login.redirect.on_success โ€” filament()->getUrl()
Failure URL ->failureUrl() passwordless-login.redirect.on_failure โ€” filament()->getLoginUrl()

Login Page

The plugin extends Filament's native Filament\Pages\Auth\Login โ€” no custom views or Blade templates needed.

The form shows only an email field. On submit, a magic link is sent and the page switches to a "Check your email!" confirmation state.

Default Setup (replaces Filament login)

FilamentPasswordlessLoginPlugin::make()
    ->loginPage()

Keep Password Login Available

FilamentPasswordlessLoginPlugin::make()
    ->loginPage()
    ->showPasswordLoginLink()

Disable (keep default Filament login)

FilamentPasswordlessLoginPlugin::make()
    ->loginPage(false)

Redirect URLs

By default, the plugin auto-detects redirect URLs from the current Filament panel โ€” no configuration needed in most cases.

URL Default (auto-detected) Description
Redirect URL filament()->getUrl() (e.g. /admin) Where the user goes after clicking the magic link
Failure URL filament()->getLoginUrl() (e.g. /admin/login) Where the user goes when a link is expired, invalid, or used

Override via Plugin

FilamentPasswordlessLoginPlugin::make()
    ->redirectUrl('/admin/dashboard')
    ->failureUrl('/admin/login?error=expired')

Priority

  1. Plugin fluent API โ€” ->redirectUrl() / ->failureUrl()
  2. Base package config โ€” passwordless-login.redirect.on_success / on_failure
  3. Auto-detected โ€” Current Filament panel URL / login URL

Multi-Panel Setup

Each panel automatically uses its own URLs. No extra configuration needed:

// Admin panel โ†’ redirects to /admin after login
class AdminPanelProvider extends PanelProvider
{
    public function panel(Panel $panel): Panel
    {
        return $panel
            ->path('admin')
            ->plugin(FilamentPasswordlessLoginPlugin::make());
    }
}

// App panel โ†’ redirects to /app after login
class AppPanelProvider extends PanelProvider
{
    public function panel(Panel $panel): Panel
    {
        return $panel
            ->path('app')
            ->plugin(FilamentPasswordlessLoginPlugin::make());
    }
}

Explicit Per-Panel Redirect

// Admin panel
FilamentPasswordlessLoginPlugin::make()
    ->redirectUrl('/admin/dashboard')
    ->failureUrl('/admin/login')

// App panel
FilamentPasswordlessLoginPlugin::make()
    ->redirectUrl('/app/home')
    ->failureUrl('/app/login')

Custom Login Page

Extend the plugin's login page:

<?php

namespace App\Filament\Pages\Auth;

use SpykApp\FilamentPasswordlessLogin\Pages\MagicLinkLogin;
use Filament\Schemas\Schema;
use Filament\Forms\Components\TextInput;
use Illuminate\Contracts\Support\Htmlable;

class MyMagicLogin extends MagicLinkLogin
{
    public function getHeading(): string|Htmlable
    {
        return 'Welcome! Sign in securely.';
    }

    public function getSubheading(): string|Htmlable|null
    {
        if ($this->magicLinkSent) {
            return 'A secure link has been sent to your inbox.';
        }

        return 'No password needed โ€” we\'ll email you a login link.';
    }
}

Register it:

FilamentPasswordlessLoginPlugin::make()
    ->login(\App\Filament\Pages\Auth\MyMagicLogin::class)

Login Action

The SendMagicLinkAction can be used in two positions on the login page, or standalone anywhere in your panel.

Position: Email Field Hint

Adds a clickable hint icon next to the email field on the default Filament password login page:

FilamentPasswordlessLoginPlugin::make()
    ->loginPage(false)       // Keep the default password login
    ->loginAction()          // Enable the action
    ->loginActionPosition(FilamentPasswordlessLoginActionPosition::EmailFieldHint)

Position: Button After Login Form

Renders a button below the sign-in button on the default Filament password login page:

FilamentPasswordlessLoginPlugin::make()
    ->loginPage(false)       // Keep the default password login
    ->loginAction()          // Enable the action
    ->loginActionPosition(FilamentPasswordlessLoginActionPosition::LoginFormEndButton)

Customizing the Login Action

FilamentPasswordlessLoginPlugin::make()
    ->loginAction()
    ->loginActionIcon('heroicon-m-envelope')
    ->loginActionColor('success')
    ->slideover()            // Open modal as slide-over

Using SendMagicLinkAction Standalone

Use SendMagicLinkAction anywhere in your Filament panel โ€” page headers, table actions, form hint actions:

Page Header Action

use SpykApp\FilamentPasswordlessLogin\Actions\SendMagicLinkAction;

protected function getHeaderActions(): array
{
    return [
        SendMagicLinkAction::make(),
    ];
}

Table Row Action

use SpykApp\FilamentPasswordlessLogin\Actions\SendMagicLinkAction;

public static function table(Table $table): Table
{
    return $table
        ->columns([...])
        ->actions([
            SendMagicLinkAction::make()
                ->fillForm(fn ($record) => ['email' => $record->email]),
        ]);
}

Form Field Hint Action

use SpykApp\FilamentPasswordlessLogin\Actions\SendMagicLinkAction;

TextInput::make('email')
    ->email()
    ->hintAction(SendMagicLinkAction::make())

As a Slide-over

SendMagicLinkAction::make()->asSlideover()

Custom Mailable

Send magic links using your own Mailable class instead of the default:

1. Create your Mailable

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class CustomMagicLinkMail extends Mailable
{
    use Queueable, SerializesModels;

    public function __construct(
        public string $url,
        public int $expiryMinutes,
    ) {}

    public function envelope(): Envelope
    {
        return new Envelope(
            subject: 'Your Secure Login Link',
        );
    }

    public function content(): Content
    {
        return new Content(
            markdown: 'emails.magic-link',
            with: [
                'url' => $this->url,
                'expiryMinutes' => $this->expiryMinutes,
            ],
        );
    }
}

2. Create the Blade template

{{-- resources/views/emails/magic-link.blade.php --}}
<x-mail::message>
# Hello!

You requested a secure login link. Click the button below to sign in:

<x-mail::button :url="$url">
Sign In Now
</x-mail::button>

This link will expire in {{ $expiryMinutes }} minutes.

If you did not request this link, no action is needed.

Thanks,<br>
{{ config('app.name') }}
</x-mail::message>

3. Register it in the plugin

FilamentPasswordlessLoginPlugin::make()
    ->mailable(\App\Mail\CustomMagicLinkMail::class)

Custom Notification

Use your own Laravel Notification class instead of the default:

1. Create your Notification

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class CustomMagicLinkNotification extends Notification
{
    use Queueable;

    public function __construct(
        public string $url,
        public int $expiryMinutes,
        public array $metadata = [],
    ) {}

    public function via($notifiable): array
    {
        return ['mail'];
    }

    public function toMail($notifiable): MailMessage
    {
        return (new MailMessage)
            ->subject('Your Login Link โ€” ' . config('app.name'))
            ->greeting('Hello ' . ($notifiable->name ?? '') . '!')
            ->line('Click the button below to sign in securely.')
            ->action('Sign In', $this->url)
            ->line('This link expires in ' . $this->expiryMinutes . ' minutes.')
            ->line('If you did not request this, please ignore this email.');
    }
}

2. Register it in the plugin

FilamentPasswordlessLoginPlugin::make()
    ->notification(\App\Notifications\CustomMagicLinkNotification::class)

Note: Your notification class receives $url, $expiryMinutes, and $metadata in the constructor โ€” same signature as the base package's default notification.

Token Resource

The plugin registers a full Filament resource for managing magic login tokens at /admin/magic-login-tokens (configurable).

Features

  • List View โ€” All tokens with status badges (Active / Expired / Used)
  • Filters โ€” By status (active, expired, used) and date range
  • Search โ€” By user name or email
  • Create โ€” Generate tokens manually with options: user, guard, redirect URL, expiry, max uses, send notification toggle
  • Invalidate โ€” Expire individual tokens immediately
  • Bulk Delete โ€” Select and delete multiple tokens
  • Cleanup โ€” Header action to delete all expired tokens at once

Widgets on Resource Page

The stats and chart widgets are displayed as header widgets on the token list page (not on the dashboard):

  • Stats Overview โ€” 4 stat cards: Total Generated (with week-over-week trend), Successfully Used (with success rate), Expired Unused, Active Links. Includes sparkline mini-charts.
  • Line Chart โ€” Generated vs Used vs Failed over the configured time range (default 30 days).

Customization

FilamentPasswordlessLoginPlugin::make()
    ->resource()                        // Enable (default)
    ->resourceSlug('magic-links')       // Custom URL: /admin/magic-links
    ->navigationGroup('Security')       // Custom sidebar group
    ->navigationIcon('heroicon-o-key')  // Custom sidebar icon
    ->navigationSort(50)                // Custom sort order
    ->canCreateTokens(false)            // Hide create button
    ->canDeleteTokens(false)            // Hide delete actions
    ->statsWidget()                     // Enable stats (default)
    ->chartsWidget()                    // Enable charts (default)
    ->chartDays(60)                     // Show 60 days of data

Customizing Navigation via Language Files

Navigation group and label are pulled from language files by default. Publish and edit:

php artisan vendor:publish --tag=filament-passwordless-login-lang

Then edit lang/vendor/filament-passwordless-login/en/filament.php:

return [
    'navigation_group' => 'Security',        // Sidebar group name
    'navigation_label' => 'Login Links',     // Sidebar item label
    'resource_label' => 'Login Link',        // Singular label
    'resource_plural_label' => 'Login Links', // Plural label
    // ... all other strings
];

Note: The fluent API (->navigationGroup('...')) takes priority over language files when both are set.

Multilingual Support

8 languages included out of the box:

Language Code Example Navigation Group
English en Authentication
Spanish es Autenticaciรณn
French fr Authentification
German de Authentifizierung
Dutch nl Authenticatie
Arabic ar ุงู„ู…ุตุงุฏู‚ุฉ
Hindi hi เคชเฅเคฐเคฎเคพเคฃเฅ€เค•เคฐเคฃ
Portuguese pt Autenticaรงรฃo

All strings are translatable โ€” login page text, action labels, modal headings, resource columns, widget headings, status badges, navigation labels, and filter labels.

Adding a New Language

Create a new file at lang/vendor/filament-passwordless-login/{locale}/filament.php and translate all keys from the English file.

Config File Reference

Published to config/filament-passwordless-login.php:

return [

    // Login page settings
    'login_page' => [
        'enabled' => true,
        'show_password_login_link' => true,
    ],

    // Login action settings
    'login_action' => [
        'enabled' => false,
        'icon' => 'heroicon-m-link',
        'color' => 'primary',
        'slideover' => false,
        'width' => 'md',
    ],

    // Resource settings
    'resource' => [
        'enabled' => true,
        'slug' => 'magic-login-tokens',
        'can_create' => true,
        'can_delete' => true,
    ],

    // Widget settings
    'widgets' => [
        'stats_enabled' => true,
        'charts_enabled' => true,
        'chart_days' => 30,
    ],

];

Base Package Configuration

The Filament plugin uses spykapps/passwordless-login under the hood. Configure the base package in config/passwordless-login.php:

return [
    // User model
    'user_model' => \App\Models\User::class,
    'email_column' => 'email',

    // Token settings
    'token' => [
        'length' => 32,
        'hash_algorithm' => 'sha256',
    ],

    // Link expiry
    'expiry_minutes' => 15,

    // Max uses per link (null = unlimited)
    'max_uses' => 1,

    // Bot detection for email clients (Outlook, Apple Mail, SafeLinks, etc.)
    'bot_detection' => [
        'enabled' => true,
        'strategy' => 'both', // 'confirmation_page', 'javascript', or 'both'
    ],

    // Rate limiting
    'throttle' => [
        'enabled' => true,
        'max_attempts' => 5,
        'decay_minutes' => 10,
    ],

    // Redirect after login
    'redirect' => [
        'on_success' => '/admin',
        'on_failure' => '/admin/login',
    ],

    // Security
    'security' => [
        'invalidate_previous' => true,
        'invalidate_on_login' => true,
        'ip_binding' => false,
        'user_agent_binding' => false,
    ],
];

See the spykapps/passwordless-login README for the full list of configuration options.

Complete Setup Examples

Example 1: Magic Link Only (No Password Login)

FilamentPasswordlessLoginPlugin::make()
    ->loginPage()
    ->showPasswordLoginLink(false)
    ->mailable(\App\Mail\BrandedMagicLink::class)

Example 2: Password Login with Magic Link Hint

FilamentPasswordlessLoginPlugin::make()
    ->loginPage(false)                // Keep default Filament login
    ->loginAction()                   // Enable login action
    ->loginActionPosition(FilamentPasswordlessLoginActionPosition::EmailFieldHint)
    ->loginActionIcon('heroicon-m-envelope')
    ->loginActionColor('success')

Example 3: Password Login with Magic Link Button

FilamentPasswordlessLoginPlugin::make()
    ->loginPage(false)                // Keep default Filament login
    ->loginAction()                   // Enable login action
    ->loginActionPosition(FilamentPasswordlessLoginActionPosition::LoginFormEndButton)
    ->slideover()                     // Open as slide-over

Example 4: Full Featured

FilamentPasswordlessLoginPlugin::make()
    ->loginPage()
    ->showPasswordLoginLink()
    ->redirectUrl('/admin/dashboard')
    ->failureUrl('/admin/login')
    ->loginAction()
    ->loginActionPosition(FilamentPasswordlessLoginActionPosition::EmailFieldHint)
    ->slideover()
    ->mailable(\App\Mail\CustomMagicLink::class)
    ->resource()
    ->navigationGroup('Security')
    ->navigationIcon('heroicon-o-shield-check')
    ->navigationSort(50)
    ->statsWidget()
    ->chartsWidget()
    ->chartDays(90)

Example 5: Minimal (Resource Only, No Login Changes)

FilamentPasswordlessLoginPlugin::make()
    ->loginPage(false)
    ->loginAction(false)
    ->resource()
    ->statsWidget()
    ->chartsWidget(false)

Enum Reference

use SpykApp\FilamentPasswordlessLogin\Enums\FilamentPasswordlessLoginActionPosition;

FilamentPasswordlessLoginActionPosition::EmailFieldHint      // Hint icon on email field
FilamentPasswordlessLoginActionPosition::LoginFormEndButton   // Button after login form

Credits

License

The MIT License (MIT). Please see License File for more information.