Search by

cherubimro / roundcube-fido2

cherubimro

FIDO2/WebAuthn two-factor authentication for Roundcube Webmail

Package info

github.com/cherubimro/roundcube-fido2

Type:roundcube-plugin

pkg:composer/cherubimro/roundcube-fido2

Statistics

Installs: 39

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v1.0.2 2026-10-17 02:31 UTC

This package is auto-updated.

Last update: 2026-10-17 02:35:00 UTC


README

A Roundcube Webmail plugin that adds FIDO2/WebAuthn second-factor authentication using hardware security keys.

Users log in with their password first, then verify with a security key tap as a second factor. Supports YubiKey, Feitian BioPass, SoloKeys, Google Titan, and any FIDO2-compliant authenticator.

Requirements

  • Roundcube 1.6+
  • PHP 8.1+
  • HTTPS (WebAuthn requires a secure context)
  • Composer

Installation

Via Composer (recommended)

From your Roundcube directory:

composer require cherubimro/roundcube-fido2

If the package isn't on Packagist yet, add the repository first in your Roundcube composer.json:

"repositories": [
    { "type": "vcs", "url": "https://github.com/cherubimro/roundcube-fido2" }
]

Manual installation

Copy the plugin into your Roundcube plugins/ directory as webauthn:

cd /path/to/roundcube
cp -r /path/to/roundcube-fido2 plugins/webauthn
cd plugins/webauthn
composer install --no-dev

Copy the example configuration and edit it:

cp config.inc.php.dist config.inc.php

At minimum, set the relying party ID to match your domain:

$config['webauthn_rp_id'] = 'mail.example.com';

Enable the plugin in Roundcube's config/config.inc.php:

$config['plugins'] = ['webauthn', /* other plugins */];

The database table is created automatically on first use.

Configuration

All settings go in plugins/webauthn/config.inc.php:

Setting Default Description
webauthn_2fa_policy optional off / optional / required
webauthn_rp_name Roundcube Display name shown to users during ceremonies
webauthn_rp_id auto-detect Domain name (e.g. mail.example.com). Must match the domain users access Roundcube on. Set it: left empty it is taken from the request's Host header. An IP address cannot be a relying party ID, and a ceremony on one is refused
webauthn_rp_origins [] Allowed origins array (e.g. ['https://mail.example.com']). Empty = auto-detect
webauthn_timeout 60000 Ceremony timeout in milliseconds
webauthn_user_verification preferred preferred / required / discouraged
webauthn_attestation none none / indirect / direct
webauthn_attachment '' '' (any) / platform / cross-platform

Policy modes

  • off - Plugin disabled entirely.
  • optional - Users choose whether to enable 2FA in Settings > Security Keys. This is the default.
  • required - Every account must have a key and use it. A user with no key is held at Settings > Security Keys until one is registered; nothing else in the webmail is reachable until then, and registering the key counts as that session's verification.

Usage

Registering a key

  1. Log in to Roundcube.
  2. Go to Settings > Security Keys.
  3. Click Register New Key.
  4. Enter a name (e.g. "My YubiKey 5") and tap/touch your security key when prompted.
  5. If policy is optional, toggle Enable two-factor authentication.

Logging in with 2FA

  1. Enter your username and password as usual.
  2. A verification page appears: "Please insert and tap your security key."
  3. Tap your key. On success, you are redirected to your inbox.

Managing keys

  • Register multiple keys for redundancy.
  • Delete keys from the settings page.
  • When policy is required, the last key cannot be deleted.

Database

The plugin creates a webauthn_credentials table automatically. SQL schemas for manual creation are in the SQL/ directory:

  • SQL/mysql.sql
  • SQL/postgres.sql
  • SQL/sqlite.sql

Upgrading

To 1.0.2

This release changes when the second factor is demanded, so sessions that are already open behave differently after the upgrade:

  • A session that was opened before the user registered a key, or before the policy was set to required, is now asked for the key on its next request. Previously it kept full access for its whole lifetime, including the ability to switch two-factor authentication off again.
  • Under required, an account with no key is held at the enrolment page instead of being let through.
  • If Roundcube is reached on an IP address, ceremonies now fail with a clear message instead of binding credentials to a name that cannot work. Serve it on a domain name and set webauthn_rp_id.
  • On PostgreSQL, registering a key works for the first time. Raw bytes cannot be quoted into a statement for a bytea column, so every registration used to fail with a syntax error; binary is now written as hex and read back from the stream PostgreSQL hands over.

No database change is needed.

Security

  • The gate runs on every request through the startup hook, and it asks for a positive marker left by a completed ceremony. It does not rely on a flag set at login time, so a session that Roundcube's login_after hook never saw — one opened before enrolment, or by a plugin that logs users in itself — is held like any other.
  • What the gate requires is recomputed on every request, so registering a key, changing the policy or flipping the preference takes effect on sessions that are already open.
  • While a session is held, only the page it is held at and that page's own ceremony endpoints are reachable. A held session cannot register a key of its own, delete a key, or turn two-factor authentication off.
  • The session ID is replaced when the second factor is accepted, so the ID that existed before verification no longer grants access.
  • Challenges are single-use (consumed immediately after verification).
  • CSRF protection via Roundcube's built-in request token mechanism on every endpoint, including the one that issues an assertion challenge.
  • Clone detection: a signature counter that does not advance is refused, the credential is flagged, the event is logged, and the stored counter keeps the highest value seen so the next attempt cannot clear the flag by itself. The flag is shown next to the key in Settings.
  • All database queries are scoped to the authenticated user ID.
  • Credential IDs are stored as raw binary and compared byte-for-byte.
  • A ceremony is refused unless the request's host belongs to the configured relying party, and to one of webauthn_rp_origins when those are set.
  • Webmail is not the only way in: a second factor here does not protect direct IMAP, POP3 or SMTP access with the same password. Restrict those separately.

Supported databases

MySQL/MariaDB, PostgreSQL and SQLite, with matching schemas in SQL/. The tables are created on first use. Verified on MariaDB 11.4, PostgreSQL 16 and SQLite, with Roundcube 1.7.4.

Library

Uses lbuchs/webauthn v2.2 -- a zero-dependency PHP WebAuthn server library.

License

GPL-3.0-or-later (same as Roundcube).