Skip to content

Repository files navigation

BioAttend

Multi-factor attendance system that verifies a student's identity with face recognition, GPS geofencing, a device fingerprint check, and blink-based liveness detection before marking them present for a professor-created session.

Features

  • JWT login with two roles (professor / student), backed by an app_users table. Seeded accounts are reset on every backend startup: professor1 / prof123 and student1 / stud123 (backend/main.py).
  • Student enrollment (professor only): capture a webcam photo, extract a face embedding with InsightFace, store the photo in MinIO, and save the embedding to Postgres (backend/routers/students.py).
  • Student gallery (professor only): list and delete enrolled students (frontend/src/StudentGallery.jsx).
  • Session creation (professor only): course name, duration, classroom GPS coordinates, geofence radius, and an optional allowed WiFi SSID; a 6-digit OTP is generated per session (backend/routers/sessions.py).
  • Session dashboard: live status, attendance records, manual close, list of the professor's own sessions and all currently active sessions.
  • Four-step student attendance flow (frontend/src/StudentAttendancePage.jsx, backend/routers/attendance.py):
    1. Validate OTP + student email against an active session.
    2. Score location + device: GPS distance to the classroom via the Haversine formula (60 pts) plus a device-fingerprint sanity check (40 pts); 70+ is required to proceed.
    3. Liveness challenge: ~40 webcam frames are sent to the backend, which runs InsightFace's 106-point landmarks, computes eye-aspect-ratio (EAR) per frame, and requires at least one detected blink.
    4. Final face capture, matched against the enrolled student's stored embedding.
  • Duplicate-attendance protection: a unique constraint on (session_id, student_id) blocks marking attendance twice in the same session.
  • CSV and Excel export of attendance, per session or across all legacy logs (professor only), via pandas/openpyxl (backend/services/export_service.py).

Tech stack

Backend (backend/requirements.txt): FastAPI, Uvicorn, OpenCV (opencv-python), NumPy, psycopg2, InsightFace + ONNX Runtime (GPU build in requirements.txt, CPU build in requirements-docker.txt), MinIO client, Pandas + OpenPyXL, Pydantic / pydantic-settings, python-jose (JWT), passlib + bcrypt.

Frontend (frontend/package.json): React 19, Vite 7, Tailwind CSS 4, React Router 7, Axios, react-webcam, react-hot-toast, lucide-react icons. (react-qr-code is listed as a dependency but isn't imported anywhere in src/ — the OTP is shown as plain text, not a QR code.)

Data / infra: PostgreSQL (pgvector/pgvector:pg16 image), MinIO (S3-compatible object storage) for student photos, Nginx as reverse proxy + static file server, Docker Compose, AWS EC2 deployment script.

Architecture

Face embeddings. backend/dependencies.py::get_face_app() lazily loads InsightFace's buffalo_s model (detection, recognition, landmark_2d_106 modules only, CPU execution provider, 320×320 detection size, to fit low-memory hosts). backend/services/face_service.py::detect_face_from_base64() decodes a base64 JPEG from the browser, runs the model, and returns the first detected face's embedding as a plain list of floats.

What's actually stored in Postgres. Although the Postgres image is pgvector/pgvector:pg16, the students.embedding column is defined as JSONB (backend/migrations/000_base_schema.sql), not a vector column — no CREATE EXTENSION vector or column-type migration exists in this repo. backend/routers/attendance.py contains a _match_student_with_pgvector() helper that queries embedding <=> %s::vector (pgvector's cosine-distance operator), but it is never called from any route — it's dead code left over from an earlier design.

How a lookup actually works end to end (the live /attendance/mark-secure path):

  1. Student submits OTP + email; the backend looks up the matching attendance_sessions row and checks it's active and unexpired.
  2. Backend re-validates the GPS/device score (Haversine distance ≤ geofence_radius, plus the device-fingerprint check) and, if LIVENESS_ENABLED, requires liveness_data.passed to be true.
  3. The submitted face image is run through InsightFace to get a live embedding.
  4. The backend fetches only the one student row matching the submitted email, JSON-decodes its stored embedding, and computes cosine similarity between the live and stored embeddings with plain NumPy (np.dot(...) / (norm * norm)) — there is no nearest-neighbor search across all students in this path.
  5. If similarity ≥ RECOGNITION_THRESHOLD (0.45) and the student hasn't already been marked present for this session, a session_attendance row is inserted with the device/location/verification-score/liveness JSON blobs and a verification_method string (e.g. gps+device+liveness+face).

A separate, older path (backend/routers/camera.py, backend/services/camera_service.py) streams live webcam video from the server and, for each frame, loops over every enrolled student's embedding in memory (known_faces) to find the best cosine-similarity match, logging to a legacy attendance_logs table. This path is not wired into the current frontend (the component that would use it, frontend/src/AttendanceTable.jsx, is not imported by App.jsx), so it's effectively unreachable from the UI today.

Local setup

There's no .env file in the repo; backend/config.py falls back to defaults that match docker-compose.yml (admin / password123 / face_recognition for Postgres, minioadmin / minioadminpassword for MinIO), so the stack below runs without creating one.

1. Start Postgres, MinIO, and the backend:

docker compose up -d --build db minio backend

This uses the root docker-compose.yml: Postgres on 5432, MinIO on 9000/9001, backend on 8000.

2. Apply the SQL migrations (nothing runs these automatically — no migration runner or initdb.d mount exists in this repo):

docker compose exec -T db psql -U admin -d face_recognition < backend/migrations/000_base_schema.sql
docker compose exec -T db psql -U admin -d face_recognition < backend/migrations/001_add_session_tables.sql
docker compose exec -T db psql -U admin -d face_recognition < backend/migrations/002_add_app_users.sql
docker compose exec -T db psql -U admin -d face_recognition < backend/migrations/003_add_attendance_logs.sql

3. Run the frontend dev server:

cd frontend
npm install
npm run dev

Note: frontend/src/api.js hardcodes API_BASE_URL = "https://bioattend.duckdns.org/api" — it does not read VITE_API_BASE_URL despite reading it into an unused variable. A local npm run dev frontend will call the live production backend, not your local backend container, until you edit that line.

Full stack via Docker (as deployed): docker compose up -d --build (no service names) additionally builds the frontend service from frontend/Dockerfile, which bakes in an Nginx config for bioattend.duckdns.org on ports 80/443 and expects Let's Encrypt certificates mounted from /etc/letsencrypt on the host — it will not start cleanly on a machine without those certs. docker-compose.prod.yml (with deploy/Dockerfile.web and deploy/nginx.conf) is the more portable production variant: it proxies /api/ to the backend without any hardcoded domain or SSL termination baked into the image.

Live demo

https://bioattend.duckdns.org

Unverified — confirm with me

  • assets/wireframes/*.png — three generic wireframe mockups exist, but their content (e.g. "Visitors / Active / New Signups") doesn't match BioAttend's actual professor/student flows, so I didn't use them as the top-of-README screenshot. Let me know if you'd like a real screenshot or GIF captured from the running app instead.
  • bioattend_analytics.ipynb runs entirely on synthetic/random data generated in the notebook itself (N_STUDENTS = 120, simulated GPS distances, simulated inference latency) — it is not connected to the live database, so I did not describe it as a "live analytics" feature.

About

Face-embedding attendance system — vector similarity search over face embeddings with pgvector. React + FastAPI, containerised with Docker.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages