A full-stack mobile eKYC (Electronic Know Your Customer) application built with React Native (Expo) and Node.js (Express).
- Runtime: Node.js with TypeScript
- Framework: Express 5
- Database: SQLite with Prisma ORM
- Authentication: JWT with refresh token rotation
- Logging: Pino with structured logging
- Validation: Zod
- Framework: React Native with Expo
- Navigation: Expo Router (file-based routing)
- State Management: Zustand
- HTTP Client: Axios with interceptors
- Secure Storage: expo-secure-store
- Node.js >= 18.x
- npm >= 9.x
- Expo CLI:
npm install -g expo-cli - iOS Simulator (macOS) or Android Emulator or physical device with Expo Go
git clone <repository-url>
cd ekyc-appnpm installThis installs dependencies for both backend and mobile packages (npm workspaces).
Create packages/backend/.env:
# Server
PORT=3000
NODE_ENV=development
# Database
DATABASE_URL="file:./dev.db"
# JWT
JWT_SECRET=your-secure-jwt-secret-here
JWT_ACCESS_EXPIRES_IN=600
JWT_REFRESH_EXPIRES_IN=2592000
# Security
REFRESH_TOKEN_SECRET=your-secure-refresh-secret-here
# Logging
LOG_LEVEL=debug
# Demo (optional)
DEMO_AUTO_APPROVE_DELAY_MS=60000Create packages/mobile/.env:
# Use your machine's local IP for physical devices
# Use localhost for iOS Simulator
EXPO_PUBLIC_API_URL=http://192.168.x.x:3000To find your local IP:
# macOS
ipconfig getifaddr en0
# Linux
hostname -I | awk '{print $1}'Generate Prisma client:
npm run prisma:generateRun migrations:
npm run prisma:migrateSeed the database with a test user:
npm run prisma:seedThis creates a test user:
- Email:
omar@test.com - Password:
password123
npm run dev:backendServer runs at http://localhost:3000
npm run dev:mobileThis opens Expo DevTools. Then:
- Press
ifor iOS Simulator - Press
afor Android Emulator - Scan QR code with Expo Go app on physical device
npm testnpm run test:backendnpm run test:mobileekyc-app/
├── package.json # Workspace root
├── packages/
│ ├── backend/ # @ekyc/backend
│ │ ├── src/
│ │ │ ├── app.ts # Express app setup
│ │ │ ├── server.ts # Server entry point
│ │ │ ├── config/ # Environment configuration
│ │ │ ├── routes/ # API route handlers
│ │ │ ├── services/ # Business logic
│ │ │ ├── middleware/ # Express middleware
│ │ │ ├── schemas/ # Zod validation schemas
│ │ │ ├── lib/ # Utilities (db, jwt, crypto, logger)
│ │ │ └── types/ # TypeScript types
│ │ ├── prisma/
│ │ │ ├── schema.prisma # Database schema
│ │ │ └── seed.ts # Database seeder
│ │ └── tests/ # Jest tests
│ │
│ └── mobile/ # @ekyc/mobile
│ ├── app/ # Expo Router pages
│ │ ├── _layout.tsx # Root layout
│ │ ├── index.tsx # Entry redirect
│ │ ├── (auth)/ # Auth screens (login)
│ │ └── (app)/ # Protected screens
│ │ ├── (tabs)/ # Tab navigation (home, settings)
│ │ └── onboarding/ # Onboarding flow
│ ├── components/ # Reusable components
│ ├── store/ # Zustand stores
│ ├── lib/ # Utilities (api client, storage)
│ ├── hooks/ # Custom React hooks
│ └── constants/ # Theme, config
| Method | Endpoint | Description |
|---|---|---|
| POST | /v1/auth/login |
User login |
| POST | /v1/auth/register |
User registration |
| POST | /v1/auth/refresh |
Refresh access token |
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/me |
Get current user |
| POST | /v1/onboarding/submit |
Submit KYC data |
| GET | /v1/verification/status |
Get verification status |
| Method | Endpoint | Description |
|---|---|---|
| GET | /health |
Server health check |
- JWT Access Tokens: Short-lived (10 min default), signed with HS256
- Refresh Token Rotation: New refresh token issued on each refresh
- Token Reuse Detection: Detects and revokes sessions on token reuse
- Device Binding: Sessions bound to device ID
- Single Device Policy: New login revokes all previous sessions
- Secure Token Storage: Refresh tokens hashed with HMAC-SHA256
- Password Hashing: bcrypt with cost factor 12
- Structured Logging: No PII or tokens in logs
| Command | Description |
|---|---|
npm run dev:backend |
Start backend dev server |
npm run dev:mobile |
Start Expo mobile app |
npm run build:backend |
Build backend for production |
npm run test:backend |
Run backend tests |
npm run test:mobile |
Run mobile tests |
npm test |
Run all tests |
npm run prisma:generate |
Generate Prisma client |
npm run prisma:migrate |
Run database migrations |
npm run prisma:seed |
Seed database with test user |
For demo purposes, KYC verification is auto-approved after 1 minute (configurable via DEMO_AUTO_APPROVE_DELAY_MS).
cd packages/mobile && npx expo start --clear# Reset database
rm packages/backend/prisma/dev.db
npm run prisma:migrate
npm run prisma:seed# Kill process on port 3000
lsof -ti:3000 | xargs kill -9
# Kill process on port 8081 (Expo)
lsof -ti:8081 | xargs kill -9