FastIDV is an async FastAPI service for identity verification workflows. It combines:
- User authentication with JWT
- NID image upload + OCR extraction through DeepseekOCR
- Face verification + spoof detection through DeepFace (Using DeepFaceAPI)
- A gated sample resource ("biscuits") available only to NID verified users
- Python
- FastAPI
- Async SQLAlchemy + Alembic
- Tested with PgSQL, Should work with any SQL Databases
- Ollama for hosting DeepSeekOCR and structured extraction
- Deepface for face verification
app/
api/routes/ # Route modules: users, idv, biscuits
core/ # Settings and dependencies
db/ # Engine/session/base definitions
models/ # SQLAlchemy models
schemas/ # Pydantic schemas
services/ # LLM and deepface client wrappers
utils/ # JWT and password helpers
alembic/ # Database migrations
uploads/ # Uploaded NID/face images
Install and run the following before testing full IDV flows:
- Python 3.14 or newer
- uv (recommended) for running commands
- Ollama server at http://localhost:11434 with models:
- deepseek-ocr:latest
- qwen2.5:latest
- (DeepFaceAPI)[https://github.com/rozari0/DeepfaceAPI] at http://localhost:8008 exposing:
- POST /verify/
- POST /analyze/
If external services are not running, authentication and basic endpoints still work, but OCR/verification endpoints will fail.
uv syncCreate a .env file in the project root. These are the available settings and defaults:
DATABASE_URL=postgresql+asyncpg://user:password@host:port/dbname
SECRET_KEY=CHANGETHISSECRET
ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=60
UPLOAD_DIR=uploads
OLLAMA_API_URL=http://localhost:11434
ECHO_SQLALCHEMY=FalseFor production-like usage, change SECRET_KEY and DATABASE_URL.
Apply migrations:
just migrateCreate a new migration:
just makemigrations 006 your migration messagejustThis starts:
uv run uvicorn app.main:app --reload
Default docs:
- Swagger UI: http://127.0.0.1:8000/docs
- ReDoc: http://127.0.0.1:8000/redoc
Base prefix for most endpoints: /api/v1
Public endpoint:
- GET /health
User routes:
- POST /api/v1/users/signup
- POST /api/v1/users/login
- GET /api/v1/users/me
IDV routes (authenticated):
- POST /api/v1/idv/nid
- PUT /api/v1/idv/nid
- GET /api/v1/idv/nid
- POST /api/v1/idv/verify
Biscuits route (authenticated):
- GET /api/v1/biscuits/
- Sign up with email/password.
- Log in with OAuth2 form fields (username is your email).
- Use returned access_token as Bearer token for protected routes.
Example:
Authorization: Bearer <access_token>curl -X POST "http://127.0.0.1:8000/api/v1/users/signup" \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com","password":"secret123"}'curl -X POST "http://127.0.0.1:8000/api/v1/users/login" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=user@example.com&password=secret123"curl -X POST "http://127.0.0.1:8000/api/v1/idv/nid" \
-H "Authorization: Bearer <access_token>" \
-F "file=@/path/to/nid.jpg"curl -X PUT "http://127.0.0.1:8000/api/v1/idv/nid" \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
-d '{"name":"Updated Name","dob":"1990-01-01"}'curl -X POST "http://127.0.0.1:8000/api/v1/idv/verify" \
-H "Authorization: Bearer <access_token>" \
-F "file=@/path/to/selfie.jpg"curl -X GET "http://127.0.0.1:8000/api/v1/biscuits/" \
-H "Authorization: Bearer <access_token>"just format # import sorting + formatting
just clean # remove __pycache__ and .pyc files- Uploaded files are stored under uploads/<user_id>/.
- The IDV flow marks a user as verified only when both checks pass:
- Face match is true
- Spoof check is false
- CORS is currently open to all origins.
- Most of the code is writen by human, except for this README.md File