Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Email 2Fa Laravel Package

solution-forest/filament-email-2fa

Adds email-based two-factor authentication to Filament panels. Users receive one-time login codes by email, with configurable expiry, resend limits, and familiar Filament screens for setup and verification. Integrates quickly with existing Filament authentication.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a very young package (no stars, no dependents), so treat the README as a starting hypothesis and the src/ directory as the source of truth. Budget 20 minutes to read the code before you install it in anything real.

Minimal path:

composer require solution-forest/filament-email-2fa
php artisan vendor:publish --provider="SolutionForest\FilamentEmail2fa\FilamentEmail2faServiceProvider"
php artisan migrate

Then register the plugin on the panel(s) that need it:

// app/Providers/Filament/AdminPanelProvider.php
public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->plugin(\SolutionForest\FilamentEmail2fa\FilamentEmail2faPlugin::make());
}

Where to look first, in this order:

  1. composer.json — the require block tells you which Filament major version it targets. If it says filament/filament: ^3.0 and you're on v4, stop and evaluate compatibility before writing a single line.
  2. The service provider — what it publishes (config? migrations? views?), what it binds, whether it registers middleware or a Livewire component.
  3. config/ — code length, TTL, resend cooldown, and which mail notification is used. These are your levers.
  4. database/migrations/ — whether codes live in a dedicated table or as columns on users. This dictates whether you can have multiple active codes, and whether codes survive a logout.
  5. The login/challenge flow — find where the code is verified. That single file is what you'll be patching or wrapping when requirements drift.

First use case to prove out: put the plugin on a non-production panel, log in with MAIL_MAILER=log, and confirm the code arrives in storage/logs/laravel.log. That validates the whole loop (challenge redirect → code generation → notification → verification) in about five minutes and surfaces any missing migration or config key immediately.

Implementation Patterns

Gate the plugin behind config, not code. Since the package is unproven, keep it toggleable so you can disable it in an incident without a deploy:

->plugin(
    FilamentEmail2faPlugin::make()
        ->enabled(config('auth.email_2fa_enabled', true))
)

(Verify the method name in the plugin class — if there's no enabled() method, add one via a fork or wrap registration in an if.)

Follow the package's own challenge page rather than rebuilding it. If it ships a Filament page or a Livewire component for code entry, extend that class. Reimplementing the challenge screen means you now own the session-state handshake between "password verified" and "code verified", which is the exact place 2FA implementations leak.

Treat the notification as a first-class extension point. Almost every real deployment needs to change the mail copy, add a company logo, or route through a transactional provider. Publish the views if available; otherwise bind a replacement notification class in a service provider:

$this->app->bind(
    \SolutionForest\FilamentEmail2fa\Notifications\EmailTwoFactorCode::class,
    \App\Notifications\BrandedTwoFactorCode::class
);

Confirm the package resolves the notification from the container (app(...) or constructor injection) rather than new-ing it — if it uses new, container binding is dead weight and you'll need a fork.

Local and CI ergonomics. Notification::fake() plus a bypass is worth the setup cost:

protected function setUp(): void
{
    parent::setUp();
    if (! config('auth.email_2fa_enabled')) {
        $this->withoutMiddleware(\SolutionForest\FilamentEmail2fa\Http\Middleware\EnsureEmailTwoFactor::class);
    }
}

For local dev, MAIL_MAILER=log or Mailpit. For staging, keep 2FA on — it's the only environment where you'll catch flow bugs that don't reproduce with a faked notification.

Multi-panel setups. Register the plugin per panel rather than globally, so a customer-facing panel and an admin panel can have different TTLs and different notification branding. If the config is global-only, that's a fork candidate.

Queue the notification if your mail provider is slow — a synchronous SMTP call inside the login request is a 2–5 second hang for every user, every login. Check whether the shipped notification implements ShouldQueue; if not, swap it.

Gotchas and Tips

Pin the version exactly. With zero dependents and no release history to speak of, ^1.0 can silently break you. Use "solution-forest/filament-email-2fa": "1.0.3" and vendor the source if the package is load-bearing. Assume you may have to maintain a fork.

Mail failure = permanent lockout. The most common production incident with email OTP plugins is a misconfigured mail driver turning every login into a dead end. Always: (a) log the code at debug level in non-production, (b) ship a documented artisan command or tinker snippet to disable 2FA for a user, (c) add a break-glass admin route that authenticates with a separate mechanism.

Check how codes are stored and compared. A well-built implementation stores a hash, sets a short TTL, invalidates on use, and increments an attempt counter. A rushed one stores plaintext and allows unlimited guesses. Read the verification method before trusting it — this is the single highest-risk line in the package. If there's no attempt limit, add throttling yourself via the RateLimiter facade keyed on user ID.

Resend cooldowns are usually missing or client-side only. Enforce the cooldown server-side in the challenge component, not just by disabling the button. Otherwise a user with the network tab open can mail-bomb themselves or a colleague.

Middleware ordering matters. If the package registers its own middleware, it must run after Filament's Authenticate and before your panel pages — otherwise the challenge page itself gets caught in the redirect loop. If you see a redirect loop on the verification screen after install, this is the cause; check app/Http/Kernel.php or the panel's ->middleware([...]) order.

Test the "already verified" path in the same session. Many implementations re-challenge on every request or, worse, never challenge again until logout. Decide your policy (per-session, per-device with a remember cookie, per-N-hours) and verify the package matches — don't assume.

Debugging checklist when a code "doesn't arrive": php artisan queue:work running? MAIL_MAILER pointing at log vs SMTP? Code written to the DB at all (query the table/columns directly)? Row scoped to the right user ID? TTL already expired because the clock is off between app servers?

Extension-point reality check. Before planning customisations, confirm whether the package publishes views (vendor:publish --tag=...) and exposes a config file. If it's a single-file plugin with hardcoded strings and no publish tags, plan on a fork rather than fighting it — and note that in your ADR, because the next developer will ask.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package