...
Self-hosted two-factor authentication (2FA) app with web interface and cross-device synchronization.
demo.webm
live demo: login β dashboard β add token
- Secure TOTP generation - Compatible with Google Authenticator, Authy, 1Password, etc.
- End-to-end encryption - TOTP secrets encrypted with AES-256
- WebAuthn/Passkey support - Passwordless login with biometrics (Touch ID, Face ID, Windows Hello)
- Security key authentication - YubiKey, Titan, and other FIDO2 hardware keys
- Cross-device authentication - Scan QR code with phone to login from desktop
- Mandatory 2FA - Optional account protection during registration
- Backup codes - 10 emergency codes per account for recovery
- JWT authentication - Secure 30-day session tokens
- Registration control - Enable/disable new user signups
- Passwordless login - Use biometrics or security keys instead of passwords
- Multiple authenticators - Register YubiKey, Touch ID, Android biometrics simultaneously
- Cross-platform support - Works on desktop, mobile, and hardware keys
- Platform authenticators - Touch ID (Mac/iOS), Face ID (iOS), Windows Hello
- Roaming authenticators - YubiKey, Google Titan, USB security keys
- QR code authentication - Scan with phone to authenticate on desktop
- Security key management - Add, rename, and remove keys in Settings
- Live search - Instant filtering as you type
- Multi-field search - Find by account name or issuer/app name
- Result counter - Shows "X of Y" matches
- Expandable search bar - Smooth animations in navbar
- Perfect for 100+ entries - Fast client-side filtering
- Modern design - Clean, intuitive interface
- Dark mode - Automatic or manual theme switching
- Responsive layout - Works on desktop, tablet, and mobile browsers
- Progress indicators - Visual countdown for code expiration
- Hide codes - Optional privacy mode (auto-hide after 10 seconds)
- Bulk operations - Select and delete multiple entries
- JSON format - Standard TOTP export compatible with most apps
- otpauth URI - Import from Google Authenticator, Authy, FreeOTP+, 2FAuth
- Bulk import - Add multiple accounts at once
- Replace or merge - Choose how to handle existing entries
- Cross-device sync - Access codes from any device via web browser
- Self-hosted backend - Full control over your data
- PostgreSQL or SQLite - Choose your preferred database
- Docker Compose - One-command deployment
- Easy updates - Pull and rebuild without data loss
- Environment variables - Simple configuration
- Reverse proxy ready - Works with Nginx, Caddy, Traefik
TOTP Sync supports two database backends - choose based on your needs:
Best for: Multi-user deployments, production environments, high availability
DATABASE_TYPE=postgresql
POSTGRES_DB=totp_sync
POSTGRES_USER=totp_user
POSTGRES_PASSWORD=your_secure_password
DATABASE_URL=postgresql://totp_user:your_password@postgres:5432/totp_syncFeatures:
- β Full ACID compliance
- β Concurrent user support
- β Advanced querying capabilities
- β Battle-tested for production
Best for: Personal use, simple deployments, single-user setups
DATABASE_TYPE=sqlite
SQLITE_PATH=/data/totp-sync.dbFeatures:
- β Zero configuration
- β Single file database
- β Perfect for home labs
- β Easy backups (just copy the file)
Simply change DATABASE_TYPE in your .env file and restart:
docker compose down
docker compose up -dNote: Data is not automatically migrated between databases. Export your entries before switching.
- Docker & Docker Compose
- Git
- Clone the repository:
git clone https://github.com/PrzemekSkw/totp-sync.git
cd totp-sync- Create environment file:
cp .env.example .env- Generate secure secrets:
# JWT Secret (copy the output)
openssl rand -base64 32
# Encryption Key (copy the output)
openssl rand -hex 16- Edit
.envfile:
nano .envReplace the following values:
POSTGRES_PASSWORD: Set a strong database passwordJWT_SECRET: Paste the JWT secret from step 3ENCRYPTION_KEY: Paste the encryption key from step 3 (must be exactly 32 characters)DATABASE_URL: Update with the same password as POSTGRES_PASSWORD
Example:
POSTGRES_PASSWORD=my_secure_password_here
JWT_SECRET=1NRBJQja1Q1qjOw7LRXu2hDvm74HA5GbRWJ3yaL9GqM=
ENCRYPTION_KEY=91797e61a84e73c9dd5f78161f568ae4
DATABASE_URL=postgresql://totp:my_secure_password_here@postgres:5432/totpOptional WebAuthn configuration (for passwordless login):
RP_NAME="TOTP Sync"
RP_ID="yourdomain.com"
ORIGIN="https://yourdomain.com"Note: WebAuthn requires HTTPS in production. Use localhost for local testing.
- Start the application:
docker compose up -d- Access the application:
Open http://localhost:5173 in your browser
Important Notes:
- The
.envfile is ignored by git and won't be overwritten during updates - Always backup your
.envfile before major updates - Keep your secrets secure and never commit them to version control
- WebAuthn requires HTTPS in production (except localhost)
To update to the latest version:
git pull
docker compose down
docker compose up -d --buildYour .env file and database will be preserved during updates.
Backend configuration in docker-compose.yml:
| Variable | Description | Default | Required |
|---|---|---|---|
REQUIRE_2FA_ON_REGISTER |
Force 2FA setup during registration | "true" |
No |
ALLOW_REGISTRATION |
Enable/disable new user registration | "true" |
No |
JWT_SECRET |
Secret for JWT token signing | - | Yes |
ENCRYPTION_KEY |
Key for encrypting TOTP secrets (must be 32 chars) | - | Yes |
POSTGRES_PASSWORD |
Database password | - | Yes |
DATABASE_URL |
PostgreSQL connection string | - | Yes |
RP_NAME |
WebAuthn relying party name | "TOTP Sync" |
No |
RP_ID |
WebAuthn relying party ID (your domain) | "localhost" |
No |
ORIGIN |
WebAuthn origin (full URL with protocol) | "http://localhost:5173" |
No |
For passwordless authentication to work properly:
Local Development:
RP_ID="localhost"
ORIGIN="http://localhost:5173"Production:
RP_ID="yourdomain.com"
ORIGIN="https://yourdomain.com"Important:
RP_IDmust match your domain (without protocol or port)ORIGINmust be the full URL users access- HTTPS is required in production (localhost works with HTTP)
- Create
.well-known/webauthnfile for domain verification
- 5173 - Web interface
- 3000 - Backend API
Mandatory 2FA (default):
REQUIRE_2FA_ON_REGISTER: "true"Users must set up 2FA during registration with QR code and backup codes.
Optional 2FA:
REQUIRE_2FA_ON_REGISTER: "false"Users can enable 2FA later in Settings.
Open Registration (default):
ALLOW_REGISTRATION: "true"Anyone can create a new account.
Closed Registration:
ALLOW_REGISTRATION: "false"New user registration is disabled. Only existing users can login.
- Always change default passwords - Generate strong, unique passwords
- Generate new secrets - Never use example secrets in production
- Use HTTPS in production - Required for WebAuthn, recommended for all traffic
- Store backup codes safely - Save them in a secure password manager
- Regular backups - Back up the PostgreSQL volume regularly
- Keep updated - Pull latest changes and rebuild regularly
- WebAuthn best practices - Register multiple authenticators (YubiKey + biometrics)
For production use:
- Use a reverse proxy with SSL/TLS certificates
- Configure WebAuthn with your domain
- Change default ports
- Use Docker secrets for sensitive values
- Set up monitoring and logging
- Regular security updates
- Consider disabling registration after initial setup
- Scan QR code with any authenticator app
- Receive 10 backup codes for emergency access
- Verify setup with 6-digit code before account creation
- Enter email and password
- Automatically prompted for 2FA code when enabled
- Use backup codes if authenticator unavailable
- Platform authenticators: Touch ID, Face ID, Windows Hello
- Roaming authenticators: YubiKey, Google Titan, USB keys
- Cross-device: Scan QR code with phone to login from desktop
- No passwords needed: Completely passwordless authentication
- Enable/disable 2FA in Settings
- Generate new backup codes
- Requires password + current 2FA code to disable
- Add multiple authenticators (YubiKey, Touch ID, phone)
- Name your keys for easy identification
- Remove keys you no longer use
- See last used date for each key
- JSON - Standard TOTP export format
- otpauth URI - Compatible with Google Authenticator, Authy, FreeOTP+, etc.
- Export from your current 2FA app (Google Authenticator, Authy, FreeOTP+, 2FAuth, etc.)
- In TOTP Sync, click "Import"
- Select your export file or paste URIs
- Choose "Replace All" or "Merge" with existing entries
- Your entries will be encrypted and synced
Check logs:
docker compose logs -fEnsure PostgreSQL is healthy:
docker compose ps- Use one of your backup codes instead of TOTP code
- If no backup codes, you'll need to reset the database
- Always save backup codes in a safe place!
- Check HTTPS: WebAuthn requires HTTPS (except localhost)
- Verify RP_ID: Must match your domain exactly
- Check browser support: Use Chrome, Firefox, Safari, or Edge (latest versions)
- Clear browser data: Sometimes cached credentials cause issues
- Check .well-known/webauthn: File must be accessible and return JSON
- Ensure key is FIDO2/WebAuthn compatible
- Try a different USB port
- Update key firmware if available
- Check browser permissions
- Clear browser cache and localStorage
- Try incognito/private browsing mode
- Check browser console for errors (F12)
totp-sync/
βββ backend/ # Node.js + Express API
β βββ src/
β β βββ routes/ # API endpoints
β β βββ services/ # Business logic
β β βββ middleware/ # Auth & validation
β βββ Dockerfile
βββ web/ # React + Vite frontend
β βββ src/
β β βββ components/ # React components
β β βββ pages/ # Page views
β β βββ services/ # API client
β β βββ store/ # State management
β βββ public/
β β βββ .well-known/webauthn # WebAuthn domain verification
β βββ Dockerfile
βββ docker-compose.yml
# Backend
cd backend
npm install
npm run dev
# Frontend
cd web
npm install
npm run dev- β¨ WebAuthn/Passkey support - Passwordless login with biometrics and security keys
- β¨ Cross-device authentication - Scan QR code with phone to login from desktop
- β¨ Security key management - Add/remove YubiKey, Touch ID, Windows Hello in Settings
- β¨ Registration control -
ALLOW_REGISTRATIONenvironment variable - π Enhanced security - FIDO2/WebAuthn standard support
- π± Platform authenticators - Touch ID, Face ID, Windows Hello, Android biometrics
- π Roaming authenticators - YubiKey, Google Titan, USB security keys
- π .well-known/webauthn - Domain verification support
- π Updated documentation - WebAuthn setup guide and troubleshooting
- π Security upgrade - Migrated to Node.js native crypto module
- π AES-256-GCM encryption - Upgraded from AES-256-CBC with authentication
- ποΈ SQLite support - Choose between PostgreSQL and SQLite
- π³ Node.js 20 - Updated runtime for better performance
- β Zero vulnerabilities - Clean npm security audit
β οΈ Breaking change - Encryption system overhaul (migration required)
- β Added search functionality - Live filtering by account name and issuer
- β Expandable search bar - Smooth animations with auto-focus
- β Result counter - Shows "X of Y" matches when searching
- β Empty state - Clear messaging when no results found
- β Performance - Fast client-side filtering for 100+ entries
- β Fixed 2FA login functionality - Now working correctly
- β Fixed registration with 2FA - Proper pendingData handling
- β Improved UI - Removed unnecessary icons, added custom branding
- β Better error handling - Clear error messages and validation
- Initial release
- Basic TOTP generation
- Docker setup
- π± Mobile app - Native iOS and Android applications
- π Push notifications - Real-time sync alerts
- π Multi-language support - Internationalization
- π Usage statistics - Analytics dashboard
- π Advanced security - Rate limiting, IP whitelisting
- π Folders/Categories - Organize TOTP entries
- π·οΈ Tags - Label and filter entries
- π Advanced search - Regex and filters
- π€ Auto-backup - Scheduled exports
- π¨ Themes - Custom color schemes
MIT License - feel free to use this project for personal or commercial purposes.
Contributions are welcome! Feel free to open issues or submit pull requests.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
If you find this project useful, you can support its development:
Support via BuyMeCoffe:
Support via PayPal:
Support via Github Sponsors:
Your support helps maintain and improve this project. Thank you! β€οΈ
If you find this project useful, please consider giving it a star on GitHub!
Made with β€οΈ by PrzemekSkw
Secure your accounts with self-hosted 2FA




