Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<!-- Please answer all the questions below before submitting the issue. -->

- Operating system + version:
- Browser + version:
- Browser/Thunderbird + version:
- Information about the host app:
- How did you install it?
<!-- Installed via a package manager, downloaded a pre-built binary, compiled yourself? -->
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/chromium
/firefox
/thunderbird
/dist
/dist-webstore

Expand Down
31 changes: 26 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
VERSION ?= $(shell cat .version)

CLEAN_FILES := chromium firefox dist dist-webstore
CLEAN_FILES := chromium firefox thunderbird dist dist-webstore
CHROME := $(shell which chromium 2>/dev/null || which chromium-browser 2>/dev/null || which chrome 2>/dev/null || which google-chrome 2>/dev/null || which google-chrome-stable 2>/dev/null)

#######################
# For local development

.PHONY: all
all: extension chromium firefox
all: extension chromium firefox thunderbird

.PHONY: extension
extension:
$(MAKE) -C src

# Base extension files (shared by all builds)
EXTENSION_FILES := \
src/*.png \
src/*.svg \
Expand All @@ -31,8 +32,16 @@ EXTENSION_FILES := \
src/js/offscreen.dist.js \
src/js/options.dist.js \
src/js/inject.dist.js

# Thunderbird-specific files
THUNDERBIRD_EXTRA_FILES := \
src/thunderbird/experiment/*.js \
src/thunderbird/experiment/*.json
THUNDERBIRD_EXTRA_FILES := $(wildcard $(THUNDERBIRD_EXTRA_FILES))

CHROMIUM_FILES := $(patsubst src/%,chromium/%, $(EXTENSION_FILES))
FIREFOX_FILES := $(patsubst src/%,firefox/%, $(EXTENSION_FILES))
THUNDERBIRD_FILES := $(patsubst src/%,thunderbird/%, $(EXTENSION_FILES)) $(patsubst src/%,thunderbird/%, $(THUNDERBIRD_EXTRA_FILES))

.PHONY: chromium
chromium: extension $(CHROMIUM_FILES) chromium/manifest.json
Expand All @@ -56,6 +65,17 @@ firefox/manifest.json : src/manifest-firefox.json
[ -d $(dir $@) ] || mkdir -p $(dir $@)
cp $< $@

.PHONY: thunderbird
thunderbird: extension $(THUNDERBIRD_FILES) thunderbird/manifest.json

$(THUNDERBIRD_FILES) : thunderbird/% : src/%
[ -d $(dir $@) ] || mkdir -p $(dir $@)
cp $< $@

thunderbird/manifest.json : src/manifest-thunderbird.json
[ -d $(dir $@) ] || mkdir -p $(dir $@)
cp $< $@

#######################
# For official releases

Expand All @@ -75,13 +95,14 @@ crx-github:
mv chromium.crx browserpass-github.crx

.PHONY: dist
dist: clean extension chromium firefox crx-webstore crx-github
dist: clean extension chromium firefox thunderbird crx-webstore crx-github
mkdir -p dist

git -c tar.tar.gz.command="gzip -cn" archive -o dist/browserpass-extension-$(VERSION).tar.gz --format tar.gz --prefix=browserpass-extension-$(VERSION)/ $(VERSION)

(cd chromium && zip -r ../dist/browserpass-chromium-$(VERSION).zip *)
(cd firefox && zip -r ../dist/browserpass-firefox-$(VERSION).zip *)
(cd chromium && zip -r ../dist/browserpass-chromium-$(VERSION).zip *)
(cd firefox && zip -r ../dist/browserpass-firefox-$(VERSION).zip *)
(cd thunderbird && zip -r ../dist/browserpass-thunderbird-$(VERSION).zip *)

mv browserpass-webstore.crx dist/browserpass-webstore-$(VERSION).crx
mv browserpass-github.crx dist/browserpass-github-$(VERSION).crx
Expand Down
17 changes: 16 additions & 1 deletion PRIVACY.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ This Privacy Policy applies to Browserpass and Browserpass OTP.
## Usage of Credential Files

During the course of normal operation, Browserpass handles decrypted Credential Files.
Only files selected by the User via the Browserpass interface are decrypted.
Only files selected by the User via the Browserpass interface are decrypted. In Thunderbird,
Credential Files are decrypted when Thunderbird requests authentication for email accounts.

The contents of decrypted Credential Files are used *only* for the following purposes:

Expand All @@ -26,6 +27,20 @@ The contents of decrypted Credential Files are used *only* for the following pur
- To provide the User with an interface to edit the contents of a selected Credential File,
- To provide the OTP seed to Browserpass OTP
- To fill other fields as requested by the User (e.g. credit card data)
- To authenticate email and news accounts in Thunderbird (IMAP, SMTP, POP3, NNTP);
- To provide OAuth2 tokens for mail providers (Gmail, Microsoft, etc.) and
calendar/contacts services (CalDAV/CardDAV) in Thunderbird.

**In Thunderbird, credentials are never stored in Thunderbird's built-in password manager.**
All credentials are retrieved directly from the Password Store using GPG decryption.

When the User enters new credentials in Thunderbird (e.g., during account setup), Browserpass
may save these credentials to the Password Store. OAuth tokens obtained during authentication
are automatically stored in the Password Store for future use.

Browserpass may migrate existing credentials from Thunderbird's password manager to the
Password Store when the extension is first installed. This migration does not overwrite
existing entries in the Password Store.

## Use & Transmission of Data

Expand Down
47 changes: 46 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

Browserpass is a browser extension for [zx2c4's pass](https://www.passwordstore.org/), a UNIX based password store manager. It allows you to auto-fill or copy to clipboard credentials for the current domain, protecting you from phishing attacks.

Browserpass also supports Mozilla Thunderbird, providing password storage and autofill for email accounts (IMAP, SMTP, POP3) and OAuth2 tokens (Gmail, Microsoft, Fastmail).

In order to use Browserpass you must also install a [companion native messaging host](https://github.com/browserpass/browserpass-native), which provides an interface to your password store.

![demo](https://user-images.githubusercontent.com/1177900/56079873-87057600-5dfa-11e9-8ff1-c51744c75585.gif)
Expand All @@ -12,6 +14,8 @@ In order to use Browserpass you must also install a [companion native messaging

- [Requirements](#requirements)
- [Installation](#installation)
- [Browser extension](#browser-extension)
- [Thunderbird extension](#thunderbird-extension)
- [Verifying authenticity of the Github releases](#verifying-authenticity-of-the-github-releases)
- [Updates](#updates)
- [Usage](#usage)
Expand Down Expand Up @@ -43,7 +47,7 @@ In order to use Browserpass you must also install a [companion native messaging

## Requirements

- The latest stable version of Chromium or Firefox, or any of their derivatives.
- The latest stable version of Chromium, Firefox, or Thunderbird (128.0+), or any of their derivatives.
- The latest stable version of gpg (having `pass` or `gopass` is actually not required).
- A password store that follows certain [naming conventions](#organizing-password-store)

Expand All @@ -52,6 +56,10 @@ In order to use Browserpass you must also install a [companion native messaging
In order to install Browserpass correctly, you have to install two of its components:

- [Native messaging host](https://github.com/browserpass/browserpass-native#installation)
- Browser or Thunderbird extension (see below)

### Browser extension

- Browser extension for Chromium-based browsers (choose one of the options):
- Install using a package manager for your OS (which will provide auto-update and keep extension in sync with native host app):
- Arch Linux: [browserpass-chromium](https://www.archlinux.org/packages/community/any/browserpass-chromium/), [browserpass-chrome](https://aur.archlinux.org/packages/browserpass-chrome/)
Expand All @@ -69,6 +77,19 @@ In order to install Browserpass correctly, you have to install two of its compon
- Install the extension from [Firefox Add-ons](https://addons.mozilla.org/en-US/firefox/addon/browserpass-ce/) (which will provide auto-updates)
- Download `browserpass-firefox.zip` from the latest release, unarchive and use `Load Temporary Add-on` on `about:debugging#addons` (remember the extension will be removed after browser is closed!).

### Thunderbird extension

Thunderbird requires a separate build due to the experimental APIs needed for credential interception. When Thunderbird requests credentials, Browserpass intercepts the request and retrieves them from your pass store using GPG decryption. **Credentials are never stored in Thunderbird's password manager.**

1. **Install Native Host**: Follow the [native messaging host](https://github.com/browserpass/browserpass-native) installation, then run `make hosts-thunderbird-user`
2. **Build Extension**: Run `make thunderbird` in the browserpass-extension directory
3. **Create XPI Package**:
```bash
cd thunderbird
zip -r browserpass-thunderbird.xpi *
```
4. **Install in Thunderbird**: Open Add-ons and Themes (`Ctrl+Shift+A`), click the gear icon, select "Install Add-on From File", and select the XPI file

### Verifying authenticity of the Github releases

All release files are signed with a PGP key that is available on [maximbaz.com](https://maximbaz.com/), [keybase.io](https://keybase.io/maximbaz) and various OpenPGP key servers. First, import the public key using any of these commands:
Expand Down Expand Up @@ -121,6 +142,29 @@ Browserpass was designed with an assumption that certain conventions are being f
work.gpg
```

For Thunderbird, credentials are organized by protocol in subdirectories:

```
~/.password-store/
imap/
mail.example.com.gpg # IMAP server credentials
smtp/
mail.example.com.gpg # SMTP server credentials
https/
accounts.google.com.gpg # OAuth identity provider credentials
oauth/
mail/
user@gmail.com.gpg # OAuth mail tokens
caldav/
user@gmail.com.gpg # Calendar OAuth tokens
```

If IMAP and SMTP use the same password, you can use a symlink:
```bash
cd ~/.password-store/smtp/
ln -s ../imap/mail.example.com.gpg mail.example.com.gpg
```

1. Password must be defined on a line starting with `password:`, `pass:` or `secret:` (case-insensitive), and if all of these are absent, the first line in the password entry file is considered to be a password.

1. Username must be defined on a line starting with `login:`, `username:`, or `user:` (case-insensitive), and if all of these are absent, default username as configured in browser extension or in `.browserpass.json` of specific password store, and finally if everything is absent the file name is considered to be a username.
Expand Down Expand Up @@ -405,6 +449,7 @@ See below the list of available `make` goals (check Makefile for more details).
| `make extension` | Compile the extension source code |
| `make chromium` | Compile the extension source code, prepare unpacked extension for Chromium |
| `make firefox` | Compile the extension source code, prepare unpacked extension for Firefox |
| `make thunderbird` | Compile the extension source code, prepare unpacked extension for Thunderbird |
| `make crx` | Compile the extension source code, prepare packed extension for Chromium |

### Load an unpacked extension
Expand Down
4 changes: 2 additions & 2 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ PRETTIER := node_modules/.bin/prettier
LESSC := node_modules/.bin/lessc

CLEAN_FILES := css js
PRETTIER_FILES := $(wildcard *.json *.js popup/*.js offscreen/*.js options/*.js *.less popup/*.less options/*.less *.html popup/*.html offscreen/*.html options/*.html)
PRETTIER_FILES := $(wildcard *.json *.js popup/*.js offscreen/*.js options/*.js thunderbird/experiment/*.js thunderbird/experiment/*.json *.less popup/*.less options/*.less *.html popup/*.html offscreen/*.html options/*.html)

.PHONY: all
all: deps prettier css/popup.dist.css css/options.dist.css js/background.dist.js js/popup.dist.js js/offscreen.dist.js js/options.dist.js js/inject.dist.js
Expand All @@ -24,7 +24,7 @@ css/options.dist.css: $(LESSC) options/*.less
[ -d css ] || mkdir -p css
$(LESSC) options/options.less css/options.dist.css

js/background.dist.js: $(BROWSERIFY) background.js helpers/*.js
js/background.dist.js: $(BROWSERIFY) background.js helpers/*.js thunderbird.js
[ -d js ] || mkdir -p js
$(BROWSERIFY) -o js/background.dist.js background.js

Expand Down
106 changes: 106 additions & 0 deletions src/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -1309,3 +1309,109 @@ function onExtensionInstalled(details) {
});
}
}

// =============================================================================
// Thunderbird Support
// =============================================================================
// When running in Thunderbird, this extension intercepts credential requests
// from different protocols (IMAP, SMTP, POP3, NNTP) and OAuth authentication flows.
// The experimental API in implementation.js hooks into Thunderbird's auth
// system and emits events that we handle here.

let thunderbirdModule = null;

/**
* Initializes Thunderbird support if running in Thunderbird.
* Sets up listeners for credential requests and storage events.
*/
(function initThunderbird() {
if (!helpers.isThunderbird()) {
return;
}

try {
thunderbirdModule = require("./thunderbird");
console.log("Browserpass: Thunderbird mode enabled");

/**
* Listener for credential requests from Thunderbird.
* Called when Thunderbird needs credentials for IMAP, SMTP, POP3, or other protocols.
* Returns matching credentials from the pass store or empty list if none found.
*
* @param {object} credentialInfo - Information about the credential request
* @param {string} credentialInfo.host - The host requesting credentials
* @param {string} credentialInfo.username - Optional username hint
* @returns {object} Result with autoSubmit flag and array of matching credentials
*/
browser.credentials.onCredentialRequested.addListener(async function (credentialInfo) {
try {
const settings = await getFullSettings();
settings.appID = appID;
return await thunderbirdModule.handleCredentialRequest(settings, credentialInfo);
} catch (error) {
console.error("Error handling credential request:", error);
return { autoSubmit: false, credentials: [] };
}
});

/**
* Listener for new credential storage requests.
* Called when Thunderbird has new credentials to store (e.g., after successful login).
* Stores the credentials to the pass store for future use.
*
* @param {object} credentialInfo - Information about the credentials to store
* @param {string} credentialInfo.host - The host these credentials are for
* @param {string} credentialInfo.login - The username
* @param {string} credentialInfo.password - The password or OAuth token
* @returns {boolean} True if credentials were successfully stored
*/
browser.credentials.onNewCredential.addListener(async function (credentialInfo) {
try {
const settings = await getFullSettings();
settings.appID = appID;
return await thunderbirdModule.handleNewCredential(settings, credentialInfo);
} catch (error) {
console.error("Error handling new credential:", error);
return false;
}
});

/**
* Listener for messages from content scripts and other extension components.
* Handles special "saveOAuthToken" action to store OAuth tokens obtained from browser windows.
*
* @param {object} request - The message request object
* @param {string} request.action - The action to perform (e.g., "saveOAuthToken")
* @param {string} request.host - The host the token is for
* @param {string} request.login - The username associated with the token
* @param {string} request.token - The OAuth token to store
* @param {object} sender - Information about the message sender
* @param {function} sendResponse - Callback to send response back to sender
* @returns {boolean} True to indicate the response will be sent asynchronously
*/
browser.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if (request.action === "saveOAuthToken") {
(async function () {
try {
console.log("Browserpass: Saving OAuth token for:", request.host);
const settings = await getFullSettings();
settings.appID = appID;
const result = await thunderbirdModule.handleNewCredential(settings, {
host: request.host,
login: request.login,
password: request.token,
});
sendResponse({ success: result });
} catch (error) {
console.error("Error saving OAuth token:", error);
sendResponse({ success: false });
}
})();
return true;
}
return false;
});
} catch (error) {
console.error("Browserpass: Failed to initialize Thunderbird support:", error);
}
})();
11 changes: 11 additions & 0 deletions src/helpers/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ module.exports = {
getSetting,
ignoreFiles,
isChrome,
isThunderbird,
makeTOTP,
parseAuthUrl,
prepareLogin,
Expand Down Expand Up @@ -96,6 +97,16 @@ function isChrome() {
return chrome.runtime.getURL("/").startsWith("chrom");
}

/**
* Returns true if running in Thunderbird.
* Checks for the browser.credentials API which is only available in Thunderbird.
*
* @return boolean
*/
function isThunderbird() {
return typeof browser !== "undefined" && browser.credentials !== undefined;
}

/**
* Get the deepest available domain component of a path
*
Expand Down
Loading