Appearance
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 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 key | What |
|---|---|
_kitloom_two_factor_secret | The authenticator secret, encrypted with the application key |
_kitloom_two_factor_confirmed_at | When 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_codes | Hashes of the unused recovery codes, as encrypted JSON: a code is shown once and spent when used |
_kitloom_two_factor_email | 1 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:
| Option | Default | What |
|---|---|---|
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 upOn 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 mostA 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.