A secure, full-stack web application that lets individuals store digital assets and documents as a formal will, then automatically delivers everything to designated heirs when the owner becomes inactive.
Quick Start ยท Changelog ยท API Reference ยท Security ยท Contributors
- Overview
- Features
- Tech Stack
- Architecture
- Project Structure
- Database Schema
- Getting Started
- Configuration
- API Reference
- Security
- Dead Man's Switch
- Email System
- Admin Panel
- Frontend & Routing
- Testing
- Roadmap
- Contributing
- Contributors
- Team
- Changelog
- License
Wasiyya (ูุตููุฉ โ Arabic for will/testament) solves a critical problem: when someone passes away or becomes incapacitated, vital digital information โ bank accounts, passwords, crypto wallets, contracts โ is often lost forever.
- A user registers, creates a will, and adds their sensitive digital assets and documents
- They designate trustees (heirs) by name and email
- The system monitors user activity via a Dead Man's Switch โ the user must periodically confirm they are alive
- If they fail to check in within the configured grace period, the will is automatically triggered
- Every trustee receives a secure, time-limited email link granting access to the full will contents
| Role | Description |
|---|---|
user โ ู
ูุตู |
Creates and manages their own will, assets, documents, and trustees. Subject to the Dead Man's Switch. |
admin โ ู
ุฏูุฑ ุงููุธุงู
|
Manages the platform: users, roles, audit logs, system stats. Never accesses will content or decrypted data (Zero-Trust). |
manager โ ู
ุฑุงุฌุน ูุซุงุฆู |
Reviews and verifies document integrity across all users. Can run SHA-256 + RSA signature checks. Cannot download or read document content. |
๐ Account & Authentication
- Register with full name, email, and a strong password
- Password policy: 8+ characters, uppercase, digit, special character
- Passwords hashed with bcrypt (cost factor 12); legacy plaintext auto-migrated on first login
- JWT-based sessions (24-hour expiry)
- Two-Factor Authentication (TOTP) โ compatible with Google Authenticator and Authy
- OAuth login โ Sign in with Google or GitHub (existing accounts only โ no auto-registration on login)
- OAuth sign-up โ Register via Google or GitHub from
/register(?mode=register); new emails go to/register?oauth_token=โฆto complete name and optional password, then the account is linked to the provider - 2FA applies to OAuth login โ if the account has 2FA enabled, a TOTP code is required after provider authorization
๐ Will Management
- Create one digital will per account
- Configure a custom check-in interval and grace period
- Update will settings at any time
- Delete will (cascades to all assets, documents, beneficiaries)
๐ผ Digital Assets
Store structured records with the following types:
| Type | Use Case |
|---|---|
account |
Social media, email, subscriptions |
bank |
Account numbers, IBAN, routing info |
password |
Passwords and PINs |
info |
General important information |
note |
Personal messages or instructions |
- Content and title encrypted at rest (AES-256-GCM) in MySQL โ ciphertext stored with
ivHex:authTagHexIV columns - Per-user encryption key โ derived from
AES_SECRET_KEY+userId; only the account owner can decrypt viaGET /api/auth/wallet-key - Client-side decryption via Web Crypto API โ plaintext never sent over the API, never stored unencrypted in the DB
- Assets without an
ivcolumn (null) were created before encryption was introduced; displayed as-is until re-saved
๐ Document Storage
- Upload: PDF, JPG, PNG, GIF, TXT, DOC, DOCX (max 10 MB)
- Files encrypted at rest using AES-256-GCM; transparently decrypted on download
- Each file is SHA-256 hashed and RSA-2048 signed at upload time
- Integrity verification โ UI modal confirms hash match and signature validity
- Download and delete your own documents at any time
๐ฅ Beneficiary (Trustee) Management
- Add trustees: full name, email, phone, relationship
- View all trustees and their access status
- Remove trustees at any time
- Secure access tokens generated only when the will is triggered
โ Dead Man's Switch
- Real-time check-in status on the dashboard (elapsed time, remaining time, color-coded progress bar)
- "I'm OK" button available on every page
- Overdue warning banner across all user pages
- Automatic email warnings at configurable thresholds
- Receive a secure email when the will is triggered
- Access link valid for 7 days โ no account required
- View the full will: title, description, all assets, all documents
- First-access timestamp is recorded
See the Admin Panel section for full details.
| Package | Version | Purpose |
|---|---|---|
| Node.js | v20 | Runtime |
| Express | ^4.18.2 | HTTP framework |
| mysql2 | ^3.22.3 | MySQL driver (Promise API) |
| jsonwebtoken | ^9.0.2 | JWT generation and verification |
| bcrypt | ^6.0.0 | Password hashing (cost factor 12) |
| speakeasy | ^2.0.0 | TOTP 2FA secret generation and verification |
| qrcode | ^1.5.4 | QR code data URLs for 2FA setup |
| passport | ^0.7.0 | OAuth authentication middleware |
| passport-google-oauth20 | ^2.0.0 | Google OAuth 2.0 strategy |
| passport-github2 | ^0.1.12 | GitHub OAuth strategy |
| express-session | ^1.19.0 | Session store (OAuth redirect cycle only) |
| nodemailer | ^6.9.7 | Email delivery (SMTP) |
| node-cron | ^3.0.3 | Dead Man's Switch scheduler |
| multer | ^1.4.5-lts.1 | Multipart file upload handling |
| uuid | ^9.0.1 | UUID v4 primary key generation |
| helmet | ^7.1.0 | HTTP security headers |
| cors | ^2.8.5 | Cross-Origin Resource Sharing |
| express-rate-limit | ^7.1.5 | Rate limiting (500 req / 15 min) |
| express-validator | ^7.0.1 | Input validation and sanitization |
| dotenv | ^16.3.1 | Environment variable loading |
| nodemon | ^3.0.2 | Dev auto-restart (devDependency) |
| Package | Version | Purpose |
|---|---|---|
| React | ^18.2.0 | UI framework |
| react-router-dom | ^6.20.0 | Client-side routing |
| axios | ^1.6.0 | HTTP client with interceptors |
| Vite | ^5.0.0 | Build tool and dev server |
| Tailwind CSS | ^3.3.0 | Utility-first CSS framework |
| lucide-react | ^0.x | Icon library (sidebar, nav, buttons) |
| Tool | Purpose |
|---|---|
| MySQL 8.0 | Relational database |
| XAMPP | Local MySQL server + phpMyAdmin |
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ CLIENT LAYER โ
โ โ
โ React 18 SPA (Vite) Arabic RTL UI โ
โ โโโ React Router v6 โโโ Tailwind CSS โ
โ โโโ AuthContext (JWT store) โโโ Sidebar (role-aware) โ
โ โโโ Axios API client โโโ CheckinBanner โ
โ http://localhost:3000 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ REST / JSON
โ Authorization: Bearer <JWT>
โโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ API LAYER โ
โ โ
โ Express 4 โ http://localhost:3001 โ
โ โโโ Helmet (security headers) โ
โ โโโ CORS (FRONTEND_URL only) โ
โ โโโ Rate Limiter (500 req / 15 min) โ
โ โโโ Passport (Google + GitHub OAuth) โ
โ โโโ authenticate (JWT verify โ req.user) โ
โ โโโ authorize (RBAC role check) โ
โ โโโ Multer (file uploads โ /uploads) โ
โ โ
โ /api/auth /api/wills /api/assets โ
โ /api/documents /api/beneficiaries โ
โ /api/checkin /api/admin /api/health โ
โโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโฌโโโโโโโโโโโ
โ โ โ
โโโโโโโโโโผโโโโโโโโ โโโโโโโโโโโโผโโโ โโโโโโผโโโโโโโโโโโ
โ MySQL Database โ โ /uploads/ โ โ node-cron โ
โ (XAMPP :3306) โ โ AES-256 โ โ Dead Man's โ
โ โ โ encrypted โ โ Switch Cron โ
โ users โ โ โ โ โ
โ wills โ โโโโโโโโโโโโโโโ โ * * * * * โ
โ assets โ โ (test: /min) โ
โ documents โ โโโโโโโโโโโโโโโ โ 0 9 * * * โ
โ beneficiaries โ โ /certs/ โ โ (prod: 9am) โ
โ audit_logs โ โ RSA-2048 โ โ โ
โ checkin_notif โ โ (auto-gen) โ โโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
Browser Request
โ Helmet / CORS / Rate Limiter
โ authenticate() โ verifies JWT, loads req.user from DB
โ authorize(roles) โ checks req.user.role against allowed list
โ Controller โ business logic
โ pool.query() โ MySQL via mysql2
โ JSON Response โ { success, data } or { success, message }
wasiyya/
โโโ backend/
โ โโโ src/
โ โ โโโ config/
โ โ โ โโโ database.js # MySQL connection pool
โ โ โ โโโ multer.js # File upload config (types, size limits)
โ โ โ
โ โ โโโ controllers/
โ โ โ โโโ auth.controller.js # register, login (bcrypt + 2FA), getMe, logout, getWalletKey
โ โ โ โโโ twofa.controller.js # 2FA setup, enable, verify, disable (TOTP)
โ โ โ โโโ will.controller.js # CRUD โ wills
โ โ โ โโโ asset.controller.js # CRUD โ assets (AES-256-GCM encrypt on write)
โ โ โ โโโ document.controller.js # upload (encrypt + sign), download (decrypt), verify
โ โ โ โโโ beneficiary.controller.js # CRUD โ trustees + public token access
โ โ โ โโโ checkin.controller.js # check-in submit + status
โ โ โ โโโ admin.controller.js # user management, audit logs, stats, test tools
โ โ โ โโโ manager.controller.js # document metadata listing + integrity verification
โ โ โ
โ โ โโโ middleware/
โ โ โ โโโ auth.middleware.js # JWT verification โ req.user
โ โ โ โโโ rbac.middleware.js # Role-based access control
โ โ โ โโโ passport.js # Google + GitHub OAuth strategies
โ โ โ โโโ validate.middleware.js # express-validator error handler
โ โ โ
โ โ โโโ routes/
โ โ โ โโโ auth.routes.js # /login, /register, /2fa/*, /google, /github, /register/oauth
โ โ โ โโโ will.routes.js
โ โ โ โโโ asset.routes.js
โ โ โ โโโ document.routes.js
โ โ โ โโโ beneficiary.routes.js
โ โ โ โโโ checkin.routes.js
โ โ โ โโโ admin.routes.js
โ โ โ โโโ manager.routes.js # /manager/documents, /manager/documents/:id/verify, /manager/stats
โ โ โ
โ โ โโโ services/
โ โ โ โโโ checkin.service.js # Dead Man's Switch cron + trigger logic
โ โ โ โโโ email.service.js # Gmail / Ethereal SMTP + HTML templates
โ โ โ โโโ encryption.service.js # AES-256-GCM encrypt/decrypt buffers
โ โ โ โโโ signature.service.js # RSA-2048 keygen, SHA-256 hash, sign, verify
โ โ โ
โ โ โโโ app.js # Express entry โ middleware, routes, HTTPS
โ โ
โ โโโ uploads/ # AES-256-GCM encrypted user files (gitignored)
โ โโโ certs/ # SSL certs + RSA-2048 keys (auto-generated, gitignored)
โ โโโ .env # Local secrets (gitignored)
โ โโโ .env.example # Environment variable template
โ โโโ package.json
โ
โโโ frontend/
โ โโโ src/
โ โ โโโ pages/
โ โ โ โโโ Login.jsx # Login form + 2FA step + OAuth redirect handler (auto-dismiss errors)
โ โ โ โโโ Register.jsx # Registration form + OAuth buttons + profile completion step
โ โ โ โโโ Dashboard.jsx # Stats cards + check-in progress bar + will overview
โ โ โ โโโ MyWill.jsx # Will creation and settings
โ โ โ โโโ Assets.jsx # Asset management (AES-256-GCM in-browser decrypt)
โ โ โ โโโ Documents.jsx # Upload / download / delete + integrity verify modal
โ โ โ โโโ Beneficiaries.jsx # Trustee management
โ โ โ โโโ Verification.jsx # Manual check-in page
โ โ โ โโโ TwoFactorSetup.jsx # 2FA enable / disable + QR code setup
โ โ โ โโโ AdminPanel.jsx # Admin panel (5 tabs โ metadata only, no will content)
โ โ โ โโโ ManagerPanel.jsx # Document review panel (verify integrity, no download)
โ โ โ โโโ AccessDenied.jsx # 403 โ unauthorized role redirect page
โ โ โ โโโ NotFound.jsx # 404 โ page not found
โ โ โ โโโ BeneficiaryAccess.jsx # Public trustee access page (no login required)
โ โ โ
โ โ โโโ components/
โ โ โ โโโ Sidebar.jsx # Role-aware navigation (4 roles, mobile-responsive, lucide icons)
โ โ โ โโโ Navbar.jsx # Top bar with hamburger toggle for mobile sidebar
โ โ โ โโโ CheckinBanner.jsx # Overdue warning banner (polls every 30s)
โ โ โ โโโ ProtectedRoute.jsx # Redirects unauthenticated users to /login
โ โ โ โโโ RoleRoute.jsx # Shows AccessDenied for wrong-role access
โ โ โ
โ โ โโโ context/
โ โ โ โโโ AuthContext.jsx # JWT storage, user state, login/logout, registerOAuth()
โ โ โ โโโ SidebarContext.jsx # Mobile sidebar open/close state + overlay
โ โ โ
โ โ โโโ services/
โ โ โ โโโ api.js # Axios instance โ baseURL + auth header interceptor
โ โ โ
โ โ โโโ utils/
โ โ โ โโโ assetCrypto.js # Web Crypto API โ AES-256-GCM decrypt in browser
โ โ โ
โ โ โโโ App.jsx # Router setup + SmartRedirect
โ โ โโโ main.jsx # React DOM entry
โ โ
โ โโโ index.html
โ โโโ vite.config.js
โ โโโ tailwind.config.js
โ โโโ package.json
โ
โโโ database/
โ โโโ schema_mysql.sql # Full schema + seed data
โ
โโโ README.md
users (1) โโโโโโโโโโโโ (0..1) wills
wills (1) โโโโโโโโโโโโ (0..*) assets
wills (1) โโโโโโโโโโโโ (0..*) documents
wills (1) โโโโโโโโโโโโ (0..*) beneficiaries
users (1) โโโโโโโโโโโโ (0..*) audit_logs
users (1) โโโโโโโโโโโโ (0..*) checkin_notifications
users
CREATE TABLE users (
id CHAR(36) PRIMARY KEY,
full_name VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password VARCHAR(500) NOT NULL, -- bcrypt hash (cost 12)
role ENUM('admin','user','manager') DEFAULT 'user',
two_fa_secret VARCHAR(255) DEFAULT NULL, -- TOTP secret
two_fa_enabled TINYINT(1) DEFAULT 0,
oauth_provider VARCHAR(50) DEFAULT NULL, -- 'google' | 'github'
oauth_id VARCHAR(255) DEFAULT NULL,
last_checkin DATETIME DEFAULT CURRENT_TIMESTAMP,
is_active TINYINT(1) DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);wills
CREATE TABLE wills (
id CHAR(36) PRIMARY KEY,
user_id CHAR(36) NOT NULL,
title TEXT NOT NULL, -- AES-256-GCM ciphertext (Base64)
title_iv VARCHAR(120) DEFAULT NULL, -- ivHex:authTagHex
description TEXT, -- AES-256-GCM ciphertext (Base64)
description_iv VARCHAR(120) DEFAULT NULL, -- ivHex:authTagHex
checkin_interval_days INT DEFAULT 30,
grace_period_days INT DEFAULT 7,
status ENUM('active','triggered','expired') DEFAULT 'active',
triggered_at DATETIME DEFAULT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);Trigger logic: Cron checks
(NOW() โ last_checkin) โฅ (checkin_interval + grace_period).
WithTIME_UNIT=minutes, day values are treated as minutes.
assets
CREATE TABLE assets (
id CHAR(36) PRIMARY KEY,
will_id CHAR(36) NOT NULL,
asset_type ENUM('account','bank','password','info','note') NOT NULL,
title TEXT NOT NULL, -- AES-256-GCM ciphertext (Base64)
title_iv VARCHAR(120) DEFAULT NULL, -- ivHex:authTagHex
content TEXT NOT NULL, -- AES-256-GCM ciphertext (Base64)
iv VARCHAR(255) DEFAULT NULL, -- ivHex:authTagHex (NULL = legacy plaintext)
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (will_id) REFERENCES wills(id) ON DELETE CASCADE
);documents
CREATE TABLE documents (
id CHAR(36) PRIMARY KEY,
will_id CHAR(36) NOT NULL,
original_name VARCHAR(255) NOT NULL,
stored_name VARCHAR(255) NOT NULL, -- UUID-based filename on disk
stored_path VARCHAR(500) NOT NULL,
file_size BIGINT NOT NULL,
mime_type VARCHAR(100) NOT NULL,
sha256_hash VARCHAR(64) DEFAULT NULL, -- integrity hash
signature TEXT DEFAULT NULL, -- RSA-2048 digital signature
iv VARCHAR(255) DEFAULT NULL, -- AES-256-GCM IV:authTag
uploaded_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (will_id) REFERENCES wills(id) ON DELETE CASCADE
);beneficiaries
CREATE TABLE beneficiaries (
id CHAR(36) PRIMARY KEY,
will_id CHAR(36) NOT NULL,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
phone VARCHAR(50),
relationship VARCHAR(100),
access_token VARCHAR(500) DEFAULT NULL, -- 64-char hex, set at trigger time
token_expires DATETIME DEFAULT NULL, -- NOW() + 7 days
notified_at DATETIME DEFAULT NULL,
accessed_at DATETIME DEFAULT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (will_id) REFERENCES wills(id) ON DELETE CASCADE
);passkeys
CREATE TABLE passkeys (
id CHAR(36) PRIMARY KEY,
user_id CHAR(36) NOT NULL,
credential_id VARCHAR(512) NOT NULL,
public_key TEXT NOT NULL,
counter BIGINT UNSIGNED NOT NULL DEFAULT 0,
device_name VARCHAR(255) DEFAULT 'ุฌูุงุฒ',
transports VARCHAR(100) DEFAULT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
last_used_at DATETIME DEFAULT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
UNIQUE KEY uk_passkey_credential (credential_id)
);audit_logs & checkin_notifications
CREATE TABLE audit_logs (
id CHAR(36) PRIMARY KEY,
user_id CHAR(36),
action VARCHAR(255) NOT NULL,
details TEXT,
ip_address VARCHAR(50),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
);
CREATE TABLE checkin_notifications (
id CHAR(36) PRIMARY KEY,
user_id CHAR(36) NOT NULL,
notification_type ENUM('warning','final_warning','triggered'),
sent_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);Audited actions: USER_REGISTERED USER_LOGIN USER_LOGOUT WILL_CREATED WILL_UPDATED WILL_TRIGGERED ASSET_CREATED ASSET_DELETED DOCUMENT_UPLOADED DOCUMENT_DELETED BENEFICIARY_ADDED BENEFICIARY_REMOVED CHECKIN
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_wills_user_id ON wills(user_id);
CREATE INDEX idx_wills_status ON wills(status);
CREATE INDEX idx_assets_will_id ON assets(will_id);
CREATE INDEX idx_documents_will_id ON documents(will_id);
CREATE INDEX idx_beneficiaries_will ON beneficiaries(will_id);
CREATE INDEX idx_audit_user_id ON audit_logs(user_id);| Role | Password | |
|---|---|---|
| admin | admin@wasiyya.com |
Admin@123 |
| user | user@wasiyya.com |
User@123 |
| manager | manager@wasiyya.com |
Manager@123 |
Note: Seed passwords are stored as plaintext in the SQL file. Each account's password is automatically upgraded to bcrypt on first login. Change all credentials before any production deployment.
ุฃูุงู ุฑ ุงูุชุดุบูู ุงููุงู ูุฉ (ุนุฑุจู): docs/RUN.md โ Dockerุ DBุ HTTPSุ ูุงูุชุดุบูู ุงูููู ู.
| Requirement | Version |
|---|---|
| Node.js | v18 or higher |
| npm | v9 or higher |
| MySQL | 8.0 (via XAMPP or standalone) |
1. Clone the repository
git clone https://github.com/omar0y/wasiyya.git
cd wasiyya2. Set up the database
Option A โ XAMPP
- Start XAMPP โ click Start for MySQL
- Open
http://localhost/phpmyadmin - Create a new database named
wasiyya - Select it โ Import tab โ choose
database/schema_mysql.sqlโ Go
Option B โ Docker (MySQL + phpMyAdmin)
# MySQL on port 3306, phpMyAdmin on http://localhost:8081
docker start wasiyya-mysql wasiyya-phpmyadmin
# Import schema_mysql.sql via phpMyAdmin or:
# docker exec -i wasiyya-mysql mysql -uroot wasiyya < database/schema_mysql.sqlLogin to phpMyAdmin: user root, empty password (default).
3. Configure the backend
cd backend
cp .env.example .env # Linux/macOS
# or
copy .env.example .env # WindowsEdit .env โ see Configuration for all variables.
Minimum required values:
JWT_SECRET=<any random string, 32+ characters>
AES_SECRET_KEY=<run: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))">4. Install dependencies
# Backend
cd backend && npm install
# Frontend (new terminal)
cd frontend && npm install5. Start the servers
# Terminal 1 โ backend
cd backend && npm run dev
# Terminal 2 โ frontend
cd frontend && npm run dev6. Open the app
Navigate to http://localhost:3000
| Account | Password | Redirects to | |
|---|---|---|---|
| Admin | admin@wasiyya.com |
Admin@123 |
/admin |
| User | user@wasiyya.com |
User@123 |
/dashboard |
๐ Wasiyya backend โ http://localhost:3001
โฑ๏ธ TIME_UNIT = minutes
โฐ Checkin cron started โ ูู ุฏูููุฉ (ูุถุน ุงูุชูุณุช)
โ
MySQL connected โ wasiyya
- Install mkcert and trust the local CA (once โ asks for Mac password):
brew install mkcert
cd backend && npm run ssl:trust- Generate the app certificate (once):
cd backend && npm run ssl:generate- In
backend/.env, enable HTTPS and usehttps://URLs:
USE_HTTPS=true
FRONTEND_URL=https://localhost:3000
BACKEND_URL=https://localhost:3001
WEBAUTHN_ORIGIN=https://localhost:3000- Update OAuth apps (Google Cloud + GitHub) callback URLs to:
https://localhost:3001/api/auth/google/callbackhttps://localhost:3001/api/auth/github/callback
- Run backend and frontend (Vite picks up the same certs automatically):
cd backend && npm run dev
cd frontend && npm run devOpen https://localhost:3000. Accept the browser warning for the self-signed cert (or install the cert with mkcert).
Backend listens on https://localhost:3001; plain HTTP on port 3080 redirects to HTTPS.
All backend configuration is managed via backend/.env. Copy .env.example to get started.
# โโโ Server โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
PORT=3001
HTTP_PORT=3080 # HTTPโHTTPS redirect port (when SSL certs are present)
NODE_ENV=development
# โโโ Database โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWORD= # XAMPP default: empty
DB_NAME=wasiyya
# โโโ JWT โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
JWT_SECRET=your_secret_min_32_chars
JWT_EXPIRES_IN=24h
# โโโ Session (OAuth cycle only) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
SESSION_SECRET=another_random_secret
# โโโ AES-256-GCM document encryption โโโโโโโโโโโโโโโโโโโโโโโโโ
# Generate: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
AES_SECRET_KEY=64_hex_characters_here
# โโโ Email โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Option A โ Gmail (real emails)
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USER=your@gmail.com
EMAIL_PASS=your_app_password # myaccount.google.com/apppasswords
EMAIL_FROM=Wasiyya <your@gmail.com>
# Option B โ leave empty to use Ethereal (dev preview mode, no real emails)
EMAIL_USER=
EMAIL_PASS=
# โโโ URLs โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
FRONTEND_URL=http://localhost:3000
BACKEND_URL=http://localhost:3001
# โโโ OAuth โ Google โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# console.cloud.google.com โ Credentials
# Callback URI: http://localhost:3001/api/auth/google/callback
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
# โโโ OAuth โ GitHub โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# github.com/settings/developers โ New OAuth App
# Callback URL: http://localhost:3001/api/auth/github/callback
GITHUB_CLIENT_ID=your_github_client_id
GITHUB_CLIENT_SECRET=your_github_client_secret
# โโโ Uploads โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
MAX_FILE_SIZE=10485760 # 10 MB
UPLOAD_PATH=./uploads
# โโโ Dead Man's Switch โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
TIME_UNIT=minutes # 'minutes' = testing mode | 'days' = production
# โโโ HTTPS (optional) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
USE_HTTPS=true # or omit; auto-on when server.cert + server.key exist
SSL_CERT_PATH=./certs/server.cert
SSL_KEY_PATH=./certs/server.key| Variable | Required | Default | Description |
|---|---|---|---|
PORT |
No | 3001 |
API server port |
HTTP_PORT |
No | 3080 |
HTTP redirect port (SSL mode) |
DB_HOST/PORT/USER/PASSWORD/NAME |
Yes | โ | MySQL connection |
JWT_SECRET |
Yes | โ | Signing secret (min 32 chars) |
JWT_EXPIRES_IN |
No | 24h |
Token lifetime |
SESSION_SECRET |
No | (JWT_SECRET) | OAuth session secret |
AES_SECRET_KEY |
Yes | โ | 64-char hex for AES-256-GCM |
EMAIL_USER / EMAIL_PASS |
No | โ | Empty = Ethereal fallback |
FRONTEND_URL |
No | http://localhost:3000 |
Email links + OAuth redirect |
BACKEND_URL |
No | http://localhost:3001 |
OAuth callback base URL |
GOOGLE_CLIENT_ID/SECRET |
No | โ | Placeholder = Google disabled |
GITHUB_CLIENT_ID/SECRET |
No | โ | Placeholder = GitHub disabled |
TIME_UNIT |
No | days |
minutes testing / days production |
USE_HTTPS |
No | โ | Force HTTPS when true (needs certs) |
SSL_CERT_PATH / SSL_KEY_PATH |
No | ./certs/โฆ |
TLS cert/key (npm run ssl:generate) |
All endpoints return JSON.
- Success:
{ "success": true, "data": { ... } } - Error:
{ "success": false, "message": "..." }
Base URL: http://localhost:3001/api
// Request
{ "full_name": "Omar Abdelaal", "email": "omar@example.com", "password": "MyPass@123" }
// 201 Created
{ "success": true, "data": { "user": { "id": "...", "role": "user" }, "token": "eyJ..." } }Password rules: 8+ chars ยท 1 uppercase ยท 1 digit ยท 1 special character
| Status | Meaning |
|---|---|
201 |
Registered successfully |
400 |
Validation failure |
409 |
Email already registered |
// Request
{ "email": "omar@example.com", "password": "MyPass@123" }
// 200 OK โ standard login
{ "success": true, "data": { "user": { ... }, "token": "eyJ..." } }
// 200 OK โ 2FA required
{ "success": true, "requires2FA": true, "tempToken": "eyJ..." }When requires2FA: true, proceed to POST /auth/2fa/verify with the tempToken.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/auth/2fa/setup |
JWT | Generate TOTP secret + QR code |
POST |
/auth/2fa/enable |
JWT | Activate 2FA after scanning QR |
POST |
/auth/2fa/verify |
tempToken | Complete 2FA login step |
POST |
/auth/2fa/disable |
JWT | Disable 2FA with TOTP confirmation |
// GET /auth/2fa/setup โ response
{ "data": { "qrCode": "data:image/png;base64,...", "secret": "JBSWY3..." } }
// POST /auth/2fa/verify โ request
{ "tempToken": "eyJ...", "code": "123456" }| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/auth/google |
โ | Login via Google; append ?mode=register from /register page |
GET |
/auth/github |
โ | Login via GitHub; append ?mode=register from /register page |
GET |
/auth/oauth/pending?token= |
โ | OAuth sign-up โ fetch pre-filled email + name |
POST |
/auth/register/oauth |
โ | Complete sign-up { oauth_token, full_name, password? } โ links oauth_provider + oauth_id |
Login success: FRONTEND_URL/login?token=<jwt>&role=<role>
Login + 2FA enabled: FRONTEND_URL/login?requires2FA=1&tempToken=<jwt> โ Login.jsx shows OTP screen
Sign-up new email: FRONTEND_URL/register?oauth_token=<jwt> for profile completion
Sign-in with unknown email: FRONTEND_URL/login?error=account_not_found
Provider not configured: FRONTEND_URL/login?error=oauth_not_configured
GitHub OAuth App: set callback URL to
{BACKEND_URL}/api/auth/github/callbackโ Device Flow not required.
Standard profile fetch and logout (both require JWT).
GET /auth/wallet-key returns the per-user AES-256-GCM decryption key derived as HMAC-SHA256(AES_SECRET_KEY, userId) โ requires role: user. The browser passes this key to the Web Crypto API to decrypt asset content locally; the plaintext is never transmitted over the API.
Requires authentication. Users can only access their own will.
| Method | Endpoint | Description |
|---|---|---|
GET |
/wills |
Get current user's will (null if none) |
POST |
/wills |
Create a new will |
PUT |
/wills/:id |
Update will settings |
DELETE |
/wills/:id |
Delete will (cascades everything) |
// POST /wills โ request body
{
"title": "My Digital Will",
"description": "Instructions for my family",
"checkin_interval_days": 30,
"grace_period_days": 7
}Requires authentication, role
user. Will ownership verified on every request.
Responses returncontent_encrypted+ivโ decrypt in the browser usingGET /auth/wallet-key.
| Method | Endpoint | Description |
|---|---|---|
GET |
/assets/:willId |
List assets โ returns content (ciphertext) + iv; no plaintext |
POST |
/assets |
Add asset โ server encrypts content with AES-256-GCM before storage |
PUT |
/assets/:id |
Update title / type / content (re-encrypts on save) |
DELETE |
/assets/:id |
Delete an asset |
// POST /assets โ request body (plaintext content; stored encrypted)
{
"will_id": "uuid",
"asset_type": "bank",
"title": "HSBC Savings Account",
"content": "Account: 1234567890\nIBAN: GB12HSBC..."
}
// GET /assets/:willId โ response item (excerpt)
{
"id": "uuid",
"title": "HSBC Savings Account",
"content_encrypted": "k8J3mP9xQ2...",
"iv": "a3f1c9d2e4b1:8e2f1a0b9c3d"
}Asset content is encrypted with AES-256-GCM using a per-user key: HMAC-SHA256(AES_SECRET_KEY, userId). The API returns content (Base64 ciphertext) and iv (ivHex:authTagHex). The browser fetches the key from GET /auth/wallet-key and decrypts locally via the Web Crypto API โ plaintext never leaves the device. Assets without an iv (null) were created before encryption was introduced; they display as-is until the user re-saves them.
Asset types: account ยท bank ยท password ยท info ยท note
| Method | Endpoint | Description |
|---|---|---|
POST |
/documents/upload |
Upload file (multipart/form-data) |
GET |
/documents/:willId |
List all documents |
GET |
/documents/download/:id |
Download (auto-decrypted) |
POST |
/documents/verify/:id |
Verify integrity + signature |
DELETE |
/documents/:id |
Delete document + disk file |
// POST /documents/verify/:id โ response
{
"data": {
"intact": true,
"hash_match": true,
"signature_valid": true,
"stored_hash": "a3f1c9...",
"current_hash": "a3f1c9...",
"message": "ุงูู
ูู ุณููู
ูุงูุชูููุน ุตุญูุญ"
}
}Upload form fields: document (file) ยท will_id (UUID)
Allowed types: PDF ยท JPG ยท PNG ยท GIF ยท TXT ยท DOC ยท DOCX ยท Max: 10 MB
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/beneficiaries/:willId |
JWT | List trustees |
POST |
/beneficiaries |
JWT | Add a trustee |
DELETE |
/beneficiaries/:id |
JWT | Remove a trustee |
GET |
/beneficiaries/access/:token |
None | Public will access for heirs |
// GET /beneficiaries/access/:token โ 200 OK
{
"data": {
"will": { "title": "...", "description": "...", "triggered_at": "..." },
"assets": [ { "asset_type": "bank", "title": "...", "content": "..." } ],
"documents": [ { "id": "...", "original_name": "contract.pdf" } ]
}
}Error codes: 403 token expired or will not triggered ยท 404 token not found
| Method | Endpoint | Description |
|---|---|---|
POST |
/checkin |
Submit "I'm alive" โ updates last_checkin |
GET |
/checkin/status |
Get real-time check-in status |
// GET /checkin/status โ response
{
"data": {
"last_checkin": "2026-05-09T10:00:00.000Z",
"elapsed": 2,
"days_remaining": 35,
"is_overdue": false,
"time_unit": "minutes"
}
}All endpoints require
role: admin.
| Method | Endpoint | Description |
|---|---|---|
GET |
/admin/stats |
System-wide statistics |
GET |
/admin/users |
All users (newest first) |
PUT |
/admin/users/:id/toggle |
Enable/disable account |
PUT |
/admin/users/:id/role |
Change user role |
GET |
/admin/logs?page=1&limit=50 |
Paginated audit logs |
GET |
/admin/all-wills |
All wills with counts |
GET |
/admin/triggered-wills |
Triggered wills + beneficiary URLs |
GET |
/admin/time-unit |
Current time unit |
GET |
/admin/email-mode |
Email config mode |
POST |
/admin/force-check |
Run Dead Man's Switch check now |
POST |
/admin/reset-checkin/:id?ago=5 |
Set last_checkin N units ago |
POST |
/admin/reset-will/:id |
Reset triggered will to active |
All endpoints require
role: manager. Managers see document metadata across all users and can verify integrity. No download, no decrypted content, no stored paths are ever returned.
| Method | Endpoint | Description |
|---|---|---|
GET |
/manager/stats |
Aggregate document + will stats |
GET |
/manager/documents |
All documents with owner, will, hash, and signature โ never stored_path or iv |
POST |
/manager/documents/:id/verify |
Decrypt in-memory โ re-hash โ compare; returns verdict only |
// POST /manager/documents/:id/verify โ response
{
"intact": true,
"hash_match": true,
"signature_valid": true,
"stored_hash": "a3f1c9...",
"current_hash": "a3f1c9...",
"message": "โ
ุงูู
ูู ุณููู
ูุงูุชูููุน ุตุญูุญ"
}Every verification is recorded in audit_logs as MANAGER_VERIFY_DOC.
{ "status": "ok", "phase": 4, "time_unit": "minutes", "timestamp": "..." }| Feature | Implementation |
|---|---|
| Password Hashing | bcrypt cost-12; plaintext passwords auto-migrated on first login |
| JWT Authentication | HS256, 24h expiry, verified on every request |
| Two-Factor Auth (TOTP) | speakeasy โ compatible with Google Authenticator and Authy |
| OAuth (Google + GitHub) | Login: find-only by oauth_id or email โ no auto-registration. Sign-up via /register?mode=register with profile completion. 2FA applies after OAuth when enabled. |
| Asset Encryption | AES-256-GCM; per-user key (HMAC-SHA256(AES_SECRET_KEY, userId)); title + content encrypted at rest; client-side decrypt via Web Crypto API |
| Will Encryption | AES-256-GCM; will title + description encrypted at rest with per-user key; admin sees only ciphertext |
| Document Encryption | AES-256-GCM at rest; transparent decryption on download |
| Document Integrity | SHA-256 hash stored at upload (documents.sha256_hash) |
| Digital Signatures | RSA-2048 auto-generated key pair; each document signed at upload |
| Integrity Verification | /verify endpoint + UI modal confirms hash match + signature |
| HTTPS | Automatic when SSL certs exist; HTTPโHTTPS redirect on HTTP_PORT |
| RBAC | Role enforced after JWT verification; strict per-route role lists |
| Zero-Trust Admin | Admin sees only metadata (names, dates, counts). Will content (description), asset content, decrypted files, and beneficiary tokens are never returned to admin endpoints. |
| Manager Read-Only | Manager can verify document integrity (decrypt in-memory, return verdict). stored_path, iv, and file bytes are never exposed to the manager role. |
| Brute-Force Protection | In-memory lockout after 5 failed login attempts per email (15-minute cooldown). |
| Rate Limiting | 500 requests per 15-minute window per IP |
| Security Headers | Helmet sets 15+ HTTP headers (XSS, HSTS, CSP, etc.) |
| CORS | Restricted to FRONTEND_URL only |
| File Validation | Multer rejects disallowed MIME types; 10 MB hard cap |
| Token Expiry | Beneficiary access tokens expire after 7 days |
| Account Control | Admin can disable accounts; disabled users rejected even with valid JWT |
| Audit Trail | Every significant action logged with timestamp and IP address |
| 2FA Temp Token | Second-step login uses a short-lived JWT (5 min, pending2FA claim) |
Standard Login (no 2FA)
1. POST /api/auth/login โ server validates credentials via bcrypt.compare
2. Server signs JWT: { userId, role } โ expires in 24h
3. Client stores token in localStorage
4. All requests include: Authorization: Bearer <token>
5. authenticate() middleware verifies and loads req.user from DB
2FA Login Flow
1. POST /api/auth/login โ server detects two_fa_enabled = 1
2. Server returns: { requires2FA: true, tempToken }
tempToken = short-lived JWT (5 min) with { pending2FA: true }
3. Client shows OTP input screen
4. POST /api/auth/2fa/verify { tempToken, code }
5. Server verifies tempToken validity and TOTP code via speakeasy
6. Server issues full JWT โ client stores and proceeds normally
OAuth Flow โ Sign-in (Google / GitHub)
Login (/api/auth/google or /github):
1. User clicks OAuth button on /login โ /api/auth/google
2. requireOAuth guard: if provider not configured โ FRONTEND_URL/login?error=oauth_not_configured
3. Passport redirects to provider consent screen
4. Provider callbacks to /api/auth/google/callback
5. findOAuthUser: lookup by oauth_id โ else by email (links provider if matched)
6. No match โ failureRedirect FRONTEND_URL/login?error=account_not_found
7. If two_fa_enabled = 1 โ issue tempToken (5 min), redirect:
FRONTEND_URL/login?requires2FA=1&tempToken=<jwt>
8. Otherwise โ sign full JWT, redirect:
FRONTEND_URL/login?token=<jwt>&role=<role>
9. Login.jsx useEffect reads URL params; if requires2FA shows OTP screen
OAuth Flow โ Sign-up (Google / GitHub)
1. User clicks OAuth on /register โ /api/auth/google?mode=register
2. Passport redirects to provider consent screen
3. Provider callback
4. If email already registered โ FRONTEND_URL/login?error=account_not_found
5. Else issue short-lived oauth_token (15 min), redirect:
FRONTEND_URL/register?oauth_token=<jwt>
6. Register.jsx calls GET /api/auth/oauth/pending?token= to pre-fill name + email
7. User completes form, optionally sets a password
8. POST /api/auth/register/oauth โ creates account, links oauth_provider + oauth_id
9. Server signs JWT โ redirects to role-appropriate page
| Route Pattern | Auth | Role |
|---|---|---|
/api/auth/register ยท /api/auth/login |
None | โ |
/api/auth/2fa/verify |
tempToken | โ |
/api/auth/google ยท /api/auth/github (+ callbacks) |
None | โ |
/api/auth/oauth/pending ยท /api/auth/register/oauth |
None | โ |
/api/beneficiaries/access/:token ยท /api/beneficiaries/access/:token/document/:docId |
None | โ |
/api/health |
None | โ |
/api/auth/me ยท /api/auth/logout ยท /api/auth/2fa/* |
JWT | any |
/api/auth/wallet-key |
JWT | user only |
/api/wills ยท /api/assets ยท /api/documents ยท /api/checkin |
JWT | user only |
/api/admin/* |
JWT | admin only |
/api/manager/* |
JWT | manager only |
| Limitation | Notes |
|---|---|
| JWT not invalidatable before expiry | Refresh token / blacklist pattern not yet implemented |
| Asset decryption key via API | Owner must be logged in; key is fetched per-session, not cached in localStorage |
| No email verification | Users can register with any email |
| No password reset flow | No forgot-password / reset-via-email flow |
This is the core mechanic of Wasiyya.
Each will stores two timing values (treated as minutes when TIME_UNIT=minutes):
| Field | Default | Description |
|---|---|---|
checkin_interval_days |
30 | Inactivity period before first warning |
grace_period_days |
7 | Additional buffer before trigger |
Total time to trigger = interval + grace
// TIME_UNIT=minutes โ runs every minute
'* * * * *'
// TIME_UNIT=days โ runs daily at 9:00 AM
'0 9 * * *'For each active will:
elapsed = NOW() โ user.last_checkin
if elapsed โฅ interval + grace โ TRIGGER (email all beneficiaries)
if elapsed = interval + grace โ 1 โ Send FINAL WARNING to user
if elapsed = interval โ Send WARNING to user
UPDATE wills SET status = 'triggered', triggered_at = NOW()- For each beneficiary:
- Generate
access_token = crypto.randomBytes(32).toString('hex') - Set
token_expires = NOW() + 7 days - Send email with secure access link
- Generate
INSERT INTO audit_logs (WILL_TRIGGERED)
T+0:00 Will created (interval=3, grace=1)
T+3:00 Cron fires โ elapsed=3 โ Warning email sent
T+3:00 Cron fires โ elapsed=3 โ Final warning sent (3 = 3+1โ1)
T+4:00 Cron fires โ elapsed=4 โ TRIGGERED โ beneficiary emails sent
The email service auto-detects the provider at startup:
- Configured (real
EMAIL_USER/EMAIL_PASS) โ uses the specified SMTP server - Not configured (empty or placeholder values) โ auto-creates an Ethereal test account
| Trigger Condition | Appearance | |
|---|---|---|
| Warning | elapsed == interval |
Amber โ link to dashboard |
| Final Warning | elapsed == interval + grace โ 1 |
Red โ urgent |
| Beneficiary Notification | Will triggered | Green โ secure access link (7-day expiry) |
No configuration needed. Email preview URLs are stored in-memory and shown in the Admin Panel โ Test Tools tab. No emails reach real inboxes.
- Enable 2-Step Verification on your Google account
- Generate an App Password at
myaccount.google.com/apppasswords - Set in
.env:EMAIL_HOST=smtp.gmail.com EMAIL_PORT=587 EMAIL_USER=your@gmail.com EMAIL_PASS=your_16_char_app_password
Email failures are logged but never throw โ the cron check always completes.
Admins are redirected to /admin after login and have no access to the user-side will pages.
- Live stat cards: total/active users, wills, triggered wills, documents, assets
- Triggered wills list with owner names
- Last 10 audit log entries
- Full list of all users with name, email, role, status, last check-in
- Toggle โ enable/disable account (admin cannot disable themselves)
- Role dropdown โ change role instantly (
user/manager/admin)
- All wills across all users with owner info and counts
- Color-coded status badges: ๐ข active ยท ๐ด triggered ยท โซ expired
- Paginated log of every system action
- Color-coded by type: blue (auth) ยท green (create) ยท red (trigger/delete) ยท gray (check-in)
- Email mode badge (Gmail vs Ethereal)
- Time unit badge (minutes vs days)
- Force Check โ runs Dead Man's Switch immediately
- Reset Check-in โ set any user's
last_checkinto N units ago - Triggered Wills โ per-beneficiary notification status,
accessed_atindicator, Ethereal email previews, reset buttons
Security note: Beneficiary access tokens (
access_token) are never returned to the admin. The admin sees atoken_validboolean andaccessed_attimestamp only. Actual document content remains inaccessible to the admin role by design.
| Path | Component | Auth | Role | Description |
|---|---|---|---|---|
/login |
Login | โ | โ | Login + 2FA step + OAuth handler (error auto-dismiss 10s) |
/register |
Register | โ | โ | Registration + OAuth buttons + profile completion step |
/dashboard |
Dashboard | โ | user | Stats cards + check-in progress bar + will overview |
/will |
MyWill | โ | user | Will creation & settings |
/assets |
Assets | โ | user | Asset management (AES-256-GCM browser decryption) |
/documents |
Documents | โ | user | Upload / download / verify |
/beneficiaries |
Beneficiaries | โ | user | Trustee management |
/verification |
Verification | โ | user | Check-in page |
/settings/2fa |
TwoFactorSetup | โ | any | Enable / disable 2FA |
/admin |
AdminPanel | โ | admin | Admin panel (5 tabs โ metadata only) |
/manager |
ManagerPanel | โ | manager | Document review + integrity verification |
/access/:token |
BeneficiaryAccess | โ | โ | Public trustee access (requires triggered will) |
/access-denied |
AccessDenied | โ | โ | Shown when a role tries to access a restricted route |
/ ยท * |
SmartRedirect / NotFound | โ | โ | Redirect by role or 404 |
Sidebar.jsx โ Role-aware navigation. All four roles (user, admin, manager, and the hidden super-admin) get distinct link sets. Shows name, email, role badge, and logout. Mobile-responsive via SidebarContext (overlay + hamburger toggle in Navbar).
SidebarContext.jsx โ React context that manages sidebar open/close state on mobile. Wraps the app in App.jsx via SidebarProvider.
CheckinBanner.jsx โ Amber/red banner across all user pages when check-in is overdue. Polls /api/checkin/status every 30 seconds. Includes inline "ุฃูุง ุจุฎูุฑ โ" button. Hidden for admins.
AuthContext.jsx โ Provides user, loading, login(token), logout() across the app.
api.js โ Axios instance with automatic Authorization: Bearer injection and 401 auto-redirect to /login.
Set TIME_UNIT=minutes in .env and restart the backend.
| Setting | Production (days) |
Test (minutes) |
|---|---|---|
| Cron schedule | Daily at 9:00 AM | Every minute |
| Interval unit | Days | Minutes |
| Default interval | 30 days | 3 minutes |
| Default grace | 7 days | 1 minute |
| Time to trigger | 37 days | 4 minutes |
- Set
TIME_UNIT=minutes, restart backend - Log in as
user@wasiyya.com - Go to
/willโ create will withinterval=2,grace=1 - Go to
/beneficiariesโ add a trustee with your email - Open Admin Panel โ Test Tools
- Click "Reset Check-in" for the user โ set to 4 minutes ago
- Click "Force Check" โ will should trigger immediately
- Ethereal mode: click "๐ง ุนุฑุถ ุงูุฅูู ูู" to preview the notification email
- Click "ุงูุชุญ ุงููุตูุฉ" โ verify the trustee access page loads correctly
- Click "ุฅุนุงุฏุฉ ุชุนููู" to reset for the next test run
- bcrypt password hashing (cost 12) โ auto-migration on login
- AES-256-GCM document encryption at rest
- SHA-256 document integrity verification
- RSA-2048 digital signatures โ auto-generated key pair
- TOTP two-factor authentication (Google Authenticator / Authy)
- Google + GitHub OAuth login (find-only โ no auto-registration on login)
- OAuth sign-up with profile completion step from
/register(?mode=register) - OAuth + 2FA support โ TOTP required after OAuth when 2FA is enabled
- AES-256-GCM asset content encryption โ per-user key, client-side decrypt via Web Crypto API
- Beneficiary access gated on
wills.status = 'triggered' - Mobile-responsive sidebar (SidebarContext, overlay, lucide-react icons)
- Dashboard stats cards + check-in progress bar UI improvements
- HTTP โ HTTPS automatic redirect
- Manager role โ document integrity reviewer (verify-only, no download, no plaintext)
- Zero-Trust admin model โ admin sees metadata only; tokens, content, and encryption keys never exposed
- Brute-force login protection (5 attempts โ 15-min lockout per email)
- Multer dual validation โ MIME type + file extension allowlist
- Non-fatal audit logging โ DB schema mismatches never crash functional operations
- AccessDenied + NotFound pages wired into RBAC routing
- WebAuthn / Passkey 2FA โ register and authenticate using device biometrics or hardware keys
- Will title + description encrypted at rest (AES-256-GCM, per-user key)
- Asset title encrypted at rest (AES-256-GCM, per-user key)
- Automated test suite (Jest + Supertest โ 34 integration tests)
- Refresh token pattern (short-lived access + long-lived refresh)
- Email verification on registration
- Password reset via email
- Multi-language support (Arabic + English)
- Further mobile UI polish
- Notification preferences (frequency, channel)
- Will versioning and history
- Legal advisor role (limited read-only access)
- CI/CD pipeline (GitHub Actions)
- Docker Compose for production deployment
- Cloud document storage (AWS S3 / Cloudflare R2)
Contributions are welcome! Here's how to get started:
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature-name - Make your changes โ follow the existing code style
- Test your changes locally using the test procedure
- Commit with a clear message:
git commit -m "feat: add your feature" - Push to your fork:
git push origin feature/your-feature-name - Open a Pull Request against
main
- Keep PRs focused โ one feature or fix per PR
- All new API routes must include proper authentication and role checks
- Sensitive operations must be logged in
audit_logs - Do not commit
.envfiles or credentials - Do not modify
database/schema_mysql.sqlwithout a migration plan
Appear on GitHub Contributors: use a GitHub-linked email in commits:
git config user.name "Your Name"
git config user.email "your-email@example.com" # same as GitHub account| Name | GitHub |
|---|---|
| ุนู ุฑ ุนุจุฏุงูุนุงู ุณุนุฏ โ Omar Abdelaal Saad | @omar0y |
| ู ุญู ุฏ ุฃุณุงู ู ู ุญู ุฏ โ Mohammed Osama Mohammed | โ |
| ู ุตุทูู ุนูู ู ุตุทูู โ Mustafa Ali Mustafa | @Ghalwash0x |
Commits must use an email linked to your GitHub account to appear in the graph above.
| Name | ID |
|---|---|
| ุนู ุฑ ุนุจุฏุงูุนุงู ุณุนุฏ โ Omar Abdelaal Saad | 2305165 |
| ู ุญู ุฏ ุฃุณุงู ู ู ุญู ุฏ โ Mohammed Osama Mohammed | 2305180 |
| ู ุตุทูู ุนูู ู ุตุทูู โ Mustafa Ali Mustafa | 2305616 |
Supervisor: Faculty of Computer and Data Science
Academic Year: 2025 / 2026
Recent session updates (OAuth register, 2FA + OAuth, encrypted assets, UI) are documented in:
MIT License
Copyright (c) 2026 Wasiyya Team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY ARISING FROM USE OF THE SOFTWARE.
ูุตููุฉ โ Wasiyya
Digital Will Management System
Built with Node.js ยท React ยท MySQL
Faculty of Computer and Data Science โ 2026
โญ Star this repo if you found it useful!