A production-grade, full-stack secure online voting platform built with Node.js, Express, MongoDB, HTML/CSS (Bootstrap 5), and Vanilla JavaScript.
| Feature | Implementation |
|---|---|
| 6-Factor MFA | VoterId β Aadhaar β OTP β Face Recognition β Liveness β CAPTCHA |
| Face Recognition | face-api.js (TinyFaceDetector + FaceRecognitionNet), threshold < 0.6 |
| Liveness Detection | EAR blink detection + head yaw via facial landmarks |
| OTP System | In-memory store, 6-digit, 2-minute expiry |
| Aadhaar Security | SHA-256 hash β raw value never stored |
| Tamper-Proof Votes | SHA-256 hash chain (each vote links to previous) |
| AI Anomaly Detection | Isolation Forest simulation β auto-suspends abusive accounts |
| Admin Panel | Full election lifecycle management, charts, log viewer |
| WAF Simulation | IP blocklist middleware + Cloudflare integration guide |
| JWT Auth | HTTP-only cookies, 1hr expiry |
| Rate Limiting | express-rate-limit (100 req/15min general, 10 auth, 5 vote) |
| Helmet | Secure HTTP headers + Content Security Policy |
face-voting-system/
βββ public/
β βββ css/style.css # Dark glassmorphism theme
β βββ js/
β β βββ register.js
β β βββ login.js
β β βββ verify.js
β β βββ dashboard.js
β β βββ vote.js
β β βββ admin.js
β βββ register.html
β βββ login.html
β βββ verify.html
β βββ dashboard.html
β βββ vote.html
β βββ admin.html
βββ routes/
β βββ auth.js # 6-step MFA flow
β βββ elections.js # Public election routes
β βββ votes.js # Vote casting + hash chain
β βββ admin.js # Admin CRUD + management
βββ models/
β βββ User.js
β βββ Election.js
β βββ ElectionRequest.js
β βββ Vote.js
β βββ Log.js
βββ middleware/
β βββ auth.js # JWT cookie middleware
β βββ adminAuth.js # Admin role gate
β βββ rateLimiter.js # express-rate-limit configs
β βββ ipBlock.js # Runtime IP blocklist
βββ utils/
β βββ hash.js # SHA-256 + vote hash chain
β βββ otp.js # OTP generate/verify/expire
β βββ captcha.js # Google reCAPTCHA verification
β βββ logger.js # Activity logging to MongoDB
β βββ anomaly.js # Isolation Forest simulation
βββ server.js # Express app entry point
βββ .env # Environment variables
βββ README.md
cd face-voting-system
npm installEdit .env:
PORT=3000
MONGODB_URI=mongodb://localhost:27017/ai_voting_system
JWT_SECRET=your_super_secret_key_here_make_it_long
JWT_EXPIRES_IN=1h
ADMIN_VOTER_ID=ADMIN001
ADMIN_PASSWORD=Admin@12345
# Get from https://www.google.com/recaptcha/admin
RECAPTCHA_SITE_KEY=YOUR_SITE_KEY
RECAPTCHA_SECRET_KEY=YOUR_SECRET_KEYNote: If reCAPTCHA keys are not configured, the system auto-skips CAPTCHA verification in dev mode (a warning is printed to the console).
# Windows
mongod --dbpath C:\data\db
# macOS/Linux
mongod --dbpath /data/dbnpm run dev # Development (auto-restart with nodemon)
# or
npm start # ProductionOpen http://localhost:3000 in your browser.
Register β register.html (name, voterId, Aadhaar, face capture)
β
Login Step 1 β login.html (Voter ID)
Login Step 2 β login.html (Aadhaar)
Login Step 3 β login.html (OTP β check server console in dev)
β
Login Step 4 β verify.html (Face Recognition)
Login Step 5 β verify.html (Liveness: blink + head turn)
Login Step 6 β verify.html (Google reCAPTCHA)
β
Dashboard β dashboard.html (view elections, request election)
β
Vote β vote.html (select candidate β CAPTCHA β confirm β hash displayed)
URL: http://localhost:3000/admin
| Credential | Value |
|---|---|
| Admin ID | ADMIN001 (or whatever you set in .env) |
| Password | Admin@12345 (or whatever you set in .env) |
- π Overview dashboard with live stats
- ποΈ Create elections, add/remove candidates, start/stop elections
- π Approve or reject election requests from voters
- π₯ View registered voters, suspend/unsuspend accounts
- π View activity logs (filterable + paginated)
- π¨ AI anomaly detection β flagged & suspended users
- π‘οΈ IP blocking β block/unblock IPs at runtime
VoterId βββΊ Aadhaar Hash Match βββΊ OTP (2min) βββΊ Face (dist<0.6) βββΊ Liveness βββΊ CAPTCHA βββΊ JWT Cookie
| Data | How Stored |
|---|---|
| Aadhaar | SHA-256 hash only |
| Face | 128-float descriptor array (not image) |
| Password (admin) | Plain text in .env β upgrade to bcrypt for production |
| JWT | HTTP-only, SameSite=Strict cookie |
Each vote is linked in a hash chain:
hash = SHA-256(userId + candidateId + timestamp + previousHash)
The first vote uses previousHash = '0'. Any tampering breaks the chain.
- Helmet β Secure HTTP headers (XSS, clickjacking, etc.)
- express-rate-limit β Brute force prevention
- ipBlock middleware β Runtime IP blocklist
- Joi validation β Input sanitization on all POST routes
- JWT β HTTP-only cookies prevent XSS token theft
For production deployments:
- Point your domain to Cloudflare (change nameservers)
- Enable WAF Rules in Cloudflare dashboard β Security β WAF
- Recommended rules:
- Block requests with SQL injection patterns
- Rate limit > 50 req/min per IP
- Challenge countries you don't serve
- IP Blocking: Use Cloudflare β Security β Tools β IP Access Rules
- Set SSL mode to "Full (Strict)" in Cloudflare β SSL/TLS
The ipBlock.js middleware acts as a backend fallback for when Cloudflare isn't in front.
| Method | Endpoint | Description |
|---|---|---|
| POST | /register |
Register voter with face descriptor |
| POST | /login/step1 |
Verify Voter ID |
| POST | /login/step2 |
Verify Aadhaar hash |
| POST | /login/step3 |
Send OTP |
| POST | /login/step4 |
Verify OTP |
| POST | /login/step5 |
Face + liveness verification |
| POST | /login/step6 |
CAPTCHA β issue JWT cookie |
| POST | /logout |
Clear session |
| GET | /me |
Get current user (requires auth) |
| Method | Endpoint | Description |
|---|---|---|
| GET | / |
List active elections |
| GET | /:id |
Get election detail |
| POST | /request |
Submit election request (requires auth) |
| Method | Endpoint | Description |
|---|---|---|
| POST | /cast |
Cast vote with CAPTCHA (requires auth) |
| GET | /status |
Get user's vote status (requires auth) |
| Method | Endpoint | Description |
|---|---|---|
| POST | /login |
Admin login |
| POST | /logout |
Admin logout |
| GET | /stats |
System statistics |
| GET | /elections |
All elections |
| POST | /elections |
Create election |
| POST | /elections/:id/candidates |
Add candidate |
| DELETE | /elections/:id/candidates/:cId |
Remove candidate |
| PATCH | /elections/:id/status |
Start/stop election |
| DELETE | /elections/:id |
Delete election |
| GET | /elections/:id/results |
Election results |
| GET | /requests |
Election requests |
| POST | /requests/:id/approve |
Approve request |
| POST | /requests/:id/reject |
Reject request |
| GET | /users |
All voters |
| POST | /users/:id/suspend |
Suspend user |
| POST | /users/:id/unsuspend |
Unsuspend user |
| GET | /logs |
Activity logs |
| GET | /anomalies |
AI anomaly data |
| GET | /blocked-ips |
Blocked IP list |
| POST | /blocked-ips/block |
Block an IP |
| POST | /blocked-ips/unblock |
Unblock an IP |
Uses a simulated Isolation Forest algorithm:
- Monitors a 15-minute sliding window of activity logs per user
- Assigns weighted scores to suspicious events:
face_failβ +5otp_failβ +4login_failβ +3captcha_failβ +3login_successβ β1 (reduces suspicion)
- Score β₯ 10 β Flagged (warning logged)
- Score β₯ 20 β Auto-suspended (account locked)
View flagged accounts in the Admin Panel β π¨ Anomalies tab.
Currently OTPs are printed to the server console. To enable real delivery:
SMS via Twilio:
npm install twilio// In utils/otp.js, replace console.log with:
const client = require('twilio')(process.env.TWILIO_SID, process.env.TWILIO_TOKEN);
await client.messages.create({ body: `Your OTP: ${otp}`, from: '+1XXXXXXXX', to: user.phone });Email via SendGrid:
npm install @sendgrid/mailconst sgMail = require('@sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_KEY);
await sgMail.send({ to: user.email, from: 'noreply@yourdomain.com', subject: 'Your OTP', text: `OTP: ${otp}` });Never run this system on plain HTTP in production:
# Option A: Use nginx as reverse proxy with Let's Encrypt
sudo certbot --nginx -d yourdomain.com
# Option B: Use Cloudflare SSL (easiest β proxy through Cloudflare)
# Set NODE_ENV=production in .env β cookie secure flag auto-enablesMIT β For educational and research purposes. Consult legal counsel before deploying for real elections.