A self-hosted password manager with client-side AES encryption. Passwords are encrypted in the browser before being sent to the server — the backend never sees plaintext credentials.
- Client-side encryption — passwords are encrypted with AES (via
crypto-js) using a master key that never leaves your device - Vault system — a master key is required to unlock your vault; the server only stores the encrypted values + IV
- Authentication — email/password and Google OAuth via Better Auth
- Password generator — configurable random password generator
- Categories — organize passwords by type: email, API, social, game, bank, or other
- Dashboard — overview of your stored passwords and vault status
- Dark/light mode — theme toggle with
next-themes
| Runtime | Node.js (ESM) |
| Framework | Express v5 |
| Language | TypeScript |
| Database | MongoDB + Mongoose |
| Auth | Better Auth (email/password + Google OAuth) |
| Security | Helmet, CORS |
| Logging | Winston |
| Framework | React 19 + Vite |
| Language | TypeScript |
| Routing | React Router v7 |
| UI | shadcn/ui + Radix UI + Tailwind CSS v4 |
| Encryption | crypto-js (AES) |
| HTTP | Axios |
| Auth client | Better Auth |
- Node.js 20+
- MongoDB (local or Atlas)
- Google OAuth credentials (optional, for Google login)
git clone https://github.com/your-username/password-vault.git
cd password-vaultcd backend
cp .env.example .env # or create .env manually (see below)
npm install
npm run build
npm run start:dev # watches dist/ for changesbackend/.env
PORT=3000
BETTER_AUTH_URL=http://localhost:3000
BETTER_AUTH_SECRET=your_random_secret_here
MONGO_URI=mongodb://localhost:27017/password-vault
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
CLIENT_URL=http://localhost:5173
SERVER_URL=http://localhost:3000cd frontend
cp .env.example .env # or create .env manually
npm install
npm run devfrontend/.env
VITE_API_URL=http://localhost:3000App will be available at http://localhost:5173.
cd backend
npm testpassword-vault/
├── backend/
│ └── src/
│ ├── config/ # MongoDB connection
│ ├── controllers/ # Route handlers
│ ├── lib/ # Better Auth setup
│ ├── middlewares/ # Request logger, auth guard
│ ├── models/ # Mongoose schemas (User, Password)
│ ├── routes/ # Express routers
│ ├── types/
│ └── server.ts
└── frontend/
└── src/
├── components/ # Reusable UI components
├── context/ # React context providers
├── dialog/ # Modal dialogs
├── hooks/ # Custom hooks
├── layouts/ # Page layouts
├── pages/ # Dashboard, Passwords, Generator, Profile, Login, Register
├── services/ # API calls (Axios)
├── types/
└── routes.tsx
- On first login the user sets a master key (never stored on the server)
- When saving a password, the client generates a random IV, encrypts the value with
AES-CBCusing the master key, and sends{ encryptedValue, iv }to the backend - When reading passwords, the client decrypts them locally using the master key stored in session/memory
- The server stores only ciphertext — a database leak exposes no plaintext passwords
| Method | Path | Description |
|---|---|---|
* |
/api/auth/* |
Better Auth (login, register, session, OAuth) |
GET |
/api/passwords |
List user's passwords |
POST |
/api/passwords |
Save new password |
PUT |
/api/passwords/:id |
Update password |
DELETE |
/api/passwords/:id |
Delete password |
GET |
/api/users/me |
Get current user profile |
GET/POST |
/api/vault |
Vault initialization & status |