System Architecture & Design

e-ID Mongolia

Passwordless authentication platform using Smart-ID pattern with mobile push verification, PKI certificate chain, and hardware-backed device keys.

5
Docker Services
3
Platforms
8
Security Layers
6
Digit Verification

System Architecture

5 Docker containers on a single server with Nginx reverse proxy and Let's Encrypt SSL.

Internet Layer β€” DNS + SSL
🌐
smartid.mn
Web + API
:443
πŸ”
keycloak.smartid.mn
Identity Provider
:443
πŸ”
debug.smartid.mn
Real-time Monitor
:443
Reverse Proxy β€” Nginx
⚑
Nginx
TLS termination, routing
Let's Encrypt
Application Layer
πŸš€
Go Backend
BFF API β€” all business logic
:8081 (chi router)
βš›οΈ
Next.js
Web frontend (SSR)
:3000
πŸ”‘
Keycloak 24
OIDC / SSO / Custom SPI
:8080
Data Layer
🐘
PostgreSQL 16
Keycloak DB + Device fingerprints
:5432
⚑
Redis 7
Challenge sessions (TTL 120s)
:6379
Mobile Clients
πŸ“±
iOS App
Swift + SwiftUI + Secure Enclave
mn.geid.smartid
πŸ€–
Android App
Kotlin + Compose + StrongBox
mn.geid.smartid
πŸ””
Firebase FCM
Push Notifications (V1 API)
APNs + GCM

Authentication Flow

Complete passwordless authentication in under 5 seconds.

1
User enters Registration Number
User visits smartid.mn and enters their national registration number (e.g., МА74101813)
POST /api/auth/init { "personalCode": "МА74101813" }
WEBGO BACKEND
2
Challenge Created + Push Sent
Go backend creates 6-digit verification code, stores in Redis (120s TTL), sends FCM push notification to registered device.
Redis: challenge:{uuid} β†’ { displayCode: "499745", status: "pending" } FCM β†’ APNs/GCM β†’ Mobile Device
GO BACKENDREDISFIREBASE
3
Web Shows Code + Polls Status
Web displays the verification code with a circular countdown timer. Polls backend every 2 seconds for status changes.
GET /api/auth/status/{sessionId} β†’ { "status": "pending" }
WEB
4
Mobile: Verify Code + Enter PIN
Push notification opens challenge screen. User compares 6-digit code, enters 6-digit PIN, confirms with biometric (Face ID / fingerprint).
iOSANDROID
5
Cryptographic Confirmation
App signs sessionId with device private key (EC P-256, hardware-backed), sends signature + X.509 certificate to backend.
POST /api/auth/confirm { "sessionId": "uuid", "action": "approve", "deviceSignature": "MEQCIG27VasBWu5xcl8y...", // ECDSA-SHA256 "deviceCertificate": "MIIBkTCB+wIU..." // X.509 from Intermediate CA }
DEVICE KEYGO BACKEND
6
Signature + Certificate Chain Verified
Go backend verifies ECDSA signature with stored public key, validates X.509 certificate chain (Root CA β†’ Intermediate CA β†’ Device Cert). Challenge marked "approved".
[Crypto] Signature VERIFIED for МА74101813 [PKI] Certificate VERIFIED, CN: МА74101813
ECDSA VERIFYX.509 CHAIN
7
Web Redirects to Dashboard
Polling detects "approved" status. Web redirects to authenticated dashboard. Total time: ~3-5 seconds.
GET /api/auth/status/{sessionId} β†’ { "status": "approved" } β†’ redirect /dashboard
WEB

PKI Certificate Hierarchy

Self-signed Root CA, replaceable with official CA without re-registering devices.

Root CA
e-ID Mongolia Root CA
Self-signed Β· EC P-256 Β· Valid 20 years
πŸ”„ Replaceable with official CA (e.g., Монгол Π¦Π“Π£)
Intermediate CA
Gerege Systems LLC
Signed by Root CA Β· Valid 10 years
Re-sign with new Root β†’ all device certs remain valid
Device Certificate
МА74101813 (iPhone)
Secure Enclave key Β· Valid 2 years
Device Certificate
МА74101813 (Android)
StrongBox/TEE key Β· Valid 2 years

Security Layers

8 layers of defense-in-depth protection.

01
Hardware-Backed Keys
EC P-256 private keys in iOS Secure Enclave / Android StrongBox. Cannot be exported even on jailbroken devices.
02
X.509 Certificate Chain
Root CA β†’ Intermediate CA β†’ Device Certificate. Full PKI with chain verification on every confirmation.
03
ECDSA Signature
Every confirmation cryptographically signed with device private key. Prevents replay and spoofing attacks.
04
Visual Verification
6-digit code displayed on both web and mobile. User verifies match β€” prevents MITM attacks.
05
PIN + Biometric
6-digit PIN entry + Face ID / Touch ID / Fingerprint. Multi-factor: possession + knowledge + inherence.
06
Time-Limited Challenges
120-second TTL on challenges. Auto-expire in Redis. Single-use β€” consumed after verification.
07
Device Fingerprint
SHA-256 composite hash of hardware identifiers. Unique per device, survives factory reset.
08
TLS + HSTS
TLS 1.2/1.3 everywhere. HSTS with includeSubDomains. All traffic encrypted end-to-end.

Technology Stack

Modern, production-ready technologies chosen for security and performance.

πŸš€
Go 1.25
BFF API server (chi router, ECDSA, X.509)
βš›οΈ
Next.js 16
Web frontend (SSR, Tailwind, Stitch design)
πŸ”‘
Keycloak 24
Identity provider (OIDC, SSO, Custom SPI)
🐘
PostgreSQL 16
Identity + device fingerprint storage
⚑
Redis 7
Challenge sessions with TTL
πŸ””
Firebase FCM v1
Push notifications (APNs + GCM)
πŸ“±
Swift + SwiftUI
iOS app (Secure Enclave, CryptoKit)
πŸ€–
Kotlin + Compose
Android app (Keystore, StrongBox)
🐳
Docker Compose
5-container deployment
πŸ”’
Let's Encrypt
Auto-renewing SSL certificates
πŸ›‘οΈ
Nginx
Reverse proxy, TLS termination
πŸ‡²πŸ‡³
MN / EN i18n
Bilingual interface

API Endpoints

All endpoints served by Go backend at smartid.mn/api/

MethodEndpointDescription
POST/api/auth/initCreate challenge + send push notification
GET/api/auth/status/{sessionId}Poll challenge status (pending/approved/rejected)
POST/api/auth/confirmApprove/reject with signature + certificate
POST/api/auth/keycloak-verifyKeycloak SPI internal verification
GET/api/auth/caRoot + Intermediate CA certificates
POST/api/device/registerRegister device + issue X.509 certificate
PUT/api/device/tokenUpdate FCM push token
POST/api/device/fingerprintSave device fingerprint to PostgreSQL
GET/api/dashboard/statsUser count, device count, sessions
GET/api/dashboard/devicesRegistered device fingerprints
GET/api/dashboard/eventsKeycloak login/logout events

Live URLs

All services are live and operational.

🌐
Landing page + Login + Dashboard
πŸ”‘
Keycloak Admin Console
πŸ”
Real-time Debug Dashboard
πŸ“
System Architecture & Design