Aller au contenu

eID Gerege — TypeScript RP SDK

@eid-mongolia/sdk — Relying Party (RP)-ийн backend дээр ажилладаг Node.js/TypeScript сан. RP-API (/v3) руу хийх бүх дуудлагыг (нэвтрэлт, гарын үсэг, session poll) нэг EidClient-д цуглуулж, дээр нь хариуны крипто-баталгаажуулалт (cert chain + signature) нэмнэ.

Энэ бол docs/RP_INTEGRATION.md-д тайлбарласан raw HTTP гэрээний typed wrapper — ижил endpoint, ижил аюулгүйн загвар, гэхдээ challenge үүсгэх, session давтаж poll хийх, гэрчилгээ шалгах зэргийг таны оронд хийнэ.

⚠️ Зөвхөн server-side. API secret (rp_sk_…) браузер/гар утсанд хэзээ ч задрахгүй. SDK нь node:crypto-д тулгуурладаг тул browser bundle биш.

Эх код: sdk/typescript/src/


1. Суулгах

npm install @eid-mongolia/sdk
  • Node.js ≥ 18 (global fetch, node:crypto).
  • ESM ба CommonJS хоёуланг гаргадаг (import / require).
import { EidClient, sha256Base64 } from "@eid-mongolia/sdk";

Package нэр, engine шаардлага: sdk/typescript/package.json.


2. RP бүртгүүлэх

RP-г оператор урьдчилан бүртгэнэ (docs/RP_INTEGRATION.md §1). Admin console-оор: admin.eidmongolia.mnХолбогдогч тал (RP)+ Шинэ RP. Бүртгэмэгц UUID ба API secret (rp_sk_…) гарна — secret зөвхөн нэг удаа харагдана.

Эдгээрийг RpCredentials-д дамжуулна:

Талбар Утга
rpUUID relying_parties-д бүртгэлтэй RP UUID
rpName Иргэнд харагдах RP нэр (≤ 32 байт UTF-8)
apiSecret API secret (rp_sk_…) — зөвхөн backend-д

Эх: types.tsRpCredentials.


3. Client тохируулах

EidClient нь бүх flow-ийн үүд:

import { EidClient } from "@eid-mongolia/sdk";

const eid = new EidClient({
  baseUrl: "https://rp-api.eidmongolia.mn",          // /v3-гүйгээр — SDK нэмнэ
  credentials: {
    rpUUID: process.env.EID_RP_UUID!,
    rpName: "Хаан Банк",
    apiSecret: process.env.EID_SECRET!,              // rp_sk_…
  },
  trust: { trustAnchorsPem: [process.env.EID_ROOT_CA_PEM!] }, // production-д ЗААВАЛ
});

ClientConfig-ийн бүх талбар (client.ts):

Талбар Заавал Утга
baseUrl RP-API суурь URL. SDK /v3-ийг нэмнэ.
credentials RpCredentials (дээрх). apiSecret хоосон бол constructor алдана.
trust production-д ✅ TrustConfig — trust anchor (Root CA) + validator тохиргоо.
defaultCertificateLevel QUALIFIED (default) эсвэл ADVANCED.
timeoutMs HTTP timeout (мс). Default 15000.
dispatcher undici Dispatcher — mTLS client cert (§8).
fetchImpl fetch орлуулагч (тест).

Constructor нь baseUrl эсвэл credentials.apiSecret дутвал шууд Error шиднэ.

EidClient нь дараах дэд API-уудыг ил гаргана:

Талбар Класс Үүрэг
eid.auth AuthApi Нэвтрэлт эхлүүлэх (push / QR)
eid.sign SignApi Гарын үсэг эхлүүлэх (digest)
eid.session SessionApi Үр дүн poll хийх (long-poll)
eid.validator ResponseValidator Хариуг крипто-баталгаажуулах

4. Үндсэн flow-ууд

4.1 Нэвтрэлт (push)

eid.auth-ийн method бүр дотроо санамсаргүй rpChallenge үүсгэж, буцаах session-д хавсаргана (validator үүнийг signature-тэй тулгана). Эх: auth.ts.

// РД / иргэний дугаар / ETSI — аль нь ч болно (сервер төрлийг таьна)
const s = await eid.auth.notificationByEtsi("PNOMN-111949212017", [
  { type: "displayTextAndPIN", displayText60: "Хаан Банк-д нэвтрэх" },
]);

showToUser(s.vc);   // ← иргэний утсан дээр харагдах баталгаажуулах код (VC)

const result = await eid.session.waitForResult(s.sessionId);  // long-poll (§4.3)
const who = eid.validator.validateAuth(result, s.rpChallenge); // ← крипто-баталгаажуулна

console.log(who.documentNumber, who.subject);  // баталгаажсан иргэн

AuthApi-ийн method-ууд:

