Skip to content

omar0y/wasiyya

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

47 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

ูˆุตูŠู‘ุฉ โ€” Wasiyya

Digital Inheritance & Electronic Will Management System

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.

Node.js React MySQL Express Tailwind

JWT bcrypt 2FA OAuth AES-256

License Version Faculty

Quick Start ยท Changelog ยท API Reference ยท Security ยท Contributors


Table of Contents


๐ŸŒ Overview

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.

How it works

  1. A user registers, creates a will, and adds their sensitive digital assets and documents
  2. They designate trustees (heirs) by name and email
  3. The system monitors user activity via a Dead Man's Switch โ€” the user must periodically confirm they are alive
  4. If they fail to check in within the configured grace period, the will is automatically triggered
  5. Every trustee receives a secure, time-limited email link granting access to the full will contents

System Roles

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.

โœจ Features

For Will Owners (ู…ูˆุตูŠ)

๐Ÿ” 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:authTagHex IV columns
  • Per-user encryption key โ€” derived from AES_SECRET_KEY + userId; only the account owner can decrypt via GET /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 iv column (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

For Trustees / Heirs (ูˆุงุฑุซ)

  • 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

For Admins (ู…ุฏูŠุฑ ุงู„ู†ุธุงู…)

See the Admin Panel section for full details.


๐Ÿ› ๏ธ Tech Stack

Backend

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)

Frontend

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)

Infrastructure

Tool Purpose
MySQL 8.0 Relational database
XAMPP Local MySQL server + phpMyAdmin

๐Ÿ—๏ธ Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                        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) โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Request Lifecycle

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 }

๐Ÿ“ Project Structure

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

๐Ÿ—„๏ธ Database Schema

Entity Relationships

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

Tables

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).
With TIME_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

Indexes

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);

Default Seed Accounts

Role Email 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.


๐Ÿš€ Getting Started

ุฃูˆุงู…ุฑ ุงู„ุชุดุบูŠู„ ุงู„ูƒุงู…ู„ุฉ (ุนุฑุจูŠ): docs/RUN.md โ€” DockerุŒ DBุŒ HTTPSุŒ ูˆุงู„ุชุดุบูŠู„ ุงู„ูŠูˆู…ูŠ.

Prerequisites

Requirement Version
Node.js v18 or higher
npm v9 or higher
MySQL 8.0 (via XAMPP or standalone)

Installation

1. Clone the repository

git clone https://github.com/omar0y/wasiyya.git
cd wasiyya

2. Set up the database

Option A โ€” XAMPP

  1. Start XAMPP โ†’ click Start for MySQL
  2. Open http://localhost/phpmyadmin
  3. Create a new database named wasiyya
  4. 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.sql

Login to phpMyAdmin: user root, empty password (default).

3. Configure the backend

cd backend
cp .env.example .env   # Linux/macOS
# or
copy .env.example .env # Windows

Edit .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 install

5. Start the servers

# Terminal 1 โ€” backend
cd backend && npm run dev

# Terminal 2 โ€” frontend
cd frontend && npm run dev

6. Open the app

Navigate to http://localhost:3000

Account Email Password Redirects to
Admin admin@wasiyya.com Admin@123 /admin
User user@wasiyya.com User@123 /dashboard

Expected startup output

๐Ÿš€ Wasiyya backend โ€” http://localhost:3001
โฑ๏ธ  TIME_UNIT = minutes
โฐ Checkin cron started โ€” ูƒู„ ุฏู‚ูŠู‚ุฉ (ูˆุถุน ุงู„ุชูŠุณุช)
โœ… MySQL connected โ€” wasiyya

HTTPS (local development)

  1. Install mkcert and trust the local CA (once โ€” asks for Mac password):
brew install mkcert
cd backend && npm run ssl:trust
  1. Generate the app certificate (once):
cd backend && npm run ssl:generate
  1. In backend/.env, enable HTTPS and use https:// URLs:
USE_HTTPS=true
FRONTEND_URL=https://localhost:3000
BACKEND_URL=https://localhost:3001
WEBAUTHN_ORIGIN=https://localhost:3000
  1. Update OAuth apps (Google Cloud + GitHub) callback URLs to:
  • https://localhost:3001/api/auth/google/callback
  • https://localhost:3001/api/auth/github/callback
  1. Run backend and frontend (Vite picks up the same certs automatically):
cd backend && npm run dev
cd frontend && npm run dev

Open 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.


โš™๏ธ Configuration

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 Reference

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)

๐Ÿ“ก API Reference

All endpoints return JSON.

  • Success: { "success": true, "data": { ... } }
  • Error: { "success": false, "message": "..." }

Base URL: http://localhost:3001/api


๐Ÿ”‘ Authentication โ€” /api/auth

POST /auth/register

// 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

POST /auth/login

// 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.


2FA Endpoints

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" }

OAuth Endpoints

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.


GET /auth/me ยท POST /auth/logout ยท GET /auth/wallet-key

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.


๐Ÿ“œ Wills โ€” /api/wills

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
}

๐Ÿ’ผ Assets โ€” /api/assets

