Are you tired of being locked out of your accounts when you reset your phone? I was. I wanted a 2FA authenticator that would sync my codes seamlessly across devices, but I didn't want to hand over my plaintext secrets to a closed-source cloud provider.
So, I built OtpVault โ an open-source, truly zero-knowledge 2FA application.
In this post, I want to share the architecture, the tech stack, and how I approached the security model.
๐ ๏ธ The Tech Stack
To make this work across Desktop (Windows), Android, and the Web (PWA), I needed a flexible yet highly secure stack:
Backend / Core Logic: Rust (Using aes-gcm, argon2, and totp-rs). Rust ensures memory safety and handles the heavy cryptographic lifting.
Desktop & Android Framework: Tauri v2. It allows wrapping the Rust backend with a web frontend natively and beautifully.
Frontend: React 19 + Vite + Tailwind CSS.
Cloud API: Neon PostgreSQL via Vercel Serverless.
๐ The Zero-Knowledge Architecture
The core philosophy of OtpVault is simple: The server must never see the plaintext data.
Here is how the email-based authentication flow works:
Local Key Derivation: When you sign up or log in, your password and a randomly generated 32-byte salt are fed into Argon2id strictly on the client side (in Rust). This generates a strong 256-bit AES key.
Vault Encryption: Your entire vault of 2FA secrets is encrypted locally using AES-256-GCM.
Cloud Sync: The application sends a POST request to the Supabase/Neon REST API. The payload only contains your email, the salt, a test payload (to verify passwords later), and the opaque encrypted vault blob.
If the database is ever breached, the attacker gets nothing but meaningless, encrypted strings.
๐ Solving the Cross-Platform Sync Problem
One of the coolest challenges was keeping the Desktop, Android, and PWA versions in sync without compromising the encryption.
I implemented a real-time polling mechanism (every 10 seconds). The Rust HTTP client fetches the encrypted vault from the Vercel API, decrypts it locally using the master key in memory, parses the JSON, merges any new accounts, re-encrypts, and pushes it back.
I even had to build a format converter on the fly, because the Android version stores secrets as secret_encrypted (base64 salt+nonce+ciphertext), while the PWA handles plaintext secrets in memory before encrypting the entire vault layer.
๐ก๏ธ Ironclad Features
Delete Confirmation: To prevent accidental deletion, users must re-enter their master password before removing any 2FA account.
Optimized Mobile QR Scanner: Using jsQR and requestAnimationFrame, the scanner pulls frames at a low JPEG quality (saving battery and processing at ~30KB/frame) while remaining blazing fast.
๐ค Check it Out!
Building OtpVault was an incredible journey into Rust cryptography and Tauriโs cross-platform capabilities. Iโd love for the community to check it out, audit the code, or use it to secure your accounts.
GitHub Repository: [https://github.com/Shadow132245/OtpVault]
Website / Try it out: [https://otpvault1.vercel.app]
I'm open to any feedback, feature requests, or contributions. Let me know what you think of the architecture!
Top comments (1)
You open with being locked out after a phone reset, and then file "users must re-enter their master password before removing any 2FA account" under Ironclad Features near the bottom. Those are the same problem. Accidental deletion is how people actually lose 2FA access, more often than a lost phone is, and you built the guard for it without connecting it back to the thing you opened with. The zero-knowledge architecture is the headline, but that detail is what makes it read like it was built by someone who'd been burned.
Wrote you a launch post, free, use it or bin it:
"OtpVault is an open-source, zero-knowledge 2FA authenticator for Windows, Android and the web. Your password and a random 32-byte salt go through Argon2id on the client, in Rust, to derive a 256-bit key, and the whole vault is encrypted locally with AES-256-GCM. The server never sees your password or your unencrypted codes - the vault reaches it as an opaque encrypted blob, so a breach hands an attacker meaningless strings. Codes stay in sync across desktop, Android and PWA, and deleting an account requires your master password. Rust, Tauri v2, React."
Full disclosure so it isn't strange: I build a tool that writes these. Nothing to sign up for and nothing to click - the post is yours either way.