Method Endpoint Хариу
notificationByEtsi(id, interactions, opts?) POST /authentication/notification/etsi/{id} NotificationSession
notificationByDocument(documentNumber, interactions, opts?) POST /authentication/notification/document/{id} NotificationSession
deviceLinkAnonymous(interactions, opts?) POST /authentication/device-link/anonymous DeviceLinkSession
deviceLinkByEtsi(id, interactions, opts?) POST /authentication/device-link/etsi/{id} DeviceLinkSession
  • notification (push) — иргэний утас руу шууд мэдэгдэл очно. NotificationSession нь { sessionId, vc, rpChallenge } буцаана.
  • device-link (QR/App2App) — QR код/deeplink үүсгэхэд. DeviceLinkSession нь { sessionId, sessionToken, sessionSecret, deviceLinkBase, rpChallenge } буцаана.

AuthOptions (auth.ts): certificateLevel (энэ session-д default-ыг дарж бичих), callbackUrl (same-device App2App буцах URL — өгвөл initialCallbackUrl-аар илгээнэ).

4.2 Гарын үсэг (qualified signature)

RP нь баримтынхаа SHA-256 digest-ийг илгээж, хариунд ирэх signature-ийг тэр digest + иргэний cert-ийн эсрэг шалгана. Эх: sign.ts.

import { sha256Base64 } from "@eid-mongolia/sdk";

const digest = sha256Base64(pdfBytes);   // баримтын SHA-256 (base64)

const s = await eid.sign.digestByEtsi("PNOMN-111949212017", digest, [
  { type: "displayTextAndPIN", displayText60: "Зээлийн гэрээнд гарын үсэг зурах" },
]);
showToUser(s.vc);

const result = await eid.session.waitForResult(s.sessionId);
const sig = eid.validator.validateSign(result, digest);  // digest-ийн эсрэг баталгаажна

console.log(sig.signatureValueB64, sig.subject);

SignApi-ийн method-ууд:

Method Endpoint Тайлбар
digestByEtsi(id, digestB64, interactions, opts?) POST /signature/notification/etsi/{id} Бэлэн digest-ээр (бинар баримт)
digestByDocument(documentNumber, digestB64, interactions, opts?) POST /signature/notification/document/{id} Бэлэн digest-ээр (тодорхой төхөөрөмж)
textByEtsi(id, text, interactions, opts?) POST /signature/notification/etsi/{id} Текстийн digest-ийг дотроо тооцоод sign

SignOptions: certificateLevel, callbackUrl, hashType (SHA256 default, эсвэл SHA384/SHA512 — digest-ийг өөрөө бэлдсэн бол тааруулна).

Тэмдэглэл: sign flow-ийн NotificationSession.rpChallenge нь хоосон ("") — signature нь challenge биш digest-ийн эсрэг баталгаажна (sign.ts:54).

4.3 Session poll (long-poll)

SessionApi (session.ts) нь RP-API-ийн GET /session/{id}?timeoutMs=-ийг long-poll хэлбэрээр давтана.

Method Тайлбар
poll(sessionId, serverPollMs = 30000) Нэг удаагийн long-poll; serverPollMs хүртэл хүлээнэ.
waitForResult(sessionId, opts?) COMPLETE болтол давтаж poll хийнэ. opts = { maxWaitMs?, serverPollMs? }.
  • Default serverPollMs = 30_000, maxWaitMs = 150_000 (иргэн хариу өгөхгүй бол таслана).
  • HTTP timeout нь серверийн poll-оос 10 сек урт тавигдана.
  • waitForResult нь хугацаа дуустал COMPLETE болоогүй бол сүүлчийн (RUNNING) үр дүнг буцаана — validator түүнийг дуусаагүй session мэт ValidationError болгоно.

SessionResult талбарууд (types.ts): state, endResult, documentNumber, certificateDerB64, certificateLevel, signatureValueB64, signatureAlgorithm, interactionTypeUsed.


5. Хариуны баталгаажуулалт — яагаад validator заавал

⚠️ endResult === "OK" гэдэгт дангаар нь итгэхгүй. RP-API эвдэрсэн/proxy хийгдсэн бол хуурамч OK ирж болзошгүй.

ResponseValidator (validator.ts) дараах алхмуудыг гүйцэтгэнэ:

  1. state === COMPLETE && endResult === OK (эс бөгөөс SessionFailedError).
  2. Иргэний гэрчилгээг trust anchor (Gerege Root CA) хүртэл chain-аар баталгаажуулна (issuer нь CA байх ёстой, гүн ≤ 8).
  3. Гэрчилгээ хүчинтэй хугацаанд (validFrom/validTo, clockSkewMs зөвшөөрнө) + шаардсан certificateLevel хангах (QUALIFIED default).
  4. Гарын үсгийг иргэний public key-ээр шалгана:
  5. auth → ACSP_V2 payload (LP("ACSP_V2") ‖ LP(rpChallenge) ‖ LP(SPKI)) дээр
  6. sign → RP-ийн өгсөн digest дээр

Аль нэг алхам бүтэлгүйтвэл ValidationError шиднэ — хариунд итгэхгүй.

Нийтийн method:

Method Буцаах Тайлбар
validateAuth(result, rpChallengeB64) VerifiedIdentity { documentNumber, certificate, subject, certificateLevel }
validateSign(result, digestB64) VerifiedSignature { documentNumber, signatureValueB64, signatureAlgorithm, certificate, subject }
checkRevocation(cert) Promise<void> OCSP/CRL hook (доор).

5.1 TrustConfig тохиргоо

client.trust-д дамжина (validator.ts):

Талбар Утга
trustAnchorsPem Root CA PEM-үүд. Production-д ЗААВАЛ. Хоосон бол ValidationError (allowUntrusted-аар л алгасна).
intermediatesPem Intermediate CA PEM-үүд (leaf ↔ root).
requiredLevel Шаардах certLevel (default QUALIFIED).
clockSkewMs Validity шалгахад зөвшөөрөх цагийн зөрүү (default 0).
allowUntrusted true бол chain шалгахгүй — зөвхөн dev/тест.
revocation OCSP/CRL шалгагч hook (RevocationChecker).
revocationMode "hard-fail" (default; unknown-г татгалзана) эсвэл "soft-fail".

Revocation. revocation hook тохируулаагүй бол cert-ийн revoke төлөв шалгахгүй — энэ нь RP-ийн үүрэг. Тохируулбал validateAuth/validateSign-ийн дараа await eid.validator.checkRevocation(who.certificate) дуудна (revocation нь сүлжээ I/O тул async, validate нь sync).


6. Алдаа боловсруулалт

Бүх алдаа EidError-оос удамшина (errors.ts):

Алдаа Утга Талбар
AuthenticationError HTTP 401 — API secret буруу/байхгүй
ForbiddenError HTTP 403 — IP allowlist / mTLS зөвшөөрөөгүй
ApiError Бусад HTTP алдаа (4xx/5xx) .status, .body
NetworkError Timeout / сүлжээний алдаа
SessionFailedError endResult ≠ OK (TIMEOUT, USER_REFUSED*, DOCUMENT_UNUSABLE, WRONG_VC…) .endResult
ValidationError Cert chain / signature / level шалгалт бүтэлгүйтсэн
import { SessionFailedError, ValidationError } from "@eid-mongolia/sdk";

try {
  const result = await eid.session.waitForResult(s.sessionId);
  const who = eid.validator.validateAuth(result, s.rpChallenge);
} catch (e) {
  if (e instanceof SessionFailedError) {
    // e.endResult: "USER_REFUSED" | "TIMEOUT" | …  → UI мессеж, дахин оролдлого
  } else if (e instanceof ValidationError) {
    // хуурамч/proxy хийгдсэн байж болзошгүй — хариунд ИТГЭХГҮЙ
  }
  throw e;
}

7. Крипто туслахууд

node:crypto-д тулгуурласан экспорт (crypto.ts):

Функц Тайлбар
sha256Base64(data) Текст/байтын SHA-256 digest (base64) — sign flow-ийн digest бэлдэхэд.
randomChallenge(bytes = 64) Санамсаргүй RP challenge (base64). Auth method-ууд дотроо дууддаг.
buildAcspV2Payload(rpChallengeB64, spkiDER) ACSP_V2 payload угсарна — validator ашигладаг.

⚠️ ACSP_V2 payload нь сервер (server/internal/crypto/acsp.go), утас, RP SDK гурвуулаа байт-ижил байх ёстой. LP(x) = 4-байт big-endian урт ‖ x.


8. mTLS (eIDAS qualified орчин)

Production RP-API-д client cert шаардвал undici Agent-ийг dispatcher-аар өгнө (http.ts):

import { Agent } from "undici";

const eid = new EidClient({
  baseUrl: "https://rp-api.eidmongolia.mn",
  credentials: { /* … */ },
  trust: { trustAnchorsPem: [ROOT_CA_PEM] },
  dispatcher: new Agent({ connect: { cert: clientCertPem, key: clientKeyPem } }),
});

dispatcher өгвөл fetch дуудлага бүрд хэрэглэнэ (зөвхөн Node орчинд).


9. Raw HTTP хувилбартай холбоо

SDK нь docs/RP_INTEGRATION.md-ийн raw HTTP гэрээг яг дагадаг — endpoint, body талбар (relyingPartyUUID, relyingPartyName, certificateLevel, signatureProtocol: "ACSP_V2", interactions), Bearer auth бүгд ижил. SDK-гүйгээр шууд HTTP-ээр интеграцчлах, эсвэл endpoint-ийн бүрэн жагсаалт, иргэнийг таних дугааруудыг үзэхийг хүсвэл:


10. Хөгжүүлэлт

cd sdk/typescript
npm install
npm run lint   # tsc --noEmit
npm test       # build + node --test

Package: sdk/typescript/. TypeScript нь reference implementation — Go/Python хувилбарууд үүний API гадаргуу, аюулгүйн загвар (ResponseValidator), wire-contract-ийг дагана (sdk/README.md).