Агуулгыг алгасах

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/PhoneCrypto JVM 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.ts apiBase()). Тиймээс локал туршилтад 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-уудаас) ажиллана.

  • Суурь URLapiBase() = RP_API_BASE (эсвэл NEXT_PUBLIC_API_BASE) + /v3, ард талын / арилгана.
  • AuthauthHeaders() нь RP_API_SECRET байвал Authorization: Bearer <secret> буцаана; байхгүй бол хоосон header (dev). post()/get() бүр энэ header-ийг залгана.
  • Хүсэлтийн bodyrelyingPartyUUID, relyingPartyName, certificateLevel (QUALIFIED), signatureProtocol (ACSP_V2), interactions, шаардлагатай үед rpChallenge / digest + hashType / initialCallbackUrl / onBehalfOf.
  • Timeout — энгийн дуудлага 15с, long-poll 140с (AbortSignal.timeout).
  • PDF stampstampSignedPdf() нь дууссан 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/-ийг үзнэ үү.