Skip to content

Conversation

@thardeck
Copy link

@thardeck thardeck commented Dec 5, 2025

Summary

This PR adds Thunderbird email client support to the Browserpass extension. It allows users to store and retrieve email credentials (IMAP, SMTP, POP3) and OAuth tokens (Gmail, Microsoft, Fastmail) from their pass password 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

  • Mail Protocol Support: Automatic password retrieval for IMAP, SMTP, and POP3 accounts
  • OAuth2 Token Storage: Refresh token storage for Gmail, Microsoft, and Fastmail
  • CalDAV/CardDAV: OAuth authentication for calendar and contacts
  • Automatic Migration: Credentials are migrated from Thunderbird's password manager to pass
  • Credential Saving: New credentials entered in Thunderbird are automatically saved to pass
  • OAuth Window Autofill: Clipboard-based autofill for OAuth browser windows with keyboard shortcuts

Technical 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 password prompts
  • OAuth2Module - Intercepts OAuth token requests for CalDAV/CardDAV
  • browserRequest - Monitors OAuth browser windows for autofill

Therefore, the extension is built separately:

  • Firefox: make firefox - Standard browser extension
  • Thunderbird: make thunderbird - Includes experimental credentials API

The core extension code remains shared; only the manifest and experiment files differ.

Architecture

Thunderbird Auth Request
        ↓
implementation.js (WebExtension Experiment)
  - Hooks MsgAuthPrompt (IMAP/SMTP/POP3)
  - Hooks OAuth2Module (CalDAV/CardDAV)
  - Hooks browserRequest (OAuth windows)
        ↓
Event Emitters → background.js
        ↓
thunderbird.js
  - Matches credentials from pass
  - Stores new credentials to pass
        ↓
browserpass-native (GPG decryption)

Requirements

  • Thunderbird 128.0+: Uses WebExtension Experiments API
  • browserpass-native: Required for GPG decryption - run make hosts-thunderbird-user to register
  • pass: Standard Unix password manager with GPG

Testing

  1. Build extension: make thunderbird
  2. Package as XPI: cd thunderbird && zip -r browserpass-thunderbird.xpi *
  3. Install in Thunderbird via Add-ons → Install from File
  4. Open Thunderbird Error Console with Ctrl+Shift+J
  5. Test credential retrieval for existing email accounts
  6. Test OAuth authentication for Google/Microsoft accounts
  7. Test migration of existing Thunderbird credentials

Password Store Organization

~/.password-store/
├── imap/
│   └── mail.example.com.gpg            # IMAP server credentials
├── smtp/
│   └── mail.example.com.gpg            # SMTP server credentials
├── pop3/
│   └── pop.example.com.gpg             # POP3 server credentials
├── https/
│   └── sso.example.com.gpg             # OAuth identity provider credentials
├── oauth/
│   ├── mail/
│   │   └── user@example.com.gpg        # OAuth mail tokens
│   ├── caldav/
│   │   └── user@example.com.gpg        # Calendar OAuth tokens
│   └── carddav/
│       └── user@example.com.gpg        # Contacts OAuth tokens
└── example.com.gpg                     # Browser credentials

The https/ directory is for OAuth identity provider login pages (e.g., Google, Okta) that appear in the OAuth browser window during account setup.

Design Decisions

1. Offline Startup Control

Thunderbird is forced to start offline to prevent auth requests before the extension is ready (This seems to be a Thunderbird issue). Once hooks are registered, Thunderbird goes online automatically. User's "Always offline" preference is respected.

2. OAuth Token Caching

Tokens retrieved from pass are cached for 8 hours to reduce GPG decryption overhead. Cache is cleared on token update or after the timeout expires.

3. Credential Migration

Credentials are migrated from Thunderbird's password manager to pass. Existing pass entries are not overwritten.

4. Service-Specific OAuth Storage

OAuth tokens are stored in service-specific directories (oauth/mail/, oauth/caldav/, oauth/carddav/) because different services may request tokens with different scopes. A calendar app might request a token with calendar-only permissions, while mail needs separate permissions. Sharing a single token would break services that require permissions the new token doesn't have.

Known Limitations

  • CalDAV Startup OAuth Windows: CalDAV calendars without offline cache may trigger OAuth windows at startup before the extension hooks are ready (This seems to be a Thunderbird issue). The extension automatically closes these windows and retries once initialization is complete
  • OAuth Autofill Method: OAuth browser window autofill uses clipboard-based injection with keyboard shortcuts rather than direct DOM manipulation, due to security restrictions in browser windows.

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:

  • Adds hosts-thunderbird-user and hosts-thunderbird Makefile targets
  • Registers firefox-host.json native messaging manifest

References

  • pass - Standard Unix password manager
  • WebExtension Experiments - Thunderbird API documentation
  • keepassxc-mail - Inspired the general architecture approach (offline startup control, WebExtension Experiments, credential event handling)

Implement password storage and autofill for Mozilla Thunderbird:
- IMAP/SMTP/POP3/NNTP password retrieval from pass
- OAuth2 token storage for Gmail, Microsoft, Fastmail
- CalDAV/CardDAV OAuth authentication
- Automatic credential migration from Thunderbird's password manager
- OAuth browser window autofill via clipboard

Uses WebExtension Experiments API to hook into Thunderbird's auth system.
Requires separate build (`make thunderbird`) due to experimental APIs.
to decrease the amount of decryption operations needed to find the
matching token.
@max-baz
Copy link
Member

max-baz commented Dec 6, 2025

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:

  1. It looks like OAuth tokens are cached in memory, but regular credentials are not? How come, is the frequency really that different? I'd imagine that synchronizing email over regular IMAP will happen just as frequently as using OAuth tokens?
  2. I'm wondering whether credentials migration is not a bit too invasive? I understand the convenience, but an unprompted dump of credentials feels a bit off. Have you considered alternatives, e.g. a shell script to explicitly call, to perform the migration? Not saying that this is a way to go, just asking.
  3. Have you considered some alternatives to the password store organization? Again, not saying that your choice is wrong, but curios to explore ideas. For example, instead of multiple files with the same name under different folders, why not have all that info in a single file, that needs to be decrypted once? Instead of enforcing a specific path, why not make use of the popup to find the credentials anywhere in the password store, and perhaps cache the last choice with the ability to override it?
  4. I didn't really understand the "Known limitations" section, maybe it will get clear once I test it... But is it something you would like to address before merging the PR, or this is just explaining reasoning behind the implementation details?
  5. GPG decryption overheard is a good point you mention, I would say that even more important point is that quite a lot of users of this extensions have Yubikeys, which requires a physical touch for every single decryption operation (some have a few seconds cache, but not all).
  6. In order to avoid merging the PR while you are still working on it, could you drop a note here whenever you feel like you have completed all the work you had in mind for the PR and are happy to see it merged? The same would apply once we go through code review and potentially think of some improvement ideas.

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants