Thunderbird Support for Browserpass#395
Conversation
|
This is an impressive amount of work, thanks for sharing! It will take some time to go through, as it's quite a lot of code, but I'll try to not delay it much. @erayd do you have time to review this as well? A few initial questions & remarks, mostly based on PR description:
|
|
Many thanks for your quick response.
|
|
I am using the extension now for over two weeks and did not run into any issues so far.
Let me know if anything else should be changed. |
|
I looked into this over the holidays and it is possible to consolidate the OAuth token to one entry. While my initial implementation takes care of the issue, it is still not as nice as having a consolidated token, especially when a Yubikey with manual interaction for each encryption is used. So I think the issue needs to be fixed in Thunderbird first. I will create an issue and maybe try to fix the issue myself when I have some spare time. I will keep this thread updated. |
Intercept Thunderbird's authentication prompts (IMAP, SMTP, POP3,
NNTP) and OAuth2 token requests (CalDAV, CardDAV, mail) to retrieve
credentials from the pass password store via GPG decryption.
Credentials are stored under thunderbird/{protocol}-{hostname} in
pass. A single OAuth refresh token is kept per provider hostname.
Pass is the sole credential store — tokens are never written to
Thunderbird's built-in password manager.
A WebExtension Experiment (implementation.js) hooks MsgAuthPrompt,
OAuth2Module, and OAuth2.prototype.connect to bridge Thunderbird's
synchronous auth callbacks with the async native messaging host.
Concurrent CalDAV calendar syncs are serialised through a pending-
lookups set to avoid multiple YubiKey touches for the same token.
The OAuth2.prototype.connect hook addresses Thunderbird bug 2008995
where CalDAV creates the shared OAuth2 instance before the extension
hook is installed, leaving its refreshToken empty. The hook populates
the token from pass before each connect() call.
Includes credential migration from Thunderbird's password manager,
an informational popup, and build/packaging support.
f48a376 to
a877887
Compare
|
I have reported the mentioned Thunderbird issue quite some time ago but did not really got feedback. In general the OAuth part in Thunderbird is the most error prone, I have only got it reliably working with this extension when enabling "Offline Support" for the calendars. The branch has been reworked since the last push. I have also adapted the original pull request description with the new changes, to not be confusing. Any feedback is welcome. Here is a summary of the changes. Password store layoutThe old layout used protocol-specific subdirectories and per-account OAuth files: This has been replaced with a flat The old layout required scope-based matching logic ( MigrationMigration is no longer automatic. A "Migrate credentials from Thunderbird" button is now available in the Add-on Preferences page (visible only in Thunderbird). Clicking it reads Thunderbird's password manager via the experiment API, translates each entry to the new This addresses the concern raised in the review about unprompted credential dumps. The user explicitly initiates migration and can see what happened. CalDAV startup OAuth windows (TB bug 2008995)The old implementation tried to close OAuth windows that appeared before the extension was initialised. This was fragile and caused issues. The new implementation adds a hook on For windows that do open before The recommended workaround for first-run CalDAV windows remains enabling offline caching (Calendar → Properties → Offline Support → Keep a local copy). Notification in OAuth browser window
YubiKey / concurrent CalDAV syncsWhen multiple CalDAV calendars sync at the same time, each triggered a separate MV3 background script lifecycleThe background script unloads after ~30 s of inactivity. Two failure modes were addressed:
The Other changes
|
|
Hi @thardeck - sorry it's taken me so long to review this PR! It's clear you've put a lot of work into it. Unfortunately, I don't think that this is really something that belongs in Browserpass - it's well outside the scope of what Browserpass is intended to be, which is a web-browser interface to a pass-style repository of gpg-encrypted website credentials. This is exacerbated somewhat by the esoteric nature of the functionality that this particular PR is intended to implement, for which I suspect the ultimate user base will be extremely small - and quite possibly limited to just yourself. With that said, as Browserpass is a project that tends to change fairly infrequently, it would likely be viable for you to maintain this patchset as an independent fork. I'm happy for us to add a link to the project README that will point any users who want this functionality in your direction (let us know if you'd like that). |
Summary (Edited)
This PR adds Thunderbird email client support to the Browserpass browser extension. It allows users to store and retrieve email credentials (IMAP, SMTP, POP3, NNTP) and OAuth tokens (Gmail, Microsoft, Fastmail) from their
passpassword store instead of Thunderbird's built-in password manager.I am already running the implementation locally since a few days. I have tried different setups, fixed several issues on the way and also ran into Thunderbird issues. For debugging purposes I have added quite some logging, which should not hurt and probably at least be kept until more users tried the extension.
I am interested in this feature myself and got some time to work on this. It turned out to be much more code then originally anticipated but there were a lot of edge cases to cover and will probably be some more. Any feedback is appreciated.
Features
OAuth2ModuleandOAuth2.prototype.connecthookspassvia the preferences pagepassTechnical Implementation
Why Separate Builds?
Thunderbird requires WebExtension Experiments to hook into its authentication system, which Firefox doesn't allow for non-privileged add-ons. The experiment API provides access to:
MsgAuthPrompt- Intercepts IMAP/SMTP/POP3/NNTP password promptsOAuth2Module- Intercepts OAuth token requests (getRefreshToken/setRefreshToken) for CalDAV/CardDAVOAuth2.prototype.connect- Populates refresh tokens before Thunderbird's own check, addressing CalDAV startup issuesbrowserRequest- Monitors OAuth browser windows for autofillTherefore, the extension is built separately:
make firefox- Standard browser extensionmake thunderbird- Includes experimental credentials APIThe core extension code remains shared; only the manifest and experiment files differ.
Architecture
Key Design: Pass as Sole Credential Store
Tokens are never written to Thunderbird's built-in
loginManager. Pass is the sole persistent store. Only null tokens (auth failure / revocation) are propagated to clear staleloginManagerentries. This means on first use (or after anyloginManagerclear), CalDAV may trigger a startup OAuth window due to Thunderbird bug 2008995. Enabling offline caching for CalDAV calendars is the recommended workaround.Synchronous Bridge
Thunderbird's auth callbacks (
MsgAuthPrompt.promptPassword,OAuth2Module.getRefreshToken) are synchronous, but native messaging and the WebExtension background are async. TheawaitSync()helper spins Mozilla's event loop (processNextEvent()) to bridge this gap, with abort detection for background script death.Requirements
make hosts-thunderbird-userto registerTesting
make thunderbirdcd thunderbird && zip -r browserpass-thunderbird.xpi *Password Store Organization
All Thunderbird credentials are stored under
thunderbird/with{protocol}-{hostname}naming. OAuth uses a single token per provider hostname, matching Thunderbird's internal approach. Thehttpsentries are for OAuth identity provider login pages that appear in the OAuth browser window.Design Decisions
1. Offline Startup Control
Thunderbird is forced to start offline to prevent auth requests before the extension is ready. Once hooks are registered, Thunderbird goes online automatically. User's "Always offline" preference is respected.
2. OAuth Token Caching
Tokens retrieved from
passare cached in memory for 8 hours to reduce GPG decryption overhead. Cache is cleared on token update or after the timeout expires.3. Credential Migration
Migration is explicit and user-triggered via a button in the Browserpass preferences page (only visible in Thunderbird). Credentials are migrated from Thunderbird's password manager to
pass. Existingpassentries are not overwritten.4. Single OAuth Token Per Provider
OAuth tokens are stored as one file per provider hostname (
thunderbird/oauth-{provider}.gpg), matching Thunderbird's internal approach of one token per server. The scope is stored inside the file for reference.5. Concurrent Lookup Serialization
A
pendingLookupsset ensures only one GPG decrypt runs at a time per token key. Parallel CalDAV/CardDAV syncs spin-wait on the pending lookup rather than starting duplicate decrypts. This is critical for YubiKey users who must physically touch their key for each decrypt.6. Failed Auth Tracking
The extension detects repeated auth prompts for the same account (indicating wrong credentials) and falls back to allowing manual password entry rather than entering an infinite retry loop.
Known Limitations
Breaking Changes
None - this is additive functionality that is mainly added to the Thunderbird extension.
Related PRs
A companion PR to browserpass-native is required to register the native host for Thunderbird:
hosts-thunderbird-userandhosts-thunderbirdMakefile targetsfirefox-host.jsonnative messaging manifestReferences