Aller au contenu principal
Jahia Store
FR

UPA - MFA factors - WebAuthn

community
Télécharger 0.2.0

Informations

Identifiant du module
mfa-factors-webauthn
Identifiant de groupe
org.jahia.community
Statut
community
Catégorie
Authentication
Auteur
Florent BOURASSE
Site web du développeur
http://www.jahia.com
Nécessite Jahia
8.2.3.0
Mis à jour
2026-08-13
Source
scm:git:git@github.com:Jahia/jahia-private.git/jahia-modules/mfa-factors-webauthn

Adds a WebAuthn / FIDO2 factor to the UPA Multi-Factor Authentication framework: passkeys, hardware security keys, and platform authenticators such as fingerprint and face unlock. Because WebAuthn credentials are origin-bound, this factor is phishing-resistant by design; a credential registered for your site cannot be replayed against an attacker's page. Built on the Yubico webauthn-server-core library (embedded in the bundle).

Users register and name multiple authenticators from a self-service panel; administrators manage policy per site (MFA Community → Security and passkeys) with the same shape as the TOTP factor, including audit and registration reporting.

  • WebAuthn / FIDO2: passkeys, security keys, and platform authenticators
  • Phishing-resistant, origin-bound credentials
  • Register, rename, and remove multiple authenticators per user
  • Relying-party ID pinned in server configuration (changing it intentionally invalidates existing passkeys)
  • Per-site policy, administrator reset, rate limiting, and audit trail
  • Self-contained bundle with the Yubico WebAuthn library embedded

Captures d'écran

Versions

Security

Three vulnerabilities affecting 0.1.0 / 0.1.1, found by an adversarial review and each confirmed by reproduction against a live Jahia 8.2 before and after the fix (#100).

All three shared one root cause: a client-supplied site value was trusted to narrow or suppress an access-control decision.

  • The site request parameter defeated the /cms/login gate. MfaLoginGateDecision.isGated() resolved the site from request.getParameter("site"), validated only against [A-Za-z0-9_-]+, and used it to replace the "is any site enforcing?" answer. Naming a site where the factor was not enabled turned gating off — a password-only POST authenticated as root with no second factor while enforcement was active. The site context now only ever widens the decision.
  • The site argument to mfaInitiate suppressed a globally enforced factor. Per-site applicability was evaluated before enforcement, so a caller holding only a password could name a site with the factor disabled, receive a "skipped" preparation, and have verify() accept an empty code. Global enforcement is now evaluated first, and a per-site switch can never skip a user who owns the factor.
  • Any single-site administrator could strip MFA from any account, including root. resetUserMfa / resetUserWebauthn authorized on siteKey alone, then acted on an unconstrained userId. The subject is now resolved once and authorized in its own right; a reset targeting a global user requires server-administrator rights.

Also: enrollmentReport no longer hands any site administrator the MFA status of every account platform-wide, and unknown usernames are reported instead of returning a green confirmation for a user that was never touched.

New — loginGate.gateBasicAuth

The /cms/login gate can now also refuse a password presented in an Authorization: Basic header, closing the same bypass on the machine-facing surface. Default false, and deliberately opt-in: that credential shape is not confined to one endpoint, so arming it refuses the provisioning API, GraphQL, the tools and WebDAV platform-wide the moment one site enforces a factor.

Read the README section before enabling it — in particular, whitelist your automation hosts, not just an admin VPN range, and verify a whitelisted client actually gets through first, because behind a reverse proxy the IP whitelist fails closed (GHSA-4v3g-mcmj-83fp).

Fixed

  • The /cms/login gate wrote the response twice (valve + filter), surfacing as HTTP 500 instead of a redirect whenever a distinct loginUrl was configured.
  • The gate matched on the mere presence of username + password request parameters while running at pipeline position 0, so it refused unrelated endpoints that happened to use those field names. It now mirrors LoginEngineAuthValveImpl#isLoginRequested.
  • The admin screens could wipe your configuration. useQuery was read without error, so a failed load left constructor defaults in the form and Save wrote them back — silently clearing enforcedFactors, the IP whitelist and the login URL.
  • The admin UI stated that trusting X-Forwarded-For was ON by default and defaulted the checkbox to true. It is OFF, because trusting the header makes the IP whitelist spoofable (SEC-135).
  • The self-service disable/regenerate dialog offered backup codes the server does not accept there — each rejected attempt burned a rate-limit strike, walking a user who had lost their authenticator into locking themselves out of their own recovery.
  • The destructive "reset a user's MFA" action never showed its in-flight state: a React 18 batching bug unmounted the confirmation box in the same render that was meant to display it. Affected both the TOTP and WebAuthn copies.

Known limitation (documented, unchanged behaviour)

The /cms/login gate is scoped to global enforcement: with enforcedFactors empty it is inert, and loginGate.enabled does not override that. A per-site opt-in deployment still challenges enrolled users through the MFA login UI, but /cms/login remains a password-only door. See The /cms/login gate → Known limitation in the README for the two supported mitigations.

Quality

The module previously had no CI, no lint gate, no coverage measurement, and no tests for its React admin apps. This release adds all four: a build workflow, yarn lint and yarn test wired into the Maven build, JaCoCo coverage, and 115 component tests — plus 22 dependency updates, ESLint 9, and Node 22.23.2.

352 Java tests + 115 React tests + 26 Cypress specs, all green.

Compatibility

Jahia 8.2.3+, user-password-authentication 0.2.0, graphql-dxm-provider 3.4+, Java 11+.

Operators who use per-site group scoping should note that a globally enforced factor now overrides it (an out-of-scope user is challenged rather than skipped, and the override is logged) — see the README's Global enforcement section.

Full Changelog: 0_1_0...0_2_0

Nécessite Jahia 8.2.3.0Mis à jour 2026-08-13

Fixes

  • Admin UI: restored the vertical scrollbar on the MFA extensions global settings page (/jahia/administration/mfa-extensions-settings). The form was clipped with no scrollbar when it grew past the viewport.

Contents

0.1.0 plus the single fix above. No API or dependency changes.

Requirements

  • Jahia 8.2.3.0
  • user-password-authentication 0.2.0 (modules import org.jahia.modules.upa.mfa at [0.2,1))

Install

Deploy the three .jar bundles via the module manager / hot-deploy folder; install mfa-factors-login-ui-0.1.1.tgz via the provisioning API.

Nécessite Jahia 8.2.3.0Mis à jour 2026-06-29
Nécessite Jahia 8.2.3.0Mis à jour 2026-06-28