A browser extension that manages your TOTP (2FA) accounts and fills one-time codes for you, automatically.
I built it for myself, with a few simple goals:
- it saves my time;
- it's easy and convenient to use;
- it's mine, so I don't have to trust other browser extensions.
- Encryption. Set a master password during, or run unencrypted if you prefer.
- TOTP account management. Manual / Import / Export.
- Site bindings. Bind an account to where it is used:
example.commatches the host and all subdomains,*.example.commatches subdomains only. The current tab's domain is suggested for you. - Autofill. When exactly one account is bound to a page (and autofill is on), the OTP field is found and filled automatically. It re-fills with a fresh code when the period rolls over, and if the page wipes the value. The moment you edit the field yourself, it backs off and stays out of your way. A subtle ring flash and a small "filled in" badge confirm the fill.
- In-page account picker. When two or more accounts match a page (or autofill is off), a popover with live codes opens right under the OTP field. Arrow keys to move, Enter to fill, Esc to dismiss.
- Hotkeys. Refer to the UI for the bindings.
- Themes. Dark / Light.
- Multi-language support. English / Russian / Simplified Chinese. (LLM translated)
npm install
npm run build # Chrome build
npm run build:firefox # Firefox buildChrome / Brave
- Open
chrome://extensions(orbrave://extensions). - Enable Developer mode.
- Click Load unpacked and select the
.output/chrome-mv3folder.
Firefox
- Open
about:debugging#/runtime/this-firefox. - Click Load Temporary Add-on and pick the manifest inside
.output/firefox-mv3.
npm run dev # Chrome dev build with HMR
npm run dev:firefox # Firefox dev build
npm run zip # packaged zip for distribution
npm test # run tests
npm run compile # type-check
npm run lint # lintDrop a file on the import dropzone (formats are auto-detected) or paste a link into the form.
| Format | Notes |
|---|---|
.2fa |
This extension's own encrypted backup. Asks for the backup password. |
Aegis JSON (.json) |
Plaintext Aegis Authenticator export. TOTP only; HOTP skipped. Encrypted Aegis exports are rejected with a clear error. |
Text (.txt) |
One or more otpauth:// or otpauth-migration:// URIs. |
| QR image | .png / .jpg / .jpeg / .webp, decoded locally. Supports otpauth:// and Google Authenticator export QR (otpauth-migration://, bulk). |
| Pasted link | Paste an otpauth:// or otpauth-migration:// link directly into the form. |
Imports deduplicate: an account you already have (same issuer, label and secret) is silently skipped. A bulk import finishes on a summary screen showing how many were imported and how many were skipped, with reasons.
The export dialog (Settings -> Backup -> Export & transfer) offers three formats. Pick the one that fits your goal.
| Format | What you get | Sensitive? |
|---|---|---|
| Encrypted | An AES-256-GCM encrypted .2fa file. The default and recommended option for backups. |
Password-protected |
| QR | One or more otpauth-migration:// QR codes you scan into Google Authenticator or any compatible app. Large vaults split across several codes ("Code 1 of N") - scan each one in order, or save the codes as PNG images ("Save QR" for the current one, "Save all" for every code). |
Plaintext - unencrypted |
| Raw | A 2fa-accounts.txt file containing one otpauth://totp/... URI per account, one per line. Any authenticator that accepts otpauth:// links can import it. |
Plaintext - unencrypted |
Encrypted format details:
- The file is a JSON envelope holding the version, KDF parameters (PBKDF2-SHA-256, 600,000 iterations), a random salt and IV, and the AES-256-GCM ciphertext.
- Exporting always asks for a backup password (at least 8 characters), which can differ from your master password. Importing asks for that same backup password.
- Encryption. With a master password set, your vault is encrypted with AES-256-GCM. The key is derived with PBKDF2-HMAC-SHA-256 at 600,000 iterations using a random salt and a fresh IV per encryption. Your password is never stored.
- Running without a password. You can skip encryption. Accounts are then stored unencrypted in extension storage and the UI shows a permanent warning so you always know.
- Secrets stay in one place. Only the background service worker ever sees secrets or generates codes. The popup and in-page UI receive codes through a typed message channel and nothing else. A content script can only request codes for accounts bound to the page it actually runs on, and that page URL is verified by the background, not trusted from the caller.
- Session key. The unlocked session key lives in session storage, which clears when the browser closes, and is never reachable from content scripts.
- Auto-lock. The vault auto-locks after idle (1 / 5 / 15 minutes or never, default 5). Five wrong passwords trigger a 60-second lockout.
- Resetting. Recovery is not possible so the only option is wipe whole storage before creating a new one.
- Permissions. The extension requests
storage,alarms,activeTab, and host access to all sites so the content script can run wherever you bind an account. On any page with no matching binding it exits immediately. - Plaintext export formats. The QR and Raw export formats produce unencrypted output. Google
Authenticator QR codes and
otpauth://link files both expose your raw TOTP secrets to anyone who scans or opens them. Neither format includes site bindings. Treat them like a password file: delete them once transferred and never store or send them unprotected. - No network. Zero network requests. No cloud, no sync, no telemetry.




