Skip to content

Two-Factor Authentication ​

A second factor at sign-in to the admin: an authenticator app or an email code, required by role.

kitloom/two-factor · admins: kitloom/two-factor-filament, kitloom/two-factor-nova

Video lessons ​

Recorded in the Filament admin; the Nova admin works the same way.

Two-Factor Authentication

Two-factor sign-in for the kitloom admins and the site, as the Two Factor and Wordfence plugins give wp-admin and wp-login.php: after the password, a code from an authenticator app (TOTP) or one mailed to the user; recovery codes for a lost phone; roles that must use it. The admins plug it into their own sign-in: kitloom/two-factor-filament (Filament's multi-factor authentication) or kitloom/two-factor-nova (Nova's, which is Laravel Fortify's). The site's sign-in asks for the code by itself.

Where it is kept ​

Nothing is added to users: the factors are user meta, written through the model's meta (getMeta()/setMeta()), so the user model needs only IsWordPressUser.

Meta keyWhat
_kitloom_two_factor_secretThe authenticator secret, encrypted with the application key
_kitloom_two_factor_confirmed_atWhen the app's first code was accepted (unix time); a secret without it is a setup in progress and not a factor
_kitloom_two_factor_recovery_codesHashes of the unused recovery codes, as encrypted JSON: a code is shown once and spent when used
_kitloom_two_factor_email1 while email codes are on

A secret that no longer decrypts (the application key changed) still counts as a factor: the user fails the challenge instead of signing in without one, until two-factor:reset.

Options ​

Written from Two-Factor Authentication → Settings in either admin, JSON lists in wp_options:

OptionDefaultWhat
two_factor_required_roles[]Users holding one of these roles cannot use the admin until they set up a factor. Nobody by default: installing the module locks nobody out
two_factor_methods["app","email"]What may be set up. A method switched off stays with whoever already has it, until they turn it off

Code ​

php
use Kitloom\TwoFactor\Requirement;
use Kitloom\TwoFactor\TwoFactor;

app(TwoFactor::class)->enabledMethods($user);   // [Method::App, Method::Email]
app(TwoFactor::class)->reset($user);            // every factor and recovery code gone
app(Requirement::class)->mustSetUp($user);       // required by role and none set up

On the site ​

Whatever signs a user in on the site — the theme's login form (POST /login of kitloom/site), Google or GitHub (kitloom/social-accounts) — asks a user with a factor for its code before they are in. The user set the factor up in the admin; the theme draws the code form:

POST /login                       → {"challenge": "two-factor", "methods": ["app", "email"], "recovery": true, "redirect": "/login/?two-factor=1"}
GET  /two-factor-challenge        → the same while a sign-in waits, 404 otherwise
POST /two-factor-challenge        code=123456 (from the app or the email) or recovery_code=abcde-fghij
POST /two-factor-challenge/email  mail a code (again); twice a minute at most

A right code signs the user in and answers as the login does ({"redirect": …} or a redirect). A sign-in waits ten minutes. Wrong codes are counted per user, not per session: after five of them (app, mailed or recovery codes alike, from any browser) the second factor refuses every code and mails none for fifteen minutes, and the user is told how long to wait; a right code clears the count (site.max_failures, site.lockout_minutes). An app code is taken once. Whoever has only the email factor gets the code at once, without asking. A browser that did not ask for JSON — the social sign-in comes back as one — is sent to site.challenge_url.

Personal data ​

With kitloom/privacy, Tools → Export Personal Data says whether the account the address belongs to has a second factor: which (authenticator app, email code) and since when the app. Never the secret or the recovery codes — the archive goes out by email, and must not open the account. Erase Personal Data leaves the factors alone: they guard the account, which the erasure keeps, and taking them away would only make it easier to break into. They go with the account, or with two-factor:reset.

Console ​

php artisan two-factor:reset {user} — the user's ID, login or email: turns every factor off and deletes the recovery codes, for when nobody who could do it in the admin can sign in.

Configuration ​

php artisan vendor:publish --tag=kitloom-two-factor-config: filament.register and nova.register turn the admins' parts off, nova.confirm_password makes Nova ask for the password before two-factor authentication is turned on or off, site.register turns the site's code step off (only for an application that asks for the code itself), site.challenge_url is the theme's page with the code form, site.max_failures and site.lockout_minutes are how many wrong codes lock a user's second factor and for how long. privacy.register turns the personal data export off.

Tests ​

From the application that installs the package: php vendor/bin/phpunit --testsuite TwoFactor.

kitloom — modules and themes for Laravel.
Pricing · Services · License · Refunds · Privacy · Community · [email protected]