Requires authentication, role user. Will ownership verified on every request.
Responses return content_encrypted + iv โ€” decrypt in the browser using GET /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


๐Ÿ“ Documents โ€” /api/documents

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


๐Ÿ‘ฅ Beneficiaries โ€” /api/beneficiaries

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


โœ… Check-in โ€” /api/checkin

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"
  }
}

๐Ÿ›ก๏ธ Admin โ€” /api/admin

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

๐Ÿ” Manager โ€” /api/manager

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.


๐Ÿฅ Health โ€” GET /api/health

{ "status": "ok", "phase": 4, "time_unit": "minutes", "timestamp": "..." }

๐Ÿ” Security

Security Features

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)

Authentication Flows

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 Protection

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

Known Limitations

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

โฑ๏ธ Dead Man's Switch

This is the core mechanic of Wasiyya.

Configuration

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

Cron Logic

// 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

Trigger Process

  1. UPDATE wills SET status = 'triggered', triggered_at = NOW()
  2. For each beneficiary:
    • Generate access_token = crypto.randomBytes(32).toString('hex')
    • Set token_expires = NOW() + 7 days
    • Send email with secure access link
  3. INSERT INTO audit_logs (WILL_TRIGGERED)

Timeline Example (testing with minutes)

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

๐Ÿ“ง Email System

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

Email Templates

Email 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)

Using Ethereal (Dev Mode)

No configuration needed. Email preview URLs are stored in-memory and shown in the Admin Panel โ†’ Test Tools tab. No emails reach real inboxes.

Using Gmail (Production)

  1. Enable 2-Step Verification on your Google account
  2. Generate an App Password at myaccount.google.com/apppasswords
  3. 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.


๐Ÿ›ก๏ธ Admin Panel

Admins are redirected to /admin after login and have no access to the user-side will pages.

Tab 1 โ€” Dashboard (ู„ูˆุญุฉ ุงู„ุชุญูƒู…)

  • Live stat cards: total/active users, wills, triggered wills, documents, assets
  • Triggered wills list with owner names
  • Last 10 audit log entries

Tab 2 โ€” Users (ุงู„ู…ุณุชุฎุฏู…ูˆู†)

  • 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)

Tab 3 โ€” Wills (ุงู„ูˆุตุงูŠุง)

  • All wills across all users with owner info and counts
  • Color-coded status badges: ๐ŸŸข active ยท ๐Ÿ”ด triggered ยท โšซ expired

Tab 4 โ€” Audit Logs (ุงู„ุณุฌู„ุงุช)

  • Paginated log of every system action
  • Color-coded by type: blue (auth) ยท green (create) ยท red (trigger/delete) ยท gray (check-in)

Tab 5 โ€” Test Tools (ุฃุฏูˆุงุช ุงู„ุชูŠุณุช)

  • 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_checkin to N units ago
  • Triggered Wills โ€” per-beneficiary notification status, accessed_at indicator, Ethereal email previews, reset buttons

Security note: Beneficiary access tokens (access_token) are never returned to the admin. The admin sees a token_valid boolean and accessed_at timestamp only. Actual document content remains inaccessible to the admin role by design.


๐Ÿ–ฅ๏ธ Frontend & Routing

Route Map

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

Key Components

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.


๐Ÿงช Testing

Setting Up Test Mode

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

End-to-End Test Procedure

  1. Set TIME_UNIT=minutes, restart backend
  2. Log in as user@wasiyya.com
  3. Go to /will โ†’ create will with interval=2, grace=1
  4. Go to /beneficiaries โ†’ add a trustee with your email
  5. Open Admin Panel โ†’ Test Tools
  6. Click "Reset Check-in" for the user โ†’ set to 4 minutes ago
  7. Click "Force Check" โ†’ will should trigger immediately
  8. Ethereal mode: click "๐Ÿ“ง ุนุฑุถ ุงู„ุฅูŠู…ูŠู„" to preview the notification email
  9. Click "ุงูุชุญ ุงู„ูˆุตูŠุฉ" โ†’ verify the trustee access page loads correctly
  10. Click "ุฅุนุงุฏุฉ ุชุนูŠูŠู†" to reset for the next test run

๐Ÿ—บ๏ธ Roadmap

โœ… Completed

  • 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)

๐Ÿ”œ Planned

  • 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)

๐Ÿค Contributing

Contributions are welcome! Here's how to get started:

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/your-feature-name
  3. Make your changes โ€” follow the existing code style
  4. Test your changes locally using the test procedure
  5. Commit with a clear message: git commit -m "feat: add your feature"
  6. Push to your fork: git push origin feature/your-feature-name
  7. Open a Pull Request against main

Guidelines

  • 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 .env files or credentials
  • Do not modify database/schema_mysql.sql without 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

๐Ÿ‘ค Contributors

Contributors
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.


๐Ÿ‘ฅ Team

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


๐Ÿ“‹ Changelog

Recent session updates (OAuth register, 2FA + OAuth, encrypted assets, UI) are documented in:

CHANGELOG_UPDATES.md


๐Ÿ“„ License

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!

Releases

No releases published

Packages

 
 
 

Contributors

Languages