A secure, self-hosted password manager application built with React, Node.js, and PostgreSQL - all containerized with Docker.
Pre-built images available on Docker Hub - no need to clone and build locally:
# Quick start with Docker Hub images
curl -O https://raw.githubusercontent.com/Liionboy/password-manager/master/docker-compose.hub.yml
# Edit .env with your settings
docker compose -f docker-compose.hub.yml up -dOr manually:
docker run -d \
-p 1532:8080 \
-e DB_PASSWORD=your_secure_password \
-e JWT_SECRET=your_jwt_secret \
adrianbrisca/password-manager:latestDocker Hub Images:
- Backend: https://hub.docker.com/r/adrianbrisca/password-manager
- Frontend: https://hub.docker.com/r/adrianbrisca/password-manager-frontend
- Secure Authentication - JWT access sessions kept in memory, with refresh-session revocation in PostgreSQL
- Two-Factor Authentication (TOTP) - Add an extra layer of security with authenticator apps
- Password Recovery - Forgot/reset password via email
- Change Password - Users can change their password from Profile page
- AES-256 Encryption - Personal vault data uses per-user AES-256-GCM derived from the master password; legacy/team data remains backward-compatible
- Password Generator - Customizable password generator (length, uppercase, lowercase, numbers, symbols)
- Password Strength Validation - Minimum 8 characters with uppercase, lowercase, numbers, and special characters
- Account Lockout - Automatic account lockout after 5 failed login attempts (15 minutes)
- Rate Limiting - Protection against brute force attacks
- Security Headers - Helmet.js for enhanced security headers
- Password Generator - Customizable password generator (length, uppercase, lowercase, numbers, symbols)
- Categories - Organize your passwords and cards with custom categories (global - visible to all users)
- Folders - Organize passwords and cards in nested folders
- Search - Real-time search across all your passwords and cards
- Import/Export - Export passwords to JSON and import from other password managers (including Bitwarden)
- Copy to Clipboard - One-click copy functionality
- Bank Cards - Store and manage your credit/debit cards with auto-brand detection
- Email Notifications - Get notified via email when passwords or cards are added, updated, or deleted (SMTP)
- Global SMTP - Admin can configure SMTP for all users
- Teams - Create teams and share passwords/folders with team members
- Role-Based Access - Admin and user roles with different permissions
- Admin Panel - Admin can manage users, teams, and settings
- Responsive Design - Works on desktop and mobile devices
| Component | Technology |
|---|---|
| Frontend | React 18 + Vite |
| Backend | Node.js + Express |
| Database | PostgreSQL 15 |
| Authentication | JWT + bcrypt |
| Encryption | AES-256 (crypto-js) |
| Nodemailer (SMTP) | |
| Container | Docker + Nginx |
- Docker installed
- Docker Compose installed
- Clone the repository:
git clone https://github.com/Liionboy/password-manager.git
cd password-manager- Start the application:
docker compose build --no-cache- Open your browser and navigate to:
http://localhost:1532
- On an empty database, set
ALLOW_FIRST_ADMIN=truein.envand start the application. - Register the first account. That account becomes administrator.
- Immediately set
ALLOW_FIRST_ADMIN=falseand restart the backend. - Login with your credentials and start adding passwords.
No default administrator account or password is created.
The access token and master password are kept only in browser memory; they are
not persisted in localStorage. After a full page reload, sign in again to
unlock the vault. The master password is sent only as a transient
X-Master-Password header over the application connection so the backend can
derive the per-user AES-GCM key; never expose the application over plain HTTP.
- Click the + Add Password button (in the Passwords tab)
- Fill in the required fields (Title, Password)
- Optionally add: username, URL, category, notes
- Use the Generate button to create a strong password
- Click the Cards tab
- Click + Add Card
- Fill in the required fields (Title, Card Number)
- Card brand (Visa, Mastercard, etc.) is auto-detected
- Length - Choose between 4-64 characters
- A-Z - Include uppercase letters
- a-z - Include lowercase letters
- 0-9 - Include numbers
- !@# - Include special symbols
- Export - Downloads all your passwords as a JSON file
- Import - Paste JSON data to import passwords from other sources (supports Bitwarden export format)
- Click Settings in the header (admin only)
- For production, configure SMTP in the server
.envfile so the password is never entered or stored in the browser:SMTP_HOST- e.g.,smtp.gmail.comSMTP_PORT- e.g.,587(TLS) or465(SSL)SMTP_USER- Your email addressSMTP_PASS- Your SMTP/App PasswordSMTP_FROM- e.g.,Password Manager <your@email.com>
- Enable notifications for add/update/delete events in the Settings page.
- Click Send Test Email in Settings to verify the server-side configuration.
- Click Teams to access team management
- Create a team - Give it a name (e.g., "Marketing", "IT")
- Add members - As team admin, click "Manage Members" β "Add Member"
- Create team folders - When creating a folder, select a team (admin only)
- Share passwords - Passwords in team folders are visible to all team members
The first registered user becomes the admin. Admin capabilities:
- Users page - Create, edit (change role), reset password, and delete users
- Teams page - Create teams, add/remove members, delete teams
- Settings page - Manage email notification preferences and test server-side SMTP
- Team folders - Assign folders to teams for team visibility
- Click Profile in the header
- Click Enable 2FA
- Scan the QR code with your authenticator app (Google Authenticator, Authy, Microsoft Authenticator, etc.)
- Enter the 6-digit code from your app to verify and enable 2FA
At login:
- Enter username and password
- Enter the 6-digit code from your authenticator app
You can disable 2FA anytime from the Profile page.
- Click Forgot your password? on the login page
- Enter your username
- Check your email for the reset link
- Click the link and set a new password
- Login with your new password
- Click Profile in the header
- Enter your current password and new password
- Click Save to update your password
password-manager/
βββ docker-compose.yml # Docker orchestration
βββ nginx.conf # Nginx configuration
βββ backend/
β βββ Dockerfile # Backend container
β βββ package.json # Node.js dependencies
β βββ src/
β βββ index.js # Express server
β βββ middleware/
β β βββ auth.js # JWT authentication
β βββ routes/
β β βββ auth.js # Auth endpoints
β β βββ passwords.js # Password CRUD
β β βββ cards.js # Card CRUD
β β βββ settings.js # SMTP settings
β βββ utils/
β βββ crypto.js # AES encryption
βββ frontend/
βββ Dockerfile # Frontend container (React + Nginx)
βββ package.json # React dependencies
βββ vite.config.js # Vite configuration
βββ src/
βββ App.jsx # Main application
βββ api.js # API client
βββ index.css # Styles
βββ main.jsx # Entry point
βββ pages/
βββ Login.jsx # Login page
βββ Register.jsx # Registration page
βββ Dashboard.jsx # Main dashboard
βββ PasswordForm.jsx # Add/Edit password
βββ CardForm.jsx # Add/Edit card
βββ Settings.jsx # SMTP settings
The following environment variables can be configured in docker-compose.yml:
| Variable | Description | Default |
|---|---|---|
JWT_SECRET |
Secret key for JWT tokens | your-super-secret-jwt-key-change-in-production |
ENCRYPTION_KEY |
Legacy/team data key; keep stable during migrations | 32+ random characters |
DB_HOST |
PostgreSQL host | postgres |
DB_USER |
PostgreSQL user | postgres |
DB_PASSWORD |
PostgreSQL password | postgres |
DB_NAME |
PostgreSQL database name | passwordmanager |
BASE_URL |
Base URL for password reset links | http://localhost:5173 |
SMTP_HOST |
SMTP server hostname; takes precedence over database SMTP settings | unset |
SMTP_PORT |
SMTP port (587 for TLS or 465 for SSL) |
587 |
SMTP_USER |
SMTP login username/email | unset |
SMTP_PASS |
SMTP password or provider app password | unset |
SMTP_FROM |
Sender address/name | SMTP_USER |
Security Note: Generate unique random
JWT_SECRET,REFRESH_SECRET, andENCRYPTION_KEYvalues in production. KeepENCRYPTION_KEYstable so old and team-shared records remain decryptable.
You can tune Argon2id cost via environment variables:
ARGON2_MEMORY_COST(default19456)ARGON2_TIME_COST(default2)ARGON2_PARALLELISM(default1)
Legacy bcrypt hashes are still accepted and will be transparently upgraded to Argon2id after successful login.
- All passwords and card numbers are encrypted using AES-256 before storage
- JWT tokens expire after 15 minutes (MFA temp tokens after 5 minutes)
- Password hashing uses Argon2id for new/updated passwords; existing bcrypt hashes remain compatible
- Passwords and card data are never stored in plain text
- PostgreSQL database is stored in a Docker volume for persistence
- Password strength validation (minimum 8 chars + 3 character types)
- Account lockout after 5 failed login attempts (15 minutes)
- Rate limiting on all endpoints (100 req/15min general, 10 req/15min auth)
- Helmet.js for security headers (HSTS, X-Frame-Options, etc.)
- Use strong, unique passwords for your master account
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/auth/register |
Register new user |
| POST | /api/auth/login |
Login user |
| GET | /api/auth/verify |
Verify token |
| GET | /api/auth/me |
Get current user profile |
| PUT | /api/auth/profile |
Update user profile (email, password) |
| POST | /api/auth/forgot-password |
Request password reset |
| POST | /api/auth/reset-password |
Reset password with token |
| POST | /api/auth/mfa/setup |
Generate MFA QR code |
| POST | /api/auth/mfa/enable |
Enable MFA with verification code |
| POST | /api/auth/mfa/disable |
Disable MFA with verification code |
| POST | /api/auth/mfa/verify-temp |
Verify MFA code after login |
| POST | /api/auth/refresh |
Rotate refresh token and get new access token |
| POST | /api/auth/logout |
Logout current client |
| POST | /api/auth/logout-all |
Revoke all user sessions |
| GET | /api/auth/sessions |
List refresh sessions for current user |
| POST | /api/auth/sessions/:id/revoke |
Revoke one refresh session |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/passwords |
Get all passwords |
| POST | /api/passwords |
Create password |
| PUT | /api/passwords/:id |
Update password |
| DELETE | /api/passwords/:id |
Delete password |
| POST | /api/passwords/generate |
Generate password |
| GET | /api/passwords/export |
Export passwords |
| POST | /api/passwords/import |
Import passwords |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/cards |
Get all cards |
| POST | /api/cards |
Create card |
| PUT | /api/cards/:id |
Update card |
| DELETE | /api/cards/:id |
Delete card |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/passwords/categories |
Get all categories |
| POST | /api/passwords/categories |
Create category |
| DELETE | /api/passwords/categories/:id |
Delete category |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/settings |
Get user settings |
| PUT | /api/settings |
Save SMTP settings (admin only) |
| POST | /api/settings/test-email |
Send test email |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/teams |
Get user's teams |
| GET | /api/teams/all |
Get all teams (admin only) |
| POST | /api/teams |
Create a team |
| POST | /api/teams/join |
Disabled; a team administrator must add members |
| DELETE | /api/teams/:id |
Delete a team |
| GET | /api/teams/:id/members |
Get team members |
| POST | /api/teams/:id/members |
Add member to team |
| DELETE | /api/teams/:id/members/:userId |
Remove member from team |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/auth/users |
Get all users (admin only) |
| POST | /api/auth/users |
Create user (admin only) |
| PUT | /api/auth/users/:id |
Update user role (admin only) |
| DELETE | /api/auth/users/:id |
Delete user (admin only) |
| POST | /api/auth/users/:id/reset-password |
Reset user password (admin only) |
# Build and start containers
docker compose up --build
# Start in detached mode
docker compose up -d
# Stop containers
docker compose down
# View logs
docker compose logs -f
# Rebuild specific service
docker compose build backend
docker compose build frontendBackup/restore is implemented for the PostgreSQL Docker volume (password-manager_pgdata) with OpenSSL encryption.
scripts/backup-encrypted.shβ creates encrypted backup (.enc) + checksum (.sha256)scripts/restore-encrypted.sh <file.enc>β restores from encrypted backupscripts/test-restore.shβ smoke test: backup β restore β/api/healthcheck
cd /home/adrian/.openclaw/workspace/password-manager
BACKUP_PASSPHRASE='set-a-strong-passphrase' ./scripts/backup-encrypted.shOutput is stored in ./backups/ (ignored by git).
cd /home/adrian/.openclaw/workspace/password-manager
BACKUP_PASSPHRASE='set-a-strong-passphrase' ./scripts/restore-encrypted.sh ./backups/<backup-file>.enccd /home/adrian/.openclaw/workspace/password-manager
BACKUP_PASSPHRASE='set-a-strong-passphrase' ./scripts/test-restore.shIf successful, script prints: Restore test passed (health=200).
- CI workflow:
.github/workflows/e2e-regression.yml - Matrix:
docs/SECURITY_REGRESSION_MATRIX.md
The E2E workflow validates:
- stack health (
/api/health) - security headers presence (CSP, X-Frame-Options, X-Content-Type-Options)
- auth rate limiting (expects HTTP
429on repeated failed login) - encrypted backup script smoke run
This project is licensed under the MIT License - see the LICENSE file for details.
Created by Liionboy
Made with β€οΈ for secure password management