Joomla Multi-factor Authentication: TOTP, WebAuthn, Passkeys
A password alone does not protect an account anymore. Passwords get phished, guessed, leaked in data breaches, and reused across sites. Joomla ships with a complete Multi-factor Authentication (MFA) system that adds a second lock to every account: an authenticator app, a security key, a passkey, or a code by email. It is built into the core, it is free, and most site owners have never opened the screen where it lives.
This article explains how Multi-factor Authentication really works in Joomla. It covers the basics for site owners, the setup and policy options for administrators, and the technical details - database tables, encryption, events, and the WebAuthn plugins - for developers.
MFA is not a plugin you buy. It is a core Joomla feature waiting to be switched on.
The goal is simple: help you understand Joomla's MFA well enough to protect your own account today and enforce it for others tomorrow.
1. The Basics
1.1 What is Multi-factor Authentication?
Authentication factors come in three families:
- Something you know - a password or PIN.
- Something you have - a phone with an authenticator app, a hardware security key, an email inbox.
- Something you are - a fingerprint or a face scan.
Multi-factor Authentication means the login requires at least two different families. An attacker who steals your password still cannot log in, because they do not have your phone or your security key. That single extra step blocks the most common account takeover attacks almost completely.
1.2 What Joomla Offers
Joomla's current MFA system arrived in Joomla 4.2 as a complete rewrite of the older "Two Factor Authentication" (2FA) feature from Joomla 3. It lives in the core Users component (com_users) and works the same way on the frontend and in the administrator backend:
- You log in with your username and password as usual.
- Joomla checks whether your account has an MFA method configured.
- If it does, you land on a locked-down captive page that accepts nothing but your second factor.
- Only after the second factor validates does Joomla let you reach the actual site.
The individual MFA methods are plugins in their own plugin group, multifactorauth. You can see them under System → Plugins, filtered by the multifactorauth folder.
1.3 MFA and Passkeys are Two Different Features
This is the most common point of confusion, so let us settle it early. Joomla contains two separate features that use the same WebAuthn browser technology:
- Multi-factor Authentication - Web Authentication (plugin group
multifactorauth): you log in with your password first, then confirm with a security key or platform authenticator as the second factor. - System - WebAuthn Passwordless Login (plugin group
system): you skip the password entirely and log in with only a passkey or security key.
Both are enabled by default, both store their data in different database tables, and you can use both at the same time. Section 6 covers them in detail.
Back to top2. The Built-in MFA Methods
2.1 The Five Plugins
Joomla ships five MFA plugins. Four are enabled out of the box; one is a demonstration plugin that stays disabled:
| Plugin | What it does | Default |
|---|---|---|
| Verification Code (TOTP) | Six-digit codes from an authenticator app such as Google Authenticator, Microsoft Authenticator, FreeOTP, or a password manager. | Enabled |
| Web Authentication (WebAuthn) | A hardware security key (YubiKey, Titan) or a platform authenticator (Windows Hello, Touch ID) confirms the login with a cryptographic signature. | Enabled |
| YubiKey | The classic YubiKey one-time password: touch the key, it types a 44-character code, Joomla validates it against Yubico's YubiCloud servers. | Enabled |
| Joomla emails you a six-digit code that is valid for a short time window. | Enabled | |
| Fixed Code | A static second password. It exists as a demonstration for developers who want to learn the MFA plugin API. | Disabled |
On top of these, Joomla automatically generates backup codes (section 3.3) the first time you set up any method. Backup codes are not a plugin - they are a built-in feature of com_users.
2.2 How TOTP Works
TOTP stands for Time-based One-Time Password, standardised in RFC 6238. During setup, Joomla generates a random 20-byte (160-bit) secret and shows it as a QR code. Your authenticator app stores the secret and from then on computes a six-digit code from the secret plus the current time, in 30-second steps. Joomla knows the same secret, computes the same code, and compares. No network connection between your phone and the site is needed - only reasonably synchronised clocks.
2.3 How the Email Method Works
The email plugin uses the same TOTP mathematics but with a longer time step: the emailed code is valid for 2 minutes by default (configurable between 30 seconds and 15 minutes in the plugin options). Think carefully before relying on this method alone: if your site's email delivery breaks, so does your login. The plugin also has a force this MFA method on all users option: when enabled, every user who already uses MFA automatically gets the email method added as an extra option, using the address from their user account.
2.4 Which Method Should You Choose?
- Best security: Web Authentication with a hardware key or platform authenticator. It is phishing-resistant - the browser only signs the challenge for the exact domain that registered the key.
- Best balance: TOTP with an authenticator app. Free, offline, works everywhere.
- Fallback only: Email. Better than nothing, but it depends on your mail server and on the security of the mailbox itself.
You can - and should - register more than one method. The captive page lets you pick which one to use at each login.
One method you will not find in the list: SMS codes. That is deliberate, not an oversight. Text messages can be intercepted and a phone number can be hijacked with a SIM-swap at the phone shop, which is why security guidelines have discouraged SMS codes for years. Joomla simply never built the weak option.
Back to top3. Setting Up MFA on Your Account
3.1 Where to Find It
In the administrator backend: click your user name in the top right corner, choose Edit Account, and open the Multi-factor Authentication tab. On the frontend: log in and open your profile edit page; the same interface appears there. Site visitors never see any of this - MFA screens only exist for logged-in users.
One important rule, enforced in the code: you can only add or edit MFA methods on your own account. Even a Super User cannot set up MFA for another user. This is deliberate - a second factor that someone else configured for you would not be "something you have".
3.2 Adding a TOTP Method Step by Step
- Open the Multi-factor Authentication tab and click Verification Code.
- Joomla shows a QR code. Scan it with your authenticator app.
- The app starts showing six-digit codes. Type the current code into the setup form to prove the pairing works.
- Save. From the next login on, Joomla asks for a code.
3.3 Backup Codes: Print Them Now
The first time you save an MFA method, Joomla generates ten backup codes, each eight digits long (two groups of four). Each code works exactly once as a replacement for your normal second factor. They exist for the day your phone is lost, broken, or wiped.
Print them or store them in a password manager now, not later. You can regenerate the whole set at any time from the same screen; regenerating invalidates all old codes.
3.4 The Default Method
If you register several methods, you can mark one as the default. The captive page preselects it at login; the others remain available behind an "use another method" link.
Back to top4. MFA for Administrators
4.1 The Policy Options
All site-wide MFA policy lives in System → Global Configuration → Users → Multi-factor Authentication (the options of com_users). The important fields:
| Option | What it does |
|---|---|
neverMFAUserGroups |
User groups that never see MFA. Useful for machine accounts or imported bulk users. |
forceMFAUserGroups |
User groups that must use MFA. Members without a method are redirected to the setup page and cannot use the site until they configure one. |
mfaonsilent |
Whether "silent" logins (see 4.3) must also pass MFA. Default: No. |
mfaredirectonlogin / mfaredirecturl |
Show a one-time "please set up MFA" page after login for users who have not configured anything yet. Users may click "do not show this again". |
mfatrycount / mfatrytime |
Brute-force protection: maximum wrong attempts per method (default 10) and how long the method is then blocked (default 1 hour). |
captive_template |
Use a different template style for the frontend captive page. |
| Allowed module positions | Which module positions may still render on the captive page (frontend and backend separately). By default: none. |
A subtle rule from the source code: if a user belongs to both a "never MFA" group and a "force MFA" group, force wins. The security-stricter setting always takes precedence.
4.2 Enforcing MFA for Administrators
The single most valuable MFA policy on a typical site: put your Administrator and Super Users groups in forceMFAUserGroups. From then on, every backend operator must complete an MFA setup at their next login before Joomla lets them do anything else. Joomla keeps a session flag for these users so they cannot dodge the requirement by enabling a method and deleting it again in the same session.
4.3 Silent Logins
Two kinds of logins do not involve typing a password: the "Remember Me" cookie and the WebAuthn passwordless login. By default (mfaonsilent = No) these skip the captive page: the cookie proves an earlier full login, and a passkey already proves possession of a physical authenticator. The response types that count as silent are configurable (default: cookie, passwordless). If your security policy demands MFA on every session regardless, switch mfaonsilent to Yes.
4.4 Helping a Locked-out User
Users lose phones. A Super User can open the locked-out user's account and delete their MFA methods (not add new ones - see 3.1), which returns the account to password-only login so the user can set up MFA again. Two protections apply, straight from the permission checks in the code: you must have core.admin (Super User) rights, and you can never reset the MFA of another Super User. Two Super Users must each manage their own second factor - or one of them has to reach for the database (section 10.2).
5. The Captive Page
5.1 A Page You Cannot Walk Around
After the password login succeeds, Joomla's application object checks the session flag com_users.mfa_checked. If it is not set and your account has an active MFA method, every single request is answered with an HTTP 307 redirect to:
index.php?option=com_users&view=captive
This "captive" page renders no menus and no modules (unless an administrator explicitly allowed specific positions), so there is nothing to click except the code field, a method switcher, and the logout link. Joomla deliberately keeps logout reachable - for the day you stand there without your phone.
5.2 What is Blocked Meanwhile
While MFA is pending, any request for a non-HTML format - format=json, format=raw, and so on - is refused with a hard HTTP 403 error. This prevents scripts and AJAX endpoints from being used as a side door around the captive page. The code makes exactly one functional exception: the finalisation steps of a Joomla core update are allowed through, so an update that happens to coincide with an MFA prompt cannot leave the site half-updated.
5.3 Brute-force Protection
Every wrong code increments a per-method try counter in the database. After mfatrycount failures (default 10), the method is blocked for mfatrytime hours (default 1). After the block window passes, the counter starts fresh. Combined with the 30-second rotation of TOTP codes, guessing a six-digit code is not a realistic attack.
6. WebAuthn and Passkeys
6.1 How WebAuthn Works
WebAuthn (the W3C Web Authentication standard) replaces shared secrets with public-key cryptography. When you register, your authenticator - a USB security key, your phone, or the platform authenticator built into your laptop - creates a key pair for that exact domain. The private key never leaves the device. At login, the site sends a random challenge, the authenticator signs it, and the site verifies the signature with the stored public key.
A few names swirl around this technology, so here is the map. The standards come from the FIDO Alliance together with the W3C. FIDO2 is the umbrella specification with two parts: WebAuthn, the browser API that websites like Joomla talk to, and CTAP (Client to Authenticator Protocol), which the browser uses to talk to the authenticator itself. And a passkey is simply the consumer-friendly name for a WebAuthn credential.
Two properties make this the strongest option Joomla offers:
- Phishing resistance: the browser refuses to use the key on any domain other than the one that registered it. A fake look-alike site gets nothing.
- Nothing useful to steal: the server stores only public keys. A database leak does not endanger the second factor.
One hard requirement, stated in the W3C specification and enforced by every browser: WebAuthn only works over HTTPS. On a plain HTTP site, both Joomla WebAuthn features quietly do not appear.
6.2 As a Second Factor (MFA)
The Multi-factor Authentication - Web Authentication plugin registers the authenticator as an MFA method. Setup is one browser dialog: Joomla asks the browser to create a credential, you touch your key or confirm the biometric prompt, done. At every following login, after your password, the captive page fires the same browser prompt instead of asking for a code. The credential ID and public key are stored - encrypted - in the method's options in the #__user_mfa table.
6.3 As a Password Replacement (Passwordless Login)
The separate System - WebAuthn Passwordless Login plugin goes a step further and replaces the password. You first add a passkey in your user profile (a "W3C Web Authentication (WebAuthn) Login" field appears there while you browse over HTTPS). After that, every Joomla login form - frontend and backend - shows an extra Web Authentication button next to the normal Log in button.
One detail worth knowing, verified in the plugin source: Joomla's implementation asks you to type your username first and then click the button; a request with an empty username is rejected before any browser dialog opens. It is a passwordless login, not a usernameless one - unlike some passkey implementations elsewhere that identify you from the credential alone.
Modern passkeys can also travel. Apple, Google, and Microsoft sync them - end-to-end encrypted - between your devices via iCloud Keychain or Google Password Manager, and browsers offer cross-device sign-in: your desktop shows a QR code, you scan it with your phone, and the phone signs the challenge. All of this happens on the browser and operating system side; Joomla only verifies the signed response, so it works without any Joomla configuration. It does have one consequence: the cloud account that syncs your passkeys becomes part of your security chain, so protect that account with MFA too.
These credentials live in their own table, #__webauthn_credentials, completely separate from MFA. And as section 4.3 explained: a passwordless login counts as a silent response type, so by default it skips the MFA captive page - the physical authenticator already proved possession.
7. Under the Hood (Developer View)
7.1 The Database Tables
Every configured MFA method is one row in #__user_mfa:
| Column | Purpose |
|---|---|
id |
Primary key. |
user_id |
The account this method belongs to. |
title |
The user-visible label ("Verification Code", "My YubiKey", …). |
method |
The plugin's method name: totp, webauthn, yubikey, email, fixed, or backupcodes. |
default |
1 for the preselected method on the captive page. |
options |
The method's configuration (TOTP secret, WebAuthn credential, backup codes) - encrypted, see 7.2. |
created_on, last_used |
Timestamps. |
tries, last_try |
The brute-force counters from section 5.3. |
The passwordless login plugin uses its own table, #__webauthn_credentials, with the credential ID, a user handle, a label, and the credential source data as JSON. That JSON also holds a signature counter: most authenticators increment it at every login, Joomla stores the last known value, and the bundled web-auth/webauthn-lib library rejects an assertion whose counter did not move forward. A cloned credential betrays itself, because two copies can never keep one counter in sync.
7.2 The Options are Encrypted with Your Site Secret
Before a row is saved, the component's encryption service encrypts the JSON-encoded options with AES, keyed by the $secret value from your site's configuration.php. This has a consequence that catches people during migrations: if the secret changes, every stored MFA method becomes undecryptable and all users effectively lose their second factor. When you clone or move a site, keep the secret - or plan an MFA re-enrolment.
7.3 The Application Handler
The captive logic is not a plugin. It is the MultiFactorAuthenticationHandler trait in libraries/src/Application/, used by exactly two application classes: SiteApplication and AdministratorApplication. The code comments spell out why: MFA is interactive by design, so the CLI application (implicitly trusted) and the API application (token-based, see section 8) do not use it. The trait maintains two session flags: com_users.mfa_checked (set once the second factor validated, or immediately when the account has no methods) and com_users.mandatory_mfa_setup (keeps members of a forced group captive on the setup page).
7.4 Migration from Joomla 3 Two Factor Authentication
The old Joomla 3 feature stored its data in the otpKey and otep columns of #__users. The handler migrates this per user, at their first captive login - not site-wide during the Joomla update, a deliberate choice to keep the update from timing out on sites with thousands of users. Old TOTP and YubiKey settings convert to #__user_mfa rows; old emergency codes become backup codes; the legacy columns are then emptied.
7.5 Writing Your Own MFA Method
An MFA plugin is a normal Joomla plugin in the multifactorauth group that subscribes to five events from Joomla\CMS\Event\MultiFactor:
public static function getSubscribedEvents(): array
{
return [
'onUserMultifactorGetMethod' => 'onUserMultifactorGetMethod', // describe the method
'onUserMultifactorGetSetup' => 'onUserMultifactorGetSetup', // render the setup form
'onUserMultifactorSaveSetup' => 'onUserMultifactorSaveSetup', // validate + store options
'onUserMultifactorCaptive' => 'onUserMultifactorCaptive', // render the captive form
'onUserMultifactorValidate' => 'onUserMultifactorValidate', // check the submitted code
];
}
GetMethod returns a MethodDescriptor (name, display title, short info, icon). GetSetup and Captive return render-option objects that describe the input field; Validate receives the record, the user, and the submitted code and returns true or false. The disabled Fixed Code plugin exists precisely as a minimal, readable example of all five - its own language strings say so. Additional events exist for special cases: Callback (for methods that need an external round-trip), BeforeDisplayMethods (the email plugin uses it for its force-enable feature), and NotifyActionLog (feeds the Action Log).
8. MFA, the Web Services API, and the CLI
8.1 The API Does Not Use MFA
Joomla's REST API (/api/index.php/v1/…) authenticates every request with an API token (or Basic authentication), not with a session - so there is no login moment where a captive page could appear, and the handler trait is simply not part of the API application. Protect the API the way it is designed to be protected: with the token plugins, over HTTPS, granted only to the accounts that need it. The authentication article covers the token mechanism in depth.
8.2 But MFA Still Guards the Token
Indirectly, MFA matters for the API too: the API token is created and shown in the user's profile. An attacker who cannot pass your captive page cannot log in to copy your token. Securing the interactive login secures the credentials that the non-interactive clients use.
8.3 CLI
Commands run with cli/joomla.php execute as an implicitly authorised user - whoever has shell access is trusted. MFA plays no role there. The practical consequence: your server's SSH configuration is part of your Joomla security perimeter.
9. SEO and Metadata
MFA has no direct SEO surface - and that is exactly how it should stay:
- The captive page and the MFA setup pages only exist for logged-in users; search engine crawlers never receive them. There is nothing to index and nothing to optimise.
- Keep your frontend login page itself out of the index (a
noindexrobots setting on the login menu item) - login pages in search results only invite password-guessing traffic. - If you use a separate template style for the captive page (
captive_template), keep it minimal on purpose: no analytics, no marketing scripts. Fewer third-party scripts on an authentication page also means a smaller attack surface. - Indirectly, MFA protects your SEO: a hijacked administrator account that injects spam links can cost a site months of rankings. The cheapest SEO insurance a Joomla site can get is MFA on every backend account.
10. Common Mistakes and Pitfalls
10.1 No Backup Codes, Lost Phone
Symptom: A user replaced their phone, the authenticator app is gone, and the captive page will not let them in.
Fix: First choice: use one of the ten backup codes. If they were never saved: a Super User deletes the user's MFA methods from the user account (section 4.4), the user logs in with password only and re-enrols. Prevention: treat "print the backup codes" as part of setup, not as optional.
10.2 The Locked-out Super User
Symptom: The only Super User is locked out, and no other Super User exists to reset them.
Fix: Database access is the escape hatch. Deleting the user's rows from #__user_mfa removes their methods; disabling the multifactorauth plugins in #__extensions (set enabled to 0) switches the feature off site-wide. Afterwards, re-enable and re-enrol. If you can reach phpMyAdmin or the MySQL CLI, you are two UPDATE statements away from your site - which is also a reminder of how important database credentials are.
10.3 The WebAuthn Button is Missing
Symptom: No Web Authentication button on the login form, no WebAuthn option during MFA setup.
Fix: Check the protocol. WebAuthn requires HTTPS by W3C specification; on plain HTTP Joomla hides the feature instead of showing a broken button. On a local development site, use a locally-trusted certificate (for example via mkcert) rather than working over HTTP.
10.4 Email MFA on a Site with Broken Mail
Symptom: Users with the email method wait for codes that never arrive; effectively they are locked out.
Fix: Test mail delivery (Global Configuration → Server → Send Test Mail) before anyone relies on the email method, and encourage TOTP or WebAuthn as the primary method with email as fallback at most. If it happens anyway: backup codes or an administrator reset.
10.5 Enabling the Fixed Code Plugin in Production
Symptom: Users "secure" their account with a static second password.
Fix: Leave the Fixed Code plugin disabled. It is a developer demonstration - its own description says so. A fixed code is just a second password: it can be phished, leaked, and reused exactly like the first one, so it adds no real second factor.
10.6 Expecting MFA to Protect the REST API
Symptom: MFA is enforced for all groups, and the assumption is that API endpoints are now behind it too.
Fix: They are not (section 8). API access is guarded by tokens. Audit which users have an active API token, disable the token plugins if you do not use the API at all, and treat tokens with the same care as passwords.
10.7 Mistaking a Hidden Login URL for a Second Factor
Symptom: The backend is protected with a secret URL key - for example with the free AdminExile plugin, which hides /administrator/ unless the visitor adds a secret key to the URL - and MFA is considered unnecessary because "there is already a second secret".
Fix: Keep the URL key, but do not count it as a factor. A secret URL is "something you know", just like your password - two knowledge secrets are two-step, not multi-factor, and one phishing page or keylogger captures both at once. It is also not authentication: the same static key is shared by all administrators, is tied to no user account, and only grants the right to see the login form. Tools like AdminExile are valuable perimeter hardening - they hide the login from the constant bot traffic that hammers every Joomla site - but they reduce exposure of the password, while MFA survives compromise of the password. Use both layers together.
10.8 Losing All MFA During a Site Move
Symptom: After migrating a site to a new server or rebuilding configuration.php, every user's second factor stopped working.
Fix: The options column is encrypted with the site $secret (section 7.2). Restore the original secret value and the methods decrypt again. If the old secret is truly gone, delete the #__user_mfa rows and let users re-enrol.
11. Best Practices
If you remember only a few things from this article, remember these:
- Enable MFA on your own account today - the feature is already installed and switched on.
- Put your Administrator and Super User groups in Force Multi-factor Authentication; those accounts are what attackers actually want.
- Prefer WebAuthn or TOTP; treat the email method as a fallback, never as the only method.
- Register two methods per account (for example TOTP plus a security key) so one lost device is an inconvenience, not an incident.
- Save the ten backup codes when they appear. That screen is not decoration.
- Keep the
$secretinconfiguration.phpunchanged when you migrate a site - or plan re-enrolment. - Serve the whole site over HTTPS; without it, the strongest methods are not even available.
- Combine MFA with the basics from the security hardening article: updates, strong unique passwords, and least-privilege user groups. MFA is a layer, not a replacement.
12. Quick Reference
WHAT WHERE / VALUE
Set up your own MFA Backend: your name (top right) > Edit Account
> Multi-factor Authentication tab
Frontend: Edit profile > same tab
Site-wide policy Global Configuration > Users
> Multi-factor Authentication
Method plugins System > Plugins > filter folder
"multifactorauth"
Default-enabled methods TOTP, WebAuthn, YubiKey, Email
Disabled demo plugin Fixed Code (leave it disabled)
Backup codes 10 codes, 8 digits, single-use
TOTP parameters 6 digits, 30 s step, RFC 6238
Email code validity 120 s default (30-900 s)
Wrong-code lockout 10 tries, then blocked 1 hour (defaults)
Captive page URL index.php?option=com_users&view=captive
MFA methods table #__user_mfa (options AES-encrypted
with the configuration.php secret)
Passwordless credentials #__webauthn_credentials
Reset a locked-out user Super User deletes the user's methods
(never possible for another Super User)
Emergency off switch (SQL) UPDATE #__extensions SET enabled = 0
WHERE folder = 'multifactorauth';
Scope Site + Administrator only;
API uses tokens, CLI is trusted
Back to top13. Summary
- Joomla ships a complete Multi-factor Authentication system in the core: TOTP, WebAuthn, YubiKey, and email methods, plus single-use backup codes.
- After the password login, a locked-down captive page demands the second factor; wrong attempts are throttled, non-HTML requests are blocked with a 403.
- Administrators enforce policy per user group: some groups never see MFA, others cannot use the site without it, and force always wins.
- WebAuthn appears twice: as an MFA method and as a separate passwordless login plugin - both HTTPS-only, both phishing-resistant.
- Under the hood, methods are rows in
#__user_mfawith AES-encrypted options keyed to your site secret, orchestrated by an application trait and five plugin events. - The REST API is deliberately outside MFA - tokens guard it - and the CLI trusts whoever holds shell access.
Switching MFA on takes five minutes. Deciding on the right policy for a site with many user groups, migrating accounts from older setups, or recovering a site where authentication went wrong takes experience. If you want a second pair of eyes on how your Joomla site handles logins, accounts, and access, it pays to have someone look at it who works with these settings every day.
Back to top

Peter is a Joomla specialist and a Linux admin for fast, secure and scalable websites.












