Skip to content

✨ Add Roundcube login with 2FA and short-lived webmail credentials - #1394

Draft
t2d wants to merge 11 commits into
mainfrom
feat/webmail-2fa
Draft

t2d wants to merge 11 commits into
mainfrom
feat/webmail-2fa

Conversation

@t2d

@t2d t2d commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Plugin side: roundcube-userli-plugin PR 9.

Goal

Enforce 2FA for mail. Roundcube logs in with the main password plus a TOTP code and then uses a short-lived app password for IMAP and SMTP. Mail programs of 2FA users need app passwords.

Changes

  • POST /api/roundcube/login checks the main password and, for 2FA users, a TOTP or backup code. It returns a webmail credential (an app password that expires after 12 hours) and its expiresAt. If the code is missing, it returns 401 twofactor required. Spam users and users who must change their password get 403. Attempts are limited to 10 per user in 15 minutes (429, new dependency symfony/rate-limiter). App passwords are rejected.
  • POST /api/roundcube/logout revokes the webmail credential. It never touches regular app passwords. A password change also revokes all webmail credentials.
  • app_passwords.expires_at: expired credentials no longer authenticate and are pruned daily. Webmail credentials are hidden from the account page, the admin list, the metrics and the user stats.
  • New setting Require app passwords with 2FA (twofactor_app_passwords_required): Dovecot and Postfix reject the main password of 2FA users. The default is off, so upgraded installs keep working. The setup wizard turns it on for new installs. When it is on, the 2FA page tells users that their mail programs need app passwords and links to them.

Tested end to end with the plugin against the dev stack (Roundcube, Dovecot, MailCrypt), with the setting both on and off.

🤖 Generated with Claude Code

@t2d
t2d added this pull request to stack #1395 October 21, 2026 14:36
@t2d t2d added enhancement New feature or request security labels Oct 21, 2026
Base automatically changed from feat/app-passwords to main October 21, 2026 14:41
t2d and others added 2 commits October 21, 2026 16:50
POST /api/roundcube/login checks the main password and, for users with
2FA, a TOTP or backup code. It returns an app password that expires
after 12 hours, which Roundcube uses for IMAP and SMTP instead of the
main password. POST /api/roundcube/logout revokes it.

Webmail credentials are hidden from the account page, the admin list,
the metrics and the user stats. Expired ones are pruned daily.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New setting twofactor_app_passwords_required: Dovecot and Postfix
reject the main password of users with 2FA, so their mail programs need
app passwords and webmail the Roundcube login.

It defaults to on for new installations. A migration turns it off when
users already exist, so existing mail setups keep working until the
admin enables it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@t2d
t2d force-pushed the feat/webmail-2fa branch from 5bcad2d to dbd98cf Compare October 21, 2026 14:51
App passwords were never released, so the table is created with
last_login_time and expires_at right away.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@t2d
t2d force-pushed the feat/webmail-2fa branch 2 times, most recently from d9e204d to 5bcad2d Compare October 21, 2026 15:13
t2d and others added 2 commits October 21, 2026 17:27
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Roundcube plugin ends the webmail session when the credential expires.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
t2d and others added 2 commits October 21, 2026 17:38
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@t2d

t2d commented Oct 21, 2026

Copy link
Copy Markdown
Contributor Author
image

t2d and others added 4 commits October 21, 2026 17:59
Failed attempts on /api/roundcube/login get no Dovecot auth penalty, so
password and 2FA code guesses are limited to 10 per user in 15 minutes.
Spam users and users who must change their password get 403 like in the
Dovecot API, and a backup code is used up only once the credential exists.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…gration

The migration relied on the settings cache being cleared after the
upgrade; a stale cache made existing installs fall back to the default
and enforce it. Now the default is off and the wizard turns it on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request security

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant