DEV3 Technologies home

Filament TOTP two-factor authentication with recovery codes

Enable authenticator-app two-factor login in a Filament 4 panel: provider setup, encrypted secret columns and recovery codes on the user.

FilamentAuthenticationfilament2fatotpsecurity

Code

<?php

namespace App\Providers\Filament;

use Filament\Auth\MultiFactor\App\AppAuthentication;
use Filament\Panel;
use Filament\PanelProvider;

class StudioPanelProvider extends PanelProvider
{
    public function panel(Panel $panel): Panel
    {
        return $panel
            ->id('studio')
            ->path('studio')
            ->login()
            ->profile() // gives users the page where they set 2FA up
            ->multiFactorAuthentication([
                AppAuthentication::make()
                    ->recoverable()          // one-time recovery codes
                    ->recoveryCodeCount(8)
                    ->codeWindow(4),         // tolerate a little clock drift
            ], isRequired: false);           // true forces every user to enrol
    }
}

Needs: filament/filament@^4.0

How it works

What it solves

Passwords alone are weak for panels that manage money or customer data. Time-based one-time passwords (TOTP) from an authenticator app are the standard second factor, and Filament 4 ships the whole flow, QR code enrolment, challenge screen and recovery codes, so you only supply storage.

How it works

In the panel provider, multiFactorAuthentication() receives a list of providers. AppAuthentication is the TOTP provider; recoverable() adds one-time recovery codes at enrolment, and codeWindow() widens the accepted time slice. isRequired: false makes it optional, users enable it themselves from the profile page, which the profile() call adds.

On the model side, Filament needs to read and write two values. The trait implements the contract methods: the secret getter and saver, the name that appears in the authenticator app, and the recovery code getter and saver. The migration adds two text columns. Both are cast with encrypted (the codes as encrypted:array) so a database dump never exposes a usable secret, and both are hidden from serialisation.

At login, a user with a secret set sees a code prompt after the password step.

Adapting it

Set isRequired: true for admin panels to force enrolment. Use the trait as written, or put the methods straight on User. Pair it with the role guard so only approved accounts reach the panel.

Pitfalls

  • Losing APP_KEY makes stored secrets undecryptable, so lock-out recovery needs a plan.
  • Server clock drift breaks TOTP, keep NTP running.
  • Recovery codes are shown once; tell users to store them.

How to use it

  1. Run composer require filament/filament:"^4.0" and create a panel.
  2. Add the migration and run php artisan migrate.
  3. Make User implement HasAppAuthentication and HasAppAuthenticationRecovery, use the trait, and add the encrypted casts and hidden columns.
  4. Add the multiFactorAuthentication() and profile() calls to the panel provider.
  5. Log in, open Profile, and scan the QR code with an authenticator app.

Licence: DEV3 Snippet Licence. Free to use, change and ship in personal and commercial projects. Attribution is appreciated, not required. Please do not redistribute snippets as a collection.

Related snippets