Web RP demo — Developer гарын авлага¶
eID платформын браузер RP demo — Next.js (App Router, TypeScript)-ээр
бичсэн жишээ Relying Party. Гар утасны eID Mongolia апп-аар QR / РД push
нэвтрэлт хийж, PDF-д qualified цахим гарын үсэг зурах урсгалыг браузерт
үзүүлнэ. Эх код: web/ (Java smartid-demo-ийн WebDemo.java + index.html-ийн
faithful порт — ижил дэлгэц, ижил урсгал, ижил монгол шошго).
Энэ бол бодит RP. macOS/iOS клиентээс ялгаатай нь web app өөрөө RP secret барьдаг: Go RP-API (
/v3/*)-ийнRP_API_SECRET-ийг зөвхөн серверийн талын route handler (web/src/lib/rpclient.ts) хэрэглэнэ. Браузер энэ секретийг хэзээ ч хардаггүй — бүх дуудлага web app-ийн нийтийн/api/*route-уудаар proxy хийгдэнэ. Дэлгэрэнгүй RP интеграцийг RP интеграц-аас, ижил/api/*-ийг хэрэглэдэг ширээний клиентийг macOS desktop-аас үзнэ үү.
1. Юу үзүүлдэг вэ¶
- Нэвтрэлт — QR код (
/api/start) эсвэл РД/иргэний дугаараар push (/api/login-notify). Утасны апп дээр PIN1-ээр баталгаажуулна. Server-side route гарын үсгийг rpChallenge дээр криптограф баталгаажуулаад cert subject-оос нэр + иргэний дугаар +documentNumber-ийг задалж буцаана (браузер cert parse хийхгүй). - PDF гарын үсэг — PDF сонгоод SHA-256 digest-ийг клиент дээр локал
тооцож, утасны апп дээр PIN2-оор (signing key, non-repudiation) зурна. Backend
тамгалагдсан (PAdES / PKCS#7 + баталгаажуулах хуудас) PDF-ийг буцаана
(
/api/sign-pdf-download). - Байгууллагын төлөөлөл — хувь хүн нэвтэрсний дараа "аль байгууллагаар
үргэлжлэх вэ" сонголтыг бүртгэлээс real-time авах (
/api/representations, Эстонийн äriregister аналог).
"Виртуал утас" симуляц ажиллахгүй. Java demo нь
VirtualPhone/PhoneCryptoJVM SDK-аар бодит threshold ECDSA хийдэг байсан. Тэр крипто зөвхөн JVM талд байгаа тул/api/simulate-phoneнь501(dev) /404(prod) буцаана (web/src/app/api/simulate-phone/route.ts). Бодит урсгал: eID Mongolia аппаар QR/push-ийг баталгаажуулна.
2. Архитектур¶
Браузер (page.tsx, demo/page.tsx)
│ fetch /api/* (same-origin, secret байхгүй)
▼
Next.js route handler (web/src/app/api/**, server-side)
│ rpclient.ts: Authorization: Bearer <RP_API_SECRET>
▼
Go RP-API (/v3/*, RP_API_BASE)
Java demo нь хөнгөн HTTP сервер дотроосоо RP-API руу холбогддог байв. Энэ порт-д
тэр backend туслах endpoint бүрийг Next.js route handler болгож, Go RP-API
(/v3/...) руу серверийн талаас дуудна (CORS-гүй, Java-той ижил логик). Wire-
contract нь Go DTO (server/internal/dto/*.go)-той 1:1: хариунд sessionID,
vc.value, result.endResult, result.documentNumber, cert.value (base64 DER),
cert.certificateLevel.
Секрет ба RP таних тэмдэг хаана байдаг¶
| Утга | Хаана | Тайлбар |
|---|---|---|
RP_API_SECRET |
env, зөвхөн server-side | rpclient.ts authHeaders()-д Authorization: Bearer болж илгээнэ. NEXT_PUBLIC_ биш тул client bundle-д задрахгүй. Тохируулаагүй бол header нэмэхгүй (dev / RP-auth унтраалттай). |
RP_UUID / RP_NAME |
rpclient.ts тогтмол |
Env биш — compile-time constant (RP_UUID = "2d87bd3a-…", RP_NAME = "Demo Bank"). relying_parties-д урьдчилан бүртгэлтэй, restart-д тогтвортой. |
rpChallenge |
server-side, санамсаргүй | Хүсэлт бүрд 64 random байт (base64). challengeStore-д sessionId-аар хадгалж, /status дээр AUTH гарын үсгийг ACSP_V2-оор баталгаажуулахад хэрэглэнэ. |
pollToken(PII gate).sessionIdнь QR-д ил гарах тул дангаараа нэр/ регистр/гарын үсэг унших эрх олгохгүй.start/login-notify/sign-pdf-startхариу бүрpollTokenбуцаадаг ба/api/status,/api/sign-pdf-downloadэнэ token-ыг заавал шаардана (web/src/lib/pollTokenStore.ts).
3. /api/* route-ууд¶
Нийтийн демо RP урсгал (page.tsx, demo/page.tsx, macOS клиент)¶
| Route | Метод | Юу хийдэг | Go RP-API endpoint |
|---|---|---|---|
/api/start |
POST | Anonymous QR session эхлүүлнэ → {sessionId, qr, deviceLinkBase, vc, pollToken} (qr = sessionId) |
POST /v3/authentication/device-link/anonymous |
/api/login-notify |
POST | {register, callbackUrl} — РД/иргэний дугаараар push → {sessionId, vc, pollToken}. Rate limit 60с/3 per target |
POST /v3/authentication/notification/etsi/{etsi} |
/api/status |
GET | ?sessionId=&pollToken= long-poll (сервер ~1с барина). COMPLETE/OK үед AUTH гарын үсгийг rpChallenge дээр verify хийж, cert-оос name/idNumber/documentNumber задална |
GET /v3/session/{id}?timeoutMs=1000 |
/api/sign-start |
POST | {etsi, doc} — текст баримтын гарын үсэг (PIN2) push → {sessionId, vc, doc, pollToken}. Rate limit 60с/3 |
POST /v3/signature/notification/etsi/{etsi} |
/api/sign-pdf-start |
POST | {etsi, digestB64, fileName, onBehalfOf?} — PDF-ийн SHA-256 digest-ийг PIN2-оор зурах session → {sessionId, vc, pollToken}. digest 32 байт байх ёстой; rate limit 60с/3 |
POST /v3/signature/notification/etsi/{etsi} |
/api/sign-pdf-download |
POST | multipart file + sessionId + pollToken → тамгалагдсан PDF bytes (application/pdf attachment) |
POST /v3/signature/stamp/{sessionId} |
/api/representations |
POST | {personId} — хувь хүний төлөөлж чадах ACTIVE байгууллагууд → {personEtsi, representations}. Rate limit 60с/10 |
GET /v3/organization/representations/etsi/{personEtsi} |
/api/simulate-phone |
POST | Хадгалагдсан товч — 501 (dev) / 404 (prod). Бодит threshold ECDSA JVM SDK шаардана |
— |
/api/health |
GET | {status:"ok", service:"eidmongolia-web"} |
— |
/demo/live proxy урсгал (web/src/app/api/demo/*)¶
Нийтийн вэбсайтын live демо хуудсанд зориулсан нарийн proxy — snake_case
хариу буцаах өөр shape-тэй ч мөн rpclient.ts (RP-API /v3)-ээр дамжина.
| Route | Метод | Юу хийдэг |
|---|---|---|
/api/demo/auth/init |
POST | Anonymous device-link auth → {session_id, device_link_url, control_code, poll_token, expires_at} (device_link_url = raw sessionId) |
/api/demo/auth/poll |
GET | ?id=&poll_token= — session poll, AUTH гарын үсгийг verify хийж identity буцаана |
/api/demo/sign/init |
POST | multipart file + x-eid-token header (нэвтрэлтээс гарсан documentNumber) — PDF-ийн SHA-256-г тооцоод PIN2 ceremony нээнэ → {session_id, document_hash, verification_code} |
/api/demo/sign/poll |
GET | ?id= + x-eid-token — COMPLETE/OK үед detached ECDSA signature_hex; хуудас өөрөө pdf-lib-ээр PDF-д шингээнэ (энд /download байхгүй) |
4. Ажиллуулах¶
cd web
npm install
npm run dev # http://localhost:3000
npm run build # production build шалгах
npm run lint
npm run dev нь web-ийг :3000, admin console-ыг :3001 дээр асаана (CLAUDE.md).
Орчны хувьсагч¶
| Хувьсагч | Default | Тайлбар |
|---|---|---|
RP_API_BASE |
— | Go RP-API-ийн суурь URL (server-only). Route handler /v3-ийг өөрөө нэмнэ. Production-д тохируулаагүй бол rpclient.ts алдаа шидэнэ |
NEXT_PUBLIC_API_BASE |
— | Хуучин fallback (нийцэл). RP_API_BASE тохируулаагүй бол уншина |
RP_API_SECRET |
— | RP shared secret. Go SMARTID_RP_API_SECRET-тэй тааруулна. Хоосон бол Bearer нэмэхгүй (dev) |
NODE_ENV |
— | production үед simulate-phone 404, RP_API_BASE заавал |
Dev fallback.
RP_API_BASE/NEXT_PUBLIC_API_BASEхоёулаа хоосон бол зөвхөн dev-дhttp://localhost:8080/v3руу унана (rpclient.tsapiBase()). Тиймээс локал туршилтад Go серверийг:8080дээр асаахад хангалттай.
Локал бүрэн стек (Go API + web):
cd server && SMARTID_RP_API_SECRET= go run ./cmd/smartid # Go API :8080 (RP-auth унтраалттай)
cd web && npm run dev # web :3000
Утасны апп мөн ижил Go серверт заасан байх ёстой (simulator дээр
SMARTID_REQUIRE_ATTESTATION=false).
5. rpclient.ts — RP-API руу яаж ханддаг¶
web/src/lib/rpclient.ts нь Java GeregeSmartIdRpClient + WebDemo-ийн
хуулбар. Зөвхөн server-side (route handler-уудаас) ажиллана.
- Суурь URL —
apiBase()=RP_API_BASE(эсвэлNEXT_PUBLIC_API_BASE) +/v3, ард талын/арилгана. - Auth —
authHeaders()ньRP_API_SECRETбайвалAuthorization: Bearer <secret>буцаана; байхгүй бол хоосон header (dev).post()/get()бүр энэ header-ийг залгана. - Хүсэлтийн body —
relyingPartyUUID,relyingPartyName,certificateLevel(QUALIFIED),signatureProtocol(ACSP_V2),interactions, шаардлагатай үедrpChallenge/digest+hashType/initialCallbackUrl/onBehalfOf. - Timeout — энгийн дуудлага 15с, long-poll 140с (
AbortSignal.timeout). - PDF stamp —
stampSignedPdf()нь дууссан SIGN session дээр эх PDF-ийгPOST /v3/signature/stamp/{sessionId}рууapplication/pdf-ээр илгээж, тамгалагдсан PDF-ийг буцаан авна.
web/src/lib/x509subject.ts нь cert subject-оос нэр + регистр (serialNumber)
гаргах минимал ASN.1 DER парсер (Java-д BouncyCastle-ийн орлуулга).
6. Холбоос¶
- RP интеграцийн ерөнхий гарын авлага: RP интеграц
- Ижил
/api/*-ийг хэрэглэдэг ширээний клиент: macOS desktop - Иргэнийг таних дугаарууд: IDENTIFIERS.md
Эх код (GitHub)¶
- Web demo:
https://github.com/gerege-systems/eid-platform-mn/tree/main/web - RP-API client:
https://github.com/gerege-systems/eid-platform-mn/blob/main/web/src/lib/rpclient.ts - Route handler-ууд:
https://github.com/gerege-systems/eid-platform-mn/tree/main/web/src/app/api /api/start:https://github.com/gerege-systems/eid-platform-mn/blob/main/web/src/app/api/start/route.ts/api/status:https://github.com/gerege-systems/eid-platform-mn/blob/main/web/src/app/api/status/route.ts/api/sign-pdf-download:https://github.com/gerege-systems/eid-platform-mn/blob/main/web/src/app/api/sign-pdf-download/route.ts
Next.js 16. Энэ repo Next.js 16 (breaking changes)-ыг ашигладаг. Next.js код бичихээсээ өмнө
web/AGENTS.md-ийн зааврын дагуу training data бишnode_modules/next/dist/docs/-ийг үзнэ үү.