@gerege-systems/eid-mongolia-sdk 0.9.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,12 +6,15 @@ import {
6
6
  ForbiddenError,
7
7
  NetworkError,
8
8
  ON_BEHALF_ERROR_CODES,
9
+ RETRYABLE_ERROR_CODES,
9
10
  SessionFailedError,
10
11
  ValidationError,
11
12
  deviceLink,
12
13
  isMobileUserAgent,
13
- openOrShowQR
14
- } from "./chunk-NXDKQZIW.js";
14
+ maskPath,
15
+ openOrShowQR,
16
+ parseRetryAfter
17
+ } from "./chunk-YDVPZMAL.js";
15
18
 
16
19
  // src/http.ts
17
20
  var DEFAULT_TIMEOUT_MS = 15e3;
@@ -54,26 +57,32 @@ var Http = class {
54
57
  };
55
58
  if (this.cfg.dispatcher) init.dispatcher = this.cfg.dispatcher;
56
59
  let res;
60
+ const op = `${method} ${maskPath(path)}`;
57
61
  try {
58
62
  res = await f(url, init);
59
63
  } catch (e) {
60
64
  const err = e;
61
65
  if (err.name === "TimeoutError" || err.name === "AbortError") {
62
- throw new NetworkError(`\u0425\u04AF\u0441\u044D\u043B\u0442 \u0445\u0443\u0433\u0430\u0446\u0430\u0430 \u0445\u044D\u0442\u044D\u0440\u043B\u044D\u044D: ${method} ${path}`);
66
+ throw new NetworkError(`\u0425\u04AF\u0441\u044D\u043B\u0442 \u0445\u0443\u0433\u0430\u0446\u0430\u0430 \u0445\u044D\u0442\u044D\u0440\u043B\u044D\u044D: ${op}`, { op, timeout: true });
63
67
  }
64
- throw new NetworkError(`\u0421\u04AF\u043B\u0436\u044D\u044D\u043D\u0438\u0439 \u0430\u043B\u0434\u0430\u0430 (${method} ${path}): ${err.message}`);
68
+ throw new NetworkError(`\u0421\u04AF\u043B\u0436\u044D\u044D\u043D\u0438\u0439 \u0430\u043B\u0434\u0430\u0430 (${op}): ${err.message.split(url).join(op)}`, { op, timeout: false });
65
69
  }
66
70
  if (res.status < 300) return res;
67
71
  const text = await res.text();
68
72
  const { error, code } = errorBody(text);
69
- if (res.status === 401) throw new AuthenticationError("API secret \u0431\u0443\u0440\u0443\u0443 \u044D\u0441\u0432\u044D\u043B \u0431\u0430\u0439\u0445\u0433\u04AF\u0439 (401)");
73
+ const headers = res.headers;
74
+ const retryAfterMs = parseRetryAfter(headers.get("Retry-After"));
75
+ if (res.status === 401) {
76
+ throw new AuthenticationError(`API secret \u0431\u0443\u0440\u0443\u0443 \u044D\u0441\u0432\u044D\u043B \u0431\u0430\u0439\u0445\u0433\u04AF\u0439 (401${code ? ` ${code}` : ""})`, { code, serverMessage: error, body: text, headers });
77
+ }
70
78
  if (res.status === 403) {
71
79
  throw new ForbiddenError(
72
80
  error ? `RP-API 403${code ? ` ${code}` : ""}: ${error}` : "IP allowlist \u044D\u0441\u0432\u044D\u043B mTLS \u0437\u04E9\u0432\u0448\u04E9\u04E9\u0440\u04E9\u04E9\u0433\u04AF\u0439 (403)",
73
- code
81
+ code,
82
+ { headers, retryAfterMs }
74
83
  );
75
84
  }
76
- throw new ApiError(res.status, text, code);
85
+ throw new ApiError(res.status, text, code, { headers, retryAfterMs });
77
86
  }
78
87
  };
79
88
  function errorBody(text) {
@@ -90,6 +99,48 @@ function errorBody(text) {
90
99
  }
91
100
  }
92
101
 
102
+ // src/contract.ts
103
+ var RP_ROUTES = {
104
+ authNotificationEtsi: { method: "POST", path: "/authentication/notification/etsi/{semanticsIdentifier}" },
105
+ authNotificationDocument: { method: "POST", path: "/authentication/notification/document/{documentNumber}" },
106
+ authDeviceLinkAnonymous: { method: "POST", path: "/authentication/device-link/anonymous" },
107
+ authDeviceLinkEtsi: { method: "POST", path: "/authentication/device-link/etsi/{semanticsIdentifier}" },
108
+ signNotificationEtsi: { method: "POST", path: "/signature/notification/etsi/{semanticsIdentifier}" },
109
+ signNotificationDocument: { method: "POST", path: "/signature/notification/document/{documentNumber}" },
110
+ pdfPrepare: { method: "POST", path: "/pdf/sign/prepare" },
111
+ pdfDocument: { method: "GET", path: "/pdf/sign/{sessionID}/document" },
112
+ orgRepresentations: { method: "GET", path: "/organization/representations/etsi/{personEtsi}" },
113
+ session: { method: "GET", path: "/session/{sessionId}" },
114
+ personCertificates: { method: "GET", path: "/certificates/etsi/{personEtsi}" },
115
+ personDevices: { method: "GET", path: "/devices/etsi/{personEtsi}" },
116
+ personActivity: { method: "GET", path: "/rp/activity/etsi/{personEtsi}" },
117
+ personSummary: { method: "GET", path: "/person/summary/etsi/{personEtsi}" },
118
+ personChildren: { method: "GET", path: "/person/children/etsi/{personEtsi}" },
119
+ tokenCertificate: { method: "POST", path: "/token/certificate" },
120
+ tokenCertificateResult: { method: "GET", path: "/token/certificate/{sessionID}" }
121
+ };
122
+ var RP_ROUTES_UNDISCOVERED = {
123
+ tokenAuthChallenge: { method: "POST", path: "/token/auth/challenge" },
124
+ tokenAuthVerify: { method: "POST", path: "/token/auth/verify" }
125
+ };
126
+ function routePath(route, ...args) {
127
+ let i = 0;
128
+ const out = route.path.replace(/\{[^}]*\}/g, () => {
129
+ const a = args[i++];
130
+ if (a === void 0) throw new Error(`eid: route ${route.path}: \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 \u0434\u0443\u0442\u0443\u0443`);
131
+ return encodeURIComponent(a);
132
+ });
133
+ if (i !== args.length) throw new Error(`eid: route ${route.path}: \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 \u0438\u043B\u04AF\u04AF`);
134
+ return out;
135
+ }
136
+ var OIDC_EXPECT = {
137
+ defaultScope: "openid",
138
+ pkceMethod: "S256",
139
+ idTokenAlg: "ES256",
140
+ /** `Authorization: Basic` (RFC 6749 §2.3.1). */
141
+ tokenEndpointAuthMethod: "client_secret_basic"
142
+ };
143
+
93
144
  // src/crypto.ts
94
145
  import { createHash, randomBytes } from "crypto";
95
146
  function sha256Base64(data) {
@@ -192,25 +243,25 @@ var AuthApi = class {
192
243
  /** Push — РД/иргэний дугаар/ETSI-ээр (сервер төрлийг таьна). Иргэний утас руу мэдэгдэл очно. */
193
244
  async notificationByEtsi(id, interactions, opts) {
194
245
  const { body, acsp } = this.baseBody(randomChallenge(), interactions, ["Notification"], opts);
195
- const raw = await this.http.post(`/authentication/notification/etsi/${encodeURIComponent(id)}`, body);
246
+ const raw = await this.http.post(routePath(RP_ROUTES.authNotificationEtsi, id), body);
196
247
  return this.parseNotification(raw, acsp);
197
248
  }
198
249
  /** Push — баримтын дугаараар (тодорхой төхөөрөмж рүү). */
199
250
  async notificationByDocument(documentNumber, interactions, opts) {
200
251
  const { body, acsp } = this.baseBody(randomChallenge(), interactions, ["Notification"], opts);
201
- const raw = await this.http.post(`/authentication/notification/document/${encodeURIComponent(documentNumber)}`, body);
252
+ const raw = await this.http.post(routePath(RP_ROUTES.authNotificationDocument, documentNumber), body);
202
253
  return this.parseNotification(raw, acsp);
203
254
  }
204
255
  /** QR/App2App — нэр томьёололгүй (иргэн хэн болохыг урьдчилж мэдэхгүй). QR код үүсгэхэд. */
205
256
  async deviceLinkAnonymous(interactions, opts) {
206
257
  const { body, acsp } = this.baseBody(randomChallenge(), interactions, ["QR"], opts);
207
- const raw = await this.http.post(`/authentication/device-link/anonymous`, body);
258
+ const raw = await this.http.post(routePath(RP_ROUTES.authDeviceLinkAnonymous), body);
208
259
  return this.parseDeviceLink(raw, acsp);
209
260
  }
210
261
  /** QR/App2App — тодорхой иргэн (РД/civil_id/ETSI). */
211
262
  async deviceLinkByEtsi(id, interactions, opts) {
212
263
  const { body, acsp } = this.baseBody(randomChallenge(), interactions, ["QR"], opts);
213
- const raw = await this.http.post(`/authentication/device-link/etsi/${encodeURIComponent(id)}`, body);
264
+ const raw = await this.http.post(routePath(RP_ROUTES.authDeviceLinkEtsi, id), body);
214
265
  return this.parseDeviceLink(raw, acsp);
215
266
  }
216
267
  };
@@ -243,6 +294,7 @@ var SignApi = class {
243
294
  };
244
295
  if (opts?.callbackUrl?.trim()) body.initialCallbackUrl = opts.callbackUrl.trim();
245
296
  if (opts?.onBehalfOf?.trim()) body.onBehalfOf = opts.onBehalfOf.trim();
297
+ if (opts?.fileName?.trim()) body.fileName = opts.fileName.trim();
246
298
  return body;
247
299
  }
248
300
  parse(raw) {
@@ -255,12 +307,12 @@ var SignApi = class {
255
307
  * digest-ийг гаднаас бэлдэж өгнө — текст бус бинар баримтад тохиромжтой.
256
308
  */
257
309
  async digestByEtsi(id, digestB64, interactions, opts) {
258
- const raw = await this.http.post(`/signature/notification/etsi/${encodeURIComponent(id)}`, this.body(digestB64, interactions, opts));
310
+ const raw = await this.http.post(routePath(RP_ROUTES.signNotificationEtsi, id), this.body(digestB64, interactions, opts));
259
311
  return this.parse(raw);
260
312
  }
261
313
  /** Бэлэн digest-ээр push sign — баримтын дугаараар (auth-аас гарсан documentNumber дээр). */
262
314
  async digestByDocument(documentNumber, digestB64, interactions, opts) {
263
- const raw = await this.http.post(`/signature/notification/document/${encodeURIComponent(documentNumber)}`, this.body(digestB64, interactions, opts));
315
+ const raw = await this.http.post(routePath(RP_ROUTES.signNotificationDocument, documentNumber), this.body(digestB64, interactions, opts));
264
316
  return this.parse(raw);
265
317
  }
266
318
  /** Текстийн SHA-256 digest-ийг дотроо тооцоод push sign (богино текст баримтад тохиромжтой). */
@@ -301,9 +353,44 @@ function parseSessionResult(raw) {
301
353
  signatureAlgorithmParameters: parseSigParams(sig.signatureAlgorithmParameters),
302
354
  interactionTypeUsed: str(r.interactionTypeUsed),
303
355
  onBehalfOf: parseOrg(r.onBehalfOf),
304
- pdf: parsePdf(r.pdf)
356
+ pdf: parsePdf(r.pdf),
357
+ person: parsePerson(r.person),
358
+ tokenCertificate: parseTokenCert(r.tokenCertificate),
359
+ raw
305
360
  };
306
361
  }
362
+ var PERSON_STRINGS = [
363
+ "firstName",
364
+ "lastName",
365
+ "familyName",
366
+ "firstNameEn",
367
+ "lastNameEn",
368
+ "civilId",
369
+ "regNo",
370
+ "birthDate",
371
+ "gender"
372
+ ];
373
+ function parsePerson(raw) {
374
+ const p = asRecord(raw);
375
+ const out = {};
376
+ for (const k of PERSON_STRINGS) if (typeof p[k] === "string" && p[k]) out[k] = p[k];
377
+ if (typeof p.geID === "number" && p.geID !== 0) out.geID = p.geID;
378
+ return Object.keys(out).length ? out : null;
379
+ }
380
+ function parseTokenCert(raw) {
381
+ const t = asRecord(raw);
382
+ const status = str(t.status);
383
+ if (!status) return null;
384
+ const out = { status };
385
+ if (t.errorCode) out.errorCode = String(t.errorCode);
386
+ if (t.authSerialHex) out.authSerialHex = String(t.authSerialHex);
387
+ if (t.signSerialHex) out.signSerialHex = String(t.signSerialHex);
388
+ return out;
389
+ }
390
+ function personMatchesCertificate(person, subject) {
391
+ const serial = typeof subject === "string" ? subject : subject?.serialNumber;
392
+ return !!person?.civilId && !!serial && serial === `PNOMN-${person.civilId}`;
393
+ }
307
394
  function parsePdf(raw) {
308
395
  const p = asRecord(raw);
309
396
  const docID = str(p.docID);
@@ -357,7 +444,7 @@ var SessionApi = class {
357
444
  /** Нэг удаагийн long-poll. serverPollMs хүртэл хүлээнэ. */
358
445
  async poll(sessionId, serverPollMs = SERVER_POLL_MS) {
359
446
  const raw = await this.http.get(
360
- `/session/${encodeURIComponent(sessionId)}?timeoutMs=${serverPollMs}`,
447
+ `${routePath(RP_ROUTES.session, sessionId)}?timeoutMs=${serverPollMs}`,
361
448
  serverPollMs + 1e4
362
449
  // HTTP timeout нь серверийн poll-оос урт
363
450
  );
@@ -382,6 +469,7 @@ var SessionApi = class {
382
469
 
383
470
  // src/pdf.ts
384
471
  import { createHash as createHash2 } from "crypto";
472
+ var DEFAULT_PDF_TIMEOUT_MS = 8e4;
385
473
  function rec3(v) {
386
474
  return v && typeof v === "object" ? v : {};
387
475
  }
@@ -394,7 +482,10 @@ var PdfApi = class {
394
482
  http;
395
483
  creds;
396
484
  defaults;
397
- /** POST /pdf/sign/prepare — multipart (`request` JSON + `pdf`). SIGN эрх шаардана. */
485
+ /**
486
+ * POST /pdf/sign/prepare — multipart (`request` JSON + `pdf`). SIGN эрх шаардана. Түр алдаа: `ApiError` 429
487
+ * PDF_PREPARE_BUSY, 503 LTV_UNAVAILABLE / TSA_UNAVAILABLE — `retryAfterMs`-ийн дараа дахин (`retryable`).
488
+ */
398
489
  async prepare(input) {
399
490
  const request = {
400
491
  relyingPartyUUID: this.creds.rpUUID,
@@ -410,7 +501,7 @@ var PdfApi = class {
410
501
  const form = new FormData();
411
502
  form.append("request", new Blob([JSON.stringify(request)], { type: "application/json" }));
412
503
  form.append("pdf", new Blob([input.pdf], { type: "application/pdf" }), input.fileName);
413
- const r = rec3(await this.http.postMultipart("/pdf/sign/prepare", form, 6e4));
504
+ const r = rec3(await this.http.postMultipart(routePath(RP_ROUTES.pdfPrepare), form, this.defaults.timeoutMs ?? DEFAULT_PDF_TIMEOUT_MS));
414
505
  const receivedAt = Date.now();
415
506
  const vc = rec3(r.vc).value;
416
507
  const dl = rec3(r.deviceLink);
@@ -432,10 +523,12 @@ var PdfApi = class {
432
523
  }
433
524
  /**
434
525
  * GET /pdf/sign/{sessionID}/document — гарын үсэгтэй PDF (READY-ээс хойш 30 мин). Татсан байтын SHA-256-ийг
435
- * session-ий `pdf.outSha256`-тай тулга. Эрх хасагдсан бол `ForbiddenError` `.code = "REPRESENTATION_REVOKED"`.
526
+ * session-ий `pdf.outSha256`-тай тулга. Эрх хасагдсан бол `ForbiddenError` `.code = "REPRESENTATION_REVOKED"`. Баримт
527
+ * бэлэн болоогүй бол `ApiError` 503 `.code` DOCUMENT_PENDING / TSA_UNAVAILABLE / LTV_UNAVAILABLE — `retryAfterMs`-ийн
528
+ * дараа дахин татна (`retryable`).
436
529
  */
437
530
  document(sessionId) {
438
- return this.http.getBytes(`/pdf/sign/${encodeURIComponent(sessionId)}/document`, 6e4);
531
+ return this.http.getBytes(routePath(RP_ROUTES.pdfDocument, sessionId), this.defaults.timeoutMs ?? DEFAULT_PDF_TIMEOUT_MS);
439
532
  }
440
533
  /**
441
534
  * {@link document} + тулгалт: татсан байтын SHA-256 нь session poll-ийн `pdf.outSha256`-тай тэнцүү байх ёстой.
@@ -467,14 +560,54 @@ var OrganizationApi = class {
467
560
  http;
468
561
  /** GET /organization/representations/etsi/{personEtsi}. Сонголт нь итгэх эх биш — сервер дахин шалгана. */
469
562
  async getRepresentations(personEtsi) {
470
- const raw = await this.http.get(`/organization/representations/etsi/${encodeURIComponent(personEtsi)}`);
563
+ const raw = await this.http.get(routePath(RP_ROUTES.orgRepresentations, personEtsi));
471
564
  const list = Array.isArray(raw.representations) ? raw.representations : [];
472
565
  return { personEtsi: String(raw.personEtsi ?? personEtsi), representations: list };
473
566
  }
474
567
  };
475
568
 
476
- // src/validator.ts
477
- import { X509Certificate as X509Certificate2, constants, createHash as createHash3, publicDecrypt, verify } from "crypto";
569
+ // src/person.ts
570
+ function rec4(v) {
571
+ return v && typeof v === "object" ? v : {};
572
+ }
573
+ var PersonApi = class {
574
+ constructor(http) {
575
+ this.http = http;
576
+ }
577
+ http;
578
+ async get(path) {
579
+ const raw = await this.http.get(path);
580
+ return { ...rec4(raw), raw };
581
+ }
582
+ /** Иргэний бүх гэрчилгээ + статусын тоолол. */
583
+ certificates(personEtsi) {
584
+ return this.get(routePath(RP_ROUTES.personCertificates, personEtsi));
585
+ }
586
+ /** Иргэний холбоотой төхөөрөмжүүд (идэвхтэй + идэвхгүй). */
587
+ devices(personEtsi) {
588
+ return this.get(routePath(RP_ROUTES.personDevices, personEtsi));
589
+ }
590
+ /** Иргэний үйл ажиллагааны түүх. */
591
+ activity(personEtsi, query) {
592
+ const q = new URLSearchParams();
593
+ if (query?.flow) q.set("flow", query.flow);
594
+ if (query?.limit && query.limit > 0) q.set("limit", String(query.limit));
595
+ if (query?.offset && query.offset > 0) q.set("offset", String(query.offset));
596
+ const qs = q.toString();
597
+ return this.get(routePath(RP_ROUTES.personActivity, personEtsi) + (qs ? `?${qs}` : ""));
598
+ }
599
+ /** Самбарын нэгдсэн тоо. */
600
+ summary(personEtsi) {
601
+ return this.get(routePath(RP_ROUTES.personSummary, personEtsi));
602
+ }
603
+ /** Асран хамгаалж буй хүүхдүүд (зөвхөн унших). */
604
+ children(personEtsi) {
605
+ return this.get(routePath(RP_ROUTES.personChildren, personEtsi));
606
+ }
607
+ };
608
+
609
+ // src/token.ts
610
+ import { X509Certificate as X509Certificate2, createHash as createHash3, createPublicKey, timingSafeEqual } from "crypto";
478
611
 
479
612
  // src/subject.ts
480
613
  import { X509Certificate } from "crypto";
@@ -580,8 +713,207 @@ function parseSubject(cert) {
580
713
  out.name = full || out.commonName || null;
581
714
  return out;
582
715
  }
716
+ function certExtension(der, oid) {
717
+ const top = tlv(der, 0, der.length);
718
+ const tbs = tlv(der, top.start, top.end);
719
+ let f = tlv(der, tbs.start, tbs.end);
720
+ if (f.tag === 160) f = tlv(der, f.end, tbs.end);
721
+ for (let k = 0; k < 5; k++) f = tlv(der, f.end, tbs.end);
722
+ for (let pos = f.end; pos < tbs.end; ) {
723
+ const t = tlv(der, pos, tbs.end);
724
+ if (t.tag === 163) {
725
+ const seq = tlv(der, t.start, t.end);
726
+ for (let ep = seq.start; ep < seq.end; ) {
727
+ const ext = tlv(der, ep, seq.end);
728
+ const id = tlv(der, ext.start, ext.end);
729
+ let v = tlv(der, id.end, ext.end);
730
+ if (v.tag === 1) v = tlv(der, v.end, ext.end);
731
+ if (v.tag !== 4) throw new ValidationError("extension: extnValue OCTET STRING \u0431\u0438\u0448");
732
+ if (oidOf(der, id) === oid) return der.subarray(v.start, v.end);
733
+ ep = ext.end;
734
+ }
735
+ return void 0;
736
+ }
737
+ pos = t.end;
738
+ }
739
+ return void 0;
740
+ }
741
+
742
+ // src/token.ts
743
+ function rec5(v) {
744
+ return v && typeof v === "object" ? v : {};
745
+ }
746
+ function hexSha256Equal(hex, data) {
747
+ if (typeof hex !== "string" || !/^[0-9a-fA-F]{64}$/.test(hex)) return false;
748
+ return timingSafeEqual(createHash3("sha256").update(data).digest(), Buffer.from(hex, "hex"));
749
+ }
750
+ var TOKEN_RESULT_TIMEOUT_MS = 3e4;
751
+ var TokenApi = class {
752
+ constructor(http, creds) {
753
+ this.http = http;
754
+ this.creds = creds;
755
+ }
756
+ http;
757
+ creds;
758
+ /**
759
+ * POST /token/certificate — токены AUTH + SIGN гэрчилгээ олголт эхлүүлнэ (иргэн утсаараа батална). CA-ийн буцаасан
760
+ * CSR hash-ууд илгээсэнтэй таарахгүй бол ValidationError.
761
+ */
762
+ async issueCertificate(input) {
763
+ if (!input.signerEtsi?.trim() || !input.csrAuth?.length || !input.csrSign?.length || !input.tokenSerial?.trim()) {
764
+ throw new Error("token: signerEtsi, csrAuth, csrSign, tokenSerial \u0448\u0430\u0430\u0440\u0434\u043B\u0430\u0433\u0430\u0442\u0430\u0439");
765
+ }
766
+ const body = {
767
+ relyingPartyUUID: this.creds.rpUUID,
768
+ relyingPartyName: this.creds.rpName,
769
+ signer: { etsi: input.signerEtsi.trim() },
770
+ csrAuth: Buffer.from(input.csrAuth).toString("base64"),
771
+ csrSign: Buffer.from(input.csrSign).toString("base64"),
772
+ tokenSerial: input.tokenSerial.trim(),
773
+ onBehalfOf: input.onBehalfOf?.trim() ?? "",
774
+ flow: "notification"
775
+ };
776
+ if (input.tokenLabel) body.tokenLabel = input.tokenLabel;
777
+ const r = rec5(await this.http.post(routePath(RP_ROUTES.tokenCertificate), body));
778
+ if (!hexSha256Equal(r.csrAuthSha256, input.csrAuth) || !hexSha256Equal(r.csrSignSha256, input.csrSign)) {
779
+ throw new ValidationError("token: CA-\u0438\u0439\u043D csrAuthSha256/csrSignSha256 \u0438\u043B\u0433\u044D\u044D\u0441\u044D\u043D CSR-\u0442\u044D\u0439 \u0442\u0430\u0430\u0440\u0441\u0430\u043D\u0433\u04AF\u0439");
780
+ }
781
+ const vc = rec5(r.vc).value ?? (typeof r.vc === "string" ? r.vc : void 0);
782
+ return {
783
+ sessionId: String(r.sessionID ?? ""),
784
+ vc: vc != null ? String(vc) : null,
785
+ csrAuthSha256: String(r.csrAuthSha256).toLowerCase(),
786
+ csrSignSha256: String(r.csrSignSha256).toLowerCase(),
787
+ ...typeof r.expiresAt === "string" ? { expiresAt: r.expiresAt } : {}
788
+ };
789
+ }
790
+ /**
791
+ * GET /token/certificate/{sessionID} — олгосон AUTH + SIGN гэрчилгээ + гинж. Токенд бичихээс өмнө
792
+ * {@link verifyTokenCertificates}-ээр CSR-ийн түлхүүртэй тулга.
793
+ */
794
+ async certificateResult(sessionId) {
795
+ const raw = await this.http.get(routePath(RP_ROUTES.tokenCertificateResult, sessionId), TOKEN_RESULT_TIMEOUT_MS);
796
+ const r = rec5(raw);
797
+ const item = (v) => v && typeof v === "object" ? v : null;
798
+ return {
799
+ status: String(r.status ?? ""),
800
+ auth: item(r.auth),
801
+ sign: item(r.sign),
802
+ chain: Array.isArray(r.chain) ? r.chain.map(String) : [],
803
+ chainPem: String(r.chainPem ?? ""),
804
+ organization: r.organization && typeof r.organization === "object" ? r.organization : null,
805
+ keyMedium: String(r.keyMedium ?? ""),
806
+ certificateLevel: String(r.certificateLevel ?? ""),
807
+ raw
808
+ };
809
+ }
810
+ /** POST /token/auth/challenge — энэ RP-д холбогдсон нэг удаагийн challenge (2 мин). */
811
+ async authChallenge() {
812
+ const r = rec5(
813
+ await this.http.post(routePath(RP_ROUTES_UNDISCOVERED.tokenAuthChallenge), {
814
+ relyingPartyUUID: this.creds.rpUUID,
815
+ relyingPartyName: this.creds.rpName
816
+ })
817
+ );
818
+ return {
819
+ challengeId: String(r.challengeId ?? ""),
820
+ challenge: String(r.challenge ?? ""),
821
+ rpUUID: String(r.rpUUID ?? ""),
822
+ expiresAt: String(r.expiresAt ?? "")
823
+ };
824
+ }
825
+ /**
826
+ * POST /token/auth/verify — токены AUTH гэрчилгээ (DER) + challenge дээрх түүхий r||s. CA гинж, төлөв, цуцлалт, гарын
827
+ * үсгийг шалгана; SDK нэмж endResult === OK ба identity.etsi === гэрчилгээний SERIALNUMBER-ийг тулгана (эс бөгөөс
828
+ * ValidationError). Challenge нэг удаагийн — алдааны дараа шинээр авна.
829
+ */
830
+ async authVerify(challengeId, certDer, signature) {
831
+ let serial;
832
+ try {
833
+ serial = parseSubject(new X509Certificate2(certDer)).serialNumber;
834
+ } catch {
835
+ throw new Error("token auth: \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D \u0437\u0430\u0434\u0430\u0440\u0441\u0430\u043D\u0433\u04AF\u0439");
836
+ }
837
+ if (!challengeId || !signature?.length) throw new Error("token auth: challengeId \u0431\u0430 signature \u0448\u0430\u0430\u0440\u0434\u043B\u0430\u0433\u0430\u0442\u0430\u0439");
838
+ const raw = await this.http.post(routePath(RP_ROUTES_UNDISCOVERED.tokenAuthVerify), {
839
+ challengeId,
840
+ certificate: Buffer.from(certDer).toString("base64"),
841
+ signature: Buffer.from(signature).toString("base64")
842
+ });
843
+ const r = rec5(raw);
844
+ if (rec5(r.result).endResult !== "OK") throw new ValidationError("token auth: endResult OK \u0431\u0438\u0448");
845
+ const id = rec5(r.identity);
846
+ if (!id.etsi || id.etsi !== serial) throw new ValidationError("token auth: identity.etsi \u043D\u044C \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D\u043D\u0438\u0439 SERIALNUMBER \u0431\u0438\u0448");
847
+ const org = rec5(r.organization);
848
+ const cert = rec5(r.cert);
849
+ return {
850
+ identity: { etsi: String(id.etsi), givenName: String(id.givenName ?? ""), surname: String(id.surname ?? "") },
851
+ serialHex: String(cert.serialHex ?? ""),
852
+ notAfter: String(cert.notAfter ?? ""),
853
+ organization: org.etsi ? { etsi: String(org.etsi), name: String(org.name ?? "") } : null,
854
+ raw
855
+ };
856
+ }
857
+ };
858
+ function csrSpki(csr) {
859
+ const top = tlv(csr, 0, csr.length);
860
+ const info = tlv(csr, top.start, top.end);
861
+ const version = tlv(csr, info.start, info.end);
862
+ const subject = tlv(csr, version.end, info.end);
863
+ const spki = tlv(csr, subject.end, info.end);
864
+ return createPublicKey({ key: Buffer.from(csr.subarray(subject.end, spki.end)), format: "der", type: "spki" }).export({
865
+ format: "der",
866
+ type: "spki"
867
+ });
868
+ }
869
+ function verifyTokenCertificates(result, csrAuth, csrSign) {
870
+ if (!result?.auth || !result.sign) throw new ValidationError("token: auth/sign \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D \u0430\u043B\u0433\u0430");
871
+ for (const [name, item, csr] of [
872
+ ["auth", result.auth, csrAuth],
873
+ ["sign", result.sign, csrSign]
874
+ ]) {
875
+ let want;
876
+ try {
877
+ want = csrSpki(csr);
878
+ } catch {
879
+ throw new Error(`token: ${name} CSR \u0437\u0430\u0434\u0430\u0440\u0441\u0430\u043D\u0433\u04AF\u0439`);
880
+ }
881
+ let got;
882
+ try {
883
+ got = new X509Certificate2(Buffer.from(item.certificate, "base64")).publicKey.export({ format: "der", type: "spki" });
884
+ } catch {
885
+ throw new ValidationError(`token: ${name} \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D \u0437\u0430\u0434\u0430\u0440\u0441\u0430\u043D\u0433\u04AF\u0439`);
886
+ }
887
+ if (!got.equals(want)) throw new ValidationError(`token: ${name} \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D\u043D\u0438\u0439 \u0442\u04AF\u043B\u0445\u04AF\u04AF\u0440 CSR-\u0438\u0439\u043D\u0445\u0442\u044D\u0439 \u0442\u0430\u0430\u0440\u0441\u0430\u043D\u0433\u04AF\u0439`);
888
+ }
889
+ }
890
+ function tokenAuthDigest(rpUUID, challengeB64) {
891
+ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(rpUUID)) {
892
+ throw new Error("token auth: rpUUID \u0436\u0438\u0436\u0438\u0433 \u04AF\u0441\u0433\u0438\u0439\u043D 36 \u0442\u044D\u043C\u0434\u044D\u0433\u0442 UUID \u0431\u0438\u0448");
893
+ }
894
+ const ch = Buffer.from(challengeB64, "base64");
895
+ if (ch.length !== 32 || ch.toString("base64") !== challengeB64) throw new Error("token auth: challenge 32 \u0431\u0430\u0439\u0442 base64 \u0431\u0438\u0448");
896
+ return createHash3("sha256").update("eID-TOKEN-AUTH", "ascii").update(rpUUID, "ascii").update(ch).digest();
897
+ }
898
+ function tokenLimitOf(err) {
899
+ if (!(err instanceof ApiError) || err.code !== "TOKEN_LIMIT_REACHED") return void 0;
900
+ let b;
901
+ try {
902
+ b = rec5(JSON.parse(err.body));
903
+ } catch {
904
+ return void 0;
905
+ }
906
+ if (typeof b.limit !== "number" || !Number.isInteger(b.limit) || b.limit < 0) return void 0;
907
+ if (b.kind === "PERSONAL") return { kind: "PERSONAL", limit: b.limit };
908
+ if (b.kind === "ORGANIZATION" && typeof b.organization === "string" && b.organization.startsWith("NTRMN-")) {
909
+ return { kind: "ORGANIZATION", limit: b.limit, organization: b.organization };
910
+ }
911
+ return void 0;
912
+ }
583
913
 
584
914
  // src/validator.ts
915
+ import { X509Certificate as X509Certificate3, constants, createHash as createHash4, publicDecrypt, verify } from "crypto";
916
+ var CITIZEN_ISSUING_CA_SKI = "b8a37f2c64ad677912db7d9e34f246358a2fb909";
585
917
  var ResponseValidator = class {
586
918
  anchors;
587
919
  intermediates;
@@ -590,14 +922,28 @@ var ResponseValidator = class {
590
922
  allowUntrusted;
591
923
  revocation;
592
924
  revocationMode;
925
+ allowedIssuers;
926
+ checkKeyUsage;
927
+ schemeName;
593
928
  constructor(trust = {}) {
594
- this.anchors = (trust.trustAnchorsPem ?? []).map((p) => new X509Certificate2(p));
595
- this.intermediates = (trust.intermediatesPem ?? []).map((p) => new X509Certificate2(p));
929
+ this.anchors = (trust.trustAnchorsPem ?? []).map((p) => new X509Certificate3(p));
930
+ this.intermediates = (trust.intermediatesPem ?? []).map((p) => new X509Certificate3(p));
596
931
  this.requiredLevel = trust.requiredLevel ?? "QUALIFIED";
597
932
  this.skew = trust.clockSkewMs ?? 0;
598
933
  this.allowUntrusted = trust.allowUntrusted ?? false;
599
934
  this.revocation = trust.revocation;
600
935
  this.revocationMode = trust.revocationMode ?? "hard-fail";
936
+ if (trust.allowedIssuerSkis?.length) {
937
+ this.allowedIssuers = new Set(
938
+ trust.allowedIssuerSkis.map((s) => {
939
+ const ski = s.trim().replace(/:/g, "").toLowerCase();
940
+ if (!/^(?:[0-9a-f]{2})+$/.test(ski)) throw new Error(`ResponseValidator: allowedIssuerSkis: "${s}" hex key identifier \u0431\u0438\u0448`);
941
+ return ski;
942
+ })
943
+ );
944
+ }
945
+ this.checkKeyUsage = trust.checkKeyUsage ?? false;
946
+ this.schemeName = trust.schemeName;
601
947
  }
602
948
  // ── нийтийн API ──
603
949
  /**
@@ -625,6 +971,7 @@ var ResponseValidator = class {
625
971
  if (!interactionTypeUsed) throw new ValidationError("interactionTypeUsed \u0430\u043B\u0433\u0430");
626
972
  const { alg, hash } = this.algorithmOf(result, leaf, acsp.signatureAlgorithm, acsp.hashAlgorithm);
627
973
  const payload = buildAcspV2Payload({
974
+ ...this.schemeName ? { schemeName: this.schemeName } : {},
628
975
  serverRandom,
629
976
  rpChallenge: acsp.rpChallenge,
630
977
  userChallenge,
@@ -728,9 +1075,10 @@ var ResponseValidator = class {
728
1075
  if (!result.certificateDerB64) throw new ValidationError("\u0425\u0430\u0440\u0438\u0443\u043D\u0434 \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D \u0430\u043B\u0433\u0430");
729
1076
  if (!result.signatureValueB64) throw new ValidationError("\u0425\u0430\u0440\u0438\u0443\u043D\u0434 \u0433\u0430\u0440\u044B\u043D \u04AF\u0441\u044D\u0433 \u0430\u043B\u0433\u0430");
730
1077
  if (!result.documentNumber) throw new ValidationError("\u0425\u0430\u0440\u0438\u0443\u043D\u0434 documentNumber \u0430\u043B\u0433\u0430");
731
- const leaf = new X509Certificate2(Buffer.from(result.certificateDerB64, "base64"));
1078
+ const leaf = new X509Certificate3(Buffer.from(result.certificateDerB64, "base64"));
732
1079
  this.checkValidity(leaf);
733
1080
  this.checkLevel(String(result.certificateLevel ?? ""));
1081
+ this.checkPurpose(leaf, protocol);
734
1082
  if (this.anchors.length === 0) {
735
1083
  if (!this.allowUntrusted) {
736
1084
  throw new ValidationError(
@@ -740,8 +1088,35 @@ var ResponseValidator = class {
740
1088
  } else {
741
1089
  this.verifyChain(leaf);
742
1090
  }
1091
+ this.checkIssuer(leaf);
743
1092
  return leaf;
744
1093
  }
1094
+ /**
1095
+ * checkKeyUsage үед гэрчилгээний зориулалт протоколд тохирох эсэх (CA crypto/ca_profile.go): ACSP_V2 (auth) → KU
1096
+ * digitalSignature + EKU clientAuth (эсвэл anyExtendedKeyUsage); RAW_DIGEST_SIGNATURE (sign) → KU contentCommitment.
1097
+ */
1098
+ checkPurpose(leaf, protocol) {
1099
+ if (!this.checkKeyUsage) return;
1100
+ const ku = keyUsageBits(leaf);
1101
+ if (protocol === "ACSP_V2") {
1102
+ if (!(ku & 128)) throw new ValidationError("\u041D\u044D\u0432\u0442\u0440\u044D\u043B\u0442\u0438\u0439\u043D \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D\u043D\u0434 KeyUsage digitalSignature \u0430\u043B\u0433\u0430");
1103
+ const eku = extKeyUsage(leaf);
1104
+ if (!eku.includes("1.3.6.1.5.5.7.3.2") && !eku.includes("2.5.29.37.0")) {
1105
+ throw new ValidationError("\u041D\u044D\u0432\u0442\u0440\u044D\u043B\u0442\u0438\u0439\u043D \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D\u043D\u0434 ExtKeyUsage clientAuth \u0430\u043B\u0433\u0430 (\u0433\u0430\u0440\u044B\u043D \u04AF\u0441\u0433\u0438\u0439\u043D \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D \u0431\u0430\u0439\u0436 \u0431\u043E\u043B\u0437\u043E\u0448\u0433\u04AF\u0439)");
1106
+ }
1107
+ return;
1108
+ }
1109
+ if (!(ku & 64)) throw new ValidationError("\u0413\u0430\u0440\u044B\u043D \u04AF\u0441\u0433\u0438\u0439\u043D \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D\u043D\u0434 KeyUsage nonRepudiation (contentCommitment) \u0430\u043B\u0433\u0430");
1110
+ }
1111
+ /** allowedIssuerSkis үед leaf-ийн шууд issuer-ийн SKI (issuer-т алга бол leaf-ийн AKI) жагсаалтад байх ёстой. */
1112
+ checkIssuer(leaf) {
1113
+ if (!this.allowedIssuers) return;
1114
+ const issuer = this.findIssuer(leaf);
1115
+ const ski = (issuer && subjectKeyId(issuer)) ?? authorityKeyId(leaf);
1116
+ if (!ski || !this.allowedIssuers.has(ski)) {
1117
+ throw new ValidationError("\u0413\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D\u0433 \u0433\u0430\u0440\u0433\u0430\u0441\u0430\u043D CA \u0437\u04E9\u0432\u0448\u04E9\u04E9\u0440\u04E9\u0433\u0434\u0441\u04E9\u043D \u0438\u0440\u0433\u044D\u043D\u0438\u0439 issuer \u0431\u0438\u0448 (allowedIssuerSkis)");
1118
+ }
1119
+ }
745
1120
  checkValidity(cert) {
746
1121
  const now = Date.now();
747
1122
  const from = Date.parse(cert.validFrom);
@@ -784,14 +1159,23 @@ var ResponseValidator = class {
784
1159
  * дуудна (тэдгээр нь sync; revocation нь сүлжээ I/O тул async).
785
1160
  */
786
1161
  async checkRevocation(cert) {
787
- if (!this.revocation) return;
1162
+ await this.checkRevocationStatus(cert);
1163
+ }
1164
+ /**
1165
+ * {@link checkRevocation} + юу шийдсэнийг буцаана (лог/аудитад): "good" | "unchecked" (unknown, soft-fail) |
1166
+ * "skipped" (hook алга). "revoked", мөн hard-fail горимд "unknown" → ValidationError; hook-ийн алдаа дамжина.
1167
+ */
1168
+ async checkRevocationStatus(cert) {
1169
+ if (!this.revocation) return "skipped";
788
1170
  const status = await this.revocation(cert, this.findIssuer(cert));
1171
+ if (status === "good") return "good";
789
1172
  if (status === "revoked") {
790
1173
  throw new ValidationError("\u0413\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D \u0446\u0443\u0446\u043B\u0430\u0433\u0434\u0441\u0430\u043D (revoked)");
791
1174
  }
792
- if (status === "unknown" && this.revocationMode === "hard-fail") {
1175
+ if (this.revocationMode === "hard-fail") {
793
1176
  throw new ValidationError("\u0413\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D\u043D\u0438\u0439 revocation \u0442\u04E9\u043B\u04E9\u0432 \u0442\u043E\u0433\u0442\u043E\u043E\u0433\u0434\u0441\u043E\u043D\u0433\u04AF\u0439 (hard-fail)");
794
1177
  }
1178
+ return "unchecked";
795
1179
  }
796
1180
  };
797
1181
  function signedCallbackUrl(fromResponse, rpCallback, flowType) {
@@ -806,7 +1190,7 @@ function signedCallbackUrl(fromResponse, rpCallback, flowType) {
806
1190
  return fromResponse;
807
1191
  }
808
1192
  function userChallengeOf(verifier) {
809
- return createHash3("sha256").update(verifier, "utf8").digest("base64url");
1193
+ return createHash4("sha256").update(verifier, "utf8").digest("base64url");
810
1194
  }
811
1195
  function verifyPayloadSignature(payload, sigB64, key, alg, hash) {
812
1196
  const sig = Buffer.from(sigB64, "base64");
@@ -908,7 +1292,7 @@ function mgf1(hash, seed, length) {
908
1292
  for (let i = 0, n = 0; n < length; i++, n += hashLen(hash)) {
909
1293
  const c = Buffer.alloc(4);
910
1294
  c.writeUInt32BE(i, 0);
911
- out.push(createHash3(nodeHash(hash)).update(seed).update(c).digest());
1295
+ out.push(createHash4(nodeHash(hash)).update(seed).update(c).digest());
912
1296
  }
913
1297
  return Buffer.concat(out).subarray(0, length);
914
1298
  }
@@ -932,9 +1316,42 @@ function emsaPssVerify(mHash, em, emBits, hash) {
932
1316
  const psLen = emLen - hLen - sLen - 2;
933
1317
  if (!db.subarray(0, psLen).every((b) => b === 0) || db[psLen] !== 1) return false;
934
1318
  const salt = db.subarray(psLen + 1);
935
- const h2 = createHash3(nodeHash(hash)).update(Buffer.alloc(8)).update(mHash).update(salt).digest();
1319
+ const h2 = createHash4(nodeHash(hash)).update(Buffer.alloc(8)).update(mHash).update(salt).digest();
936
1320
  return h.equals(h2);
937
1321
  }
1322
+ function keyUsageBits(cert) {
1323
+ const v = certExtension(cert.raw, "2.5.29.15");
1324
+ if (!v) return 0;
1325
+ const bs = tlv(v, 0, v.length);
1326
+ if (bs.tag !== 3 || bs.end - bs.start < 2) return 0;
1327
+ return v[bs.start + 1];
1328
+ }
1329
+ function extKeyUsage(cert) {
1330
+ const v = certExtension(cert.raw, "2.5.29.37");
1331
+ if (!v) return [];
1332
+ const seq = tlv(v, 0, v.length);
1333
+ const out = [];
1334
+ for (let p = seq.start; p < seq.end; ) {
1335
+ const t = tlv(v, p, seq.end);
1336
+ out.push(oidOf(v, t));
1337
+ p = t.end;
1338
+ }
1339
+ return out;
1340
+ }
1341
+ function subjectKeyId(cert) {
1342
+ const v = certExtension(cert.raw, "2.5.29.14");
1343
+ if (!v) return void 0;
1344
+ const o = tlv(v, 0, v.length);
1345
+ return o.tag === 4 && o.end > o.start ? Buffer.from(v.subarray(o.start, o.end)).toString("hex") : void 0;
1346
+ }
1347
+ function authorityKeyId(cert) {
1348
+ const v = certExtension(cert.raw, "2.5.29.35");
1349
+ if (!v) return void 0;
1350
+ const seq = tlv(v, 0, v.length);
1351
+ if (seq.start >= seq.end) return void 0;
1352
+ const k = tlv(v, seq.start, seq.end);
1353
+ return k.tag === 128 && k.end > k.start ? Buffer.from(v.subarray(k.start, k.end)).toString("hex") : void 0;
1354
+ }
938
1355
  function isCa(cert) {
939
1356
  return cert.ca === true;
940
1357
  }
@@ -962,6 +1379,8 @@ var EidClient = class {
962
1379
  sign;
963
1380
  pdf;
964
1381
  organization;
1382
+ person;
1383
+ token;
965
1384
  session;
966
1385
  validator;
967
1386
  constructor(cfg) {
@@ -977,8 +1396,10 @@ var EidClient = class {
977
1396
  const level = cfg.defaultCertificateLevel ?? "QUALIFIED";
978
1397
  this.auth = new AuthApi(http, cfg.credentials, { certificateLevel: level });
979
1398
  this.sign = new SignApi(http, cfg.credentials, { certificateLevel: level });
980
- this.pdf = new PdfApi(http, cfg.credentials, { certificateLevel: level });
1399
+ this.pdf = new PdfApi(http, cfg.credentials, { certificateLevel: level, timeoutMs: cfg.pdfTimeoutMs ?? DEFAULT_PDF_TIMEOUT_MS });
981
1400
  this.organization = new OrganizationApi(http);
1401
+ this.person = new PersonApi(http);
1402
+ this.token = new TokenApi(http, cfg.credentials);
982
1403
  this.session = new SessionApi(http);
983
1404
  this.validator = new ResponseValidator(cfg.trust ?? {});
984
1405
  }
@@ -1055,7 +1476,7 @@ function qrDeviceLinkTicker(input, onLink, opts) {
1055
1476
  }
1056
1477
 
1057
1478
  // src/oidc.ts
1058
- import { timingSafeEqual } from "crypto";
1479
+ import { timingSafeEqual as timingSafeEqual2 } from "crypto";
1059
1480
  var EID_OIDC_ISSUER = "https://login.eidmongolia.mn";
1060
1481
  var EID_ACR_QUALIFIED = "urn:eidmongolia:loa:qualified";
1061
1482
  var EID_ACR_ADVANCED = "urn:eidmongolia:loa:advanced";
@@ -1092,7 +1513,7 @@ var EidOidcClient = class {
1092
1513
  if (!cfg.clientSecret) throw new EidError("EidOidcClient: clientSecret \u0445\u043E\u043E\u0441\u043E\u043D");
1093
1514
  if (!/^https:\/\//.test(cfg.redirectUri ?? "")) throw new EidError("EidOidcClient: redirectUri https:// \u0431\u0430\u0439\u0445 \u0451\u0441\u0442\u043E\u0439");
1094
1515
  this.issuer = (cfg.issuer ?? EID_OIDC_ISSUER).replace(/\/+$/, "");
1095
- this.scope = cfg.scope ?? "openid";
1516
+ this.scope = cfg.scope ?? OIDC_EXPECT.defaultScope;
1096
1517
  }
1097
1518
  cfg;
1098
1519
  issuer;
@@ -1140,7 +1561,7 @@ var EidOidcClient = class {
1140
1561
  state,
1141
1562
  nonce,
1142
1563
  code_challenge: await pkceChallenge(codeVerifier),
1143
- code_challenge_method: "S256"
1564
+ code_challenge_method: OIDC_EXPECT.pkceMethod
1144
1565
  });
1145
1566
  if (opts.display) p.set("display", opts.display);
1146
1567
  u.search = p.toString();
@@ -1219,7 +1640,7 @@ var EidOidcClient = class {
1219
1640
  const header = decodeJsonPart(parts[0]);
1220
1641
  const payload = decodeJsonPart(parts[1]);
1221
1642
  if (!header || !payload) throw bad("\u0445\u044D\u043B\u0431\u044D\u0440 \u0431\u0443\u0440\u0443\u0443");
1222
- if (header.alg !== "ES256") throw bad("alg ES256 \u0431\u0438\u0448");
1643
+ if (header.alg !== OIDC_EXPECT.idTokenAlg) throw bad("alg ES256 \u0431\u0438\u0448");
1223
1644
  const kid = typeof header.kid === "string" ? header.kid : "";
1224
1645
  if (!kid) throw bad("kid \u0430\u043B\u0433\u0430");
1225
1646
  const pick = (ks) => ks.find(
@@ -1312,7 +1733,7 @@ function formEncode(s) {
1312
1733
  function timingSafeEqualStr(a, b) {
1313
1734
  const x = Buffer.from(a, "utf8");
1314
1735
  const y = Buffer.from(b, "utf8");
1315
- return x.length === y.length && timingSafeEqual(x, y);
1736
+ return x.length === y.length && timingSafeEqual2(x, y);
1316
1737
  }
1317
1738
  function decodeJsonPart(s) {
1318
1739
  try {
@@ -1334,7 +1755,9 @@ export {
1334
1755
  ApiError,
1335
1756
  AuthApi,
1336
1757
  AuthenticationError,
1758
+ CITIZEN_ISSUING_CA_SKI,
1337
1759
  DEFAULT_BASE_URL,
1760
+ DEFAULT_PDF_TIMEOUT_MS,
1338
1761
  DeviceLinkError,
1339
1762
  EID_ACR_ADVANCED,
1340
1763
  EID_ACR_QUALIFIED,
@@ -1349,10 +1772,13 @@ export {
1349
1772
  ON_BEHALF_ERROR_CODES,
1350
1773
  OrganizationApi,
1351
1774
  PdfApi,
1775
+ PersonApi,
1776
+ RETRYABLE_ERROR_CODES,
1352
1777
  ResponseValidator,
1353
1778
  SessionApi,
1354
1779
  SessionFailedError,
1355
1780
  SignApi,
1781
+ TokenApi,
1356
1782
  ValidationError,
1357
1783
  acspV2Digest,
1358
1784
  buildAcspV2Payload,
@@ -1361,14 +1787,19 @@ export {
1361
1787
  interactionsToBase64,
1362
1788
  isMobileUserAgent,
1363
1789
  openOrShowQR,
1790
+ parseRetryAfter,
1364
1791
  parseSessionResult,
1365
1792
  parseSubject,
1793
+ personMatchesCertificate,
1366
1794
  pkceChallenge,
1367
1795
  qrDeviceLinkTicker,
1368
1796
  randomChallenge,
1369
1797
  resolveBaseUrl,
1370
1798
  sha256Base64,
1799
+ tokenAuthDigest,
1800
+ tokenLimitOf,
1371
1801
  userChallengeOf,
1372
1802
  verifyPayloadSignature,
1373
- verifyPrehashedSignature
1803
+ verifyPrehashedSignature,
1804
+ verifyTokenCertificates
1374
1805
  };