Secure offline 2FA desktop authenticator
Project description
🛡️ Desktop-2FA
A secure, offline two-factor authentication (2FA) manager for desktop environments. Built with Python, featuring strong encryption and no cloud dependencies.
🌐 Landing Page: desktop-2fa.org
✨ Features
| Feature | Description |
|---|---|
| 🔐 Vault Security | AES-256-GCM encryption with Argon2id key derivation |
| ⏱️ TOTP Generation | RFC 6238 compliant code generation |
| 💻 Full CLI | Complete command-line interface for managing tokens |
| 📋 Clipboard Support | Automatic copying of TOTP codes to clipboard |
| 🔓 Stateless Design | Every command requires explicit password authentication |
| 🛡️ Password Policy | Configurable password strength enforcement with zxcvbn |
| 🧪 Well Tested | 289 tests passing with comprehensive coverage |
| 📖 Security Model | Detailed threat analysis and cryptographic design documentation |
📸 Screenshots
Adding a new TOTP entry interactively
Generating a TOTP code for an entry
Renaming an entry with duplicate detection
Viewing version info and listing all entries
🚀 Quick Start
Installation
pip install desktop-2fa
Verify installation:
python -c "import desktop_2fa; print(desktop_2fa.__version__)"
# Output: 0.8.1dev
Basic Usage
# Add a new TOTP token
d2fa add GitHub GitHub JBSWY3DPEHPK3PXP
# List all entries
d2fa list
# Generate a code
d2fa code GitHub
# Initialize a new vault
d2fa init-vault
Non-Interactive Usage
# Provide password via command line
d2fa --password mypassphrase add GitHub GitHub JBSWY3DPEHPK3PXP
# Provide password via file
d2fa --password-file /path/to/passphrase.txt add GitHub GitHub JBSWY3DPEHPK3PXP
📁 Vault Lifecycle
The vault is an encrypted storage file located at ~/.desktop-2fa/vault.
Vault Creation
When a command requires a vault and none exists:
- The CLI prompts for a new password (interactive mode) or requires
--password/--password-file(non-interactive mode) - An empty encrypted vault is created
- A confirmation message is always printed:
Vault created at <path>
Vault Loading
When a command requires a vault and it exists:
- The CLI prompts for the existing password (interactive mode) or requires credentials (non-interactive mode)
- The vault is decrypted and loaded
- If the password is invalid, the CLI exits with
typer.Exit(1)
Duplicate Entry Handling
The rename command enforces deterministic behavior when multiple entries match the target name:
- If multiple entries match the provided name (issuer or account_name), the rename is aborted
- Error message:
Error: Multiple entries named '<name>' exist. Operation aborted. Resolve duplicates first. - No entry is renamed in this case
- This check occurs before any mutation
📖 CLI Commands
Core Commands
| Command | Description |
|---|---|
d2fa add <name> <issuer> <secret> |
Add a new TOTP entry |
d2fa list |
List all stored TOTP entries |
d2fa code <name> [--copy|--copy-only] |
Generate TOTP code with clipboard copy options |
d2fa rename <old> <new> |
Rename an entry |
d2fa remove <name> |
Remove an entry |
Vault Management
| Command | Description |
|---|---|
d2fa init-vault [--force] |
Initialize a new vault |
d2fa unlock-vault |
Open vault with non-blocking weak password warning |
d2fa change-password |
Change the vault password |
d2fa backup |
Create an encrypted backup of the vault |
Import/Export
| Command | Description |
|---|---|
d2fa export <path> |
Export vault entries to JSON |
d2fa import <path> [--force] |
Import entries from JSON |
Global Options
| Option | Description |
|---|---|
--password <pwd> |
Provide vault password directly |
--password-file <path> |
Read vault password from file |
--json |
Output in JSON format |
--raw |
Raw output (no formatting) |
--quiet |
Suppress non-essential output |
--copy |
Copy TOTP code to clipboard and display |
--copy-only |
Copy TOTP code to clipboard only |
--force |
Force operation (bypass confirmations) |
🔒 Security
The vault uses:
- AES-256-GCM for authenticated encryption
- Argon2id for key derivation (time_cost=4, memory_cost=128MiB, parallelism=2)
- zxcvbn for password strength evaluation
- Versioned header for forward compatibility
Every command requires explicit password authentication. No session-based access.
Password Strength Evaluation
Desktop-2FA uses zxcvbn to evaluate password strength when creating or changing vault passwords:
- Score 0-2 (Weak): Easily guessable passwords (e.g., "password123")
- Score 3-4 (Strong): Resistant to common attacks (recommended)
Examples:
- ❌ Weak:
password,123456,qwerty,admin2024 - ✅ Strong:
Battery-Horse-Staple-Correct,Mountain@River*2024,MySecureVault#42
Configuration (~/.config/d2fa/config.toml):
[security]
# Require strong passwords (score >= 3)
reject_weak_passwords = false # warn but allow | true: reject weak passwords
When reject_weak_passwords = false:
- Weak passwords trigger a yellow warning
- User is prompted to confirm continuation
- Password acceptance is non-blocking
When reject_weak_passwords = true:
- Weak passwords are rejected immediately
- Command exits with error
- User must choose a stronger password
Backward Compatibility:
If you have min_password_entropy set in your config (legacy option), it is recognized and treated as equivalent to requiring zxcvbn score >= 3.
Security Hardening (v0.8.0)
Version 0.8.0 maintains all previous security hardening from v0.7.3 and introduces modular architecture for better code isolation:
- Empty passwords are immediately rejected with a clear error message
- Permission errors are distinguished from missing vault files
- No Python stack traces are shown to users
- User-friendly error messages for filesystem permission issues
- Modular design separates CLI and core cryptographic components for enhanced security boundaries
- zxcvbn-based password strength evaluation (new in v0.8.0)
📚 Documentation
| Document | Description |
|---|---|
| User Manual | Complete usage guide |
| Security Model | Detailed threat analysis and cryptographic design |
| CLI UX Specification | UX contract and behavior |
| Cryptography | Security implementation details |
🧪 Testing
pytest tests/ # Run all tests
pytest --cov=src/desktop_2fa # Run with coverage
🏗️ Project Structure
src/desktop_2fa/
├── app/ # Application components
├── cli/ # Command-line interface
├── crypto/ # Encryption utilities
├── totp/ # TOTP generation
├── ui/ # User interface components
├── vault/ # Vault management
└── utils/ # Utilities
📄 License
Apache License 2.0. See LICENSE file.
👤 Author
Łukasz Perek
🏆 Sponsorship
desktop‑2fa is supported through the Kilo OSS Sponsorship Program.
📧 Contact
For questions or support, contact us at contact@desktop-2fa.org
💖 Support the Project
Desktop‑2FA is an independent open‑source tool built with a focus on autonomy, transparency, and offline security. If you find it useful and want to support ongoing development, you can do so through the platforms below:
- Ko‑fi: https://ko-fi.com/lukaszperek
- Buy Me a Coffee: https://buymeacoffee.com/lukaszperek
- AirTM: https://airtm.me/lukper
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file desktop_2fa-0.8.1.tar.gz.
File metadata
- Download URL: desktop_2fa-0.8.1.tar.gz
- Upload date:
- Size: 331.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
347b3eaf9d213160a9fd25cfa75acebaf7751048dea98c657eba2fb1eba8caa2
|
|
| MD5 |
96c87cc42dd7284b99eff42131f620ab
|
|
| BLAKE2b-256 |
c525194d538ae07fd42c65f882b4b53decafbfa9fd4cae594a6a16c7578495b1
|
File details
Details for the file desktop_2fa-0.8.1-py3-none-any.whl.
File metadata
- Download URL: desktop_2fa-0.8.1-py3-none-any.whl
- Upload date:
- Size: 38.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
094d70b26e4650974ab5ec2600373a3c86b55fa5021ec1592a4ba0b1a9989d94
|
|
| MD5 |
d119e79bba80aafffa8ab0cbb125ff6d
|
|
| BLAKE2b-256 |
ef0fbe7af79739c942516686422f3f86128f73bb5f3ce0e8025284eff26267a2
|