@gerege-systems/eid-mongolia-sdk 0.6.1 → 0.7.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/README.md CHANGED
@@ -67,6 +67,51 @@ const sig = eid.validator.validateSign(result, digest); // digest-ийн эср
67
67
  console.log(sig.signatureValueB64, sig.subject);
68
68
  ```
69
69
 
70
+ ## 3a. Байгууллагын нэрийн өмнөөс гарын үсэг (onBehalfOf)
71
+
72
+ Иргэн **өөрийн PIN2 гэрчилгээгээр** зурна; тухайн мөчид байгууллагыг төлөөлөх эрхтэйг eID **өөрийн бүртгэлээр**
73
+ шалгана (Эстонийн загвар). Энэ нь **хувь хүний квалификацтай гарын үсэг** — байгууллагын гэрчилгээ, тамга (e-Seal) биш.
74
+ e-Seal нь зөвхөн хүнгүй, автомат системийн баримтад. Урьдчилсан нөхцөл: байгууллага eID-д `ACTIVE` (захирал
75
+ аппаараа ХУР-аар холбоно), зурагч нь `ADMIN` эсвэл PIN2-оор баталгаажуулсан `MANAGER`, RP-д `SIGN` эрх.
76
+ Дэлгэрэнгүй: [developer.eidmongolia.mn](https://developer.eidmongolia.mn/).
77
+
78
+ ```ts
79
+ import { ForbiddenError, ApiError, ON_BEHALF_ERROR_CODES } from "@gerege-systems/eid-mongolia-sdk";
80
+
81
+ // 1. Иргэн сүүлийн 24 цагт танай RP-ээр нэвтэрсэн байх ёстой.
82
+ const { representations } = await eid.organization.getRepresentations("PNOMN-12345678");
83
+ // → [{ orgEtsi: "NTRMN-6235972", orgName: "Гэрэгэ Системс ХХК", role: "ceo", rightType: "ADMIN", … }]
84
+
85
+ // 2. PDF (PAdES, CA угсарна) — санал болгох урсгал.
86
+ try {
87
+ const s = await eid.pdf.prepare({
88
+ pdf: pdfBytes, fileName: "Гэрээ №12.pdf",
89
+ signer: { etsi: "PNOMN-12345678" }, flow: "notification",
90
+ onBehalfOf: "NTRMN-6235972",
91
+ });
92
+ showToUser(s.vc);
93
+ const r = await eid.session.waitForResult(s.sessionId);
94
+ // MUST: endResult OK, pdf.documentStatus READY, pdf.signer.onBehalfOf = хүссэн байгууллага,
95
+ // татсан файлын SHA-256 = pdf.outSha256, validation.indication "valid", ltv true.
96
+ if (r.pdf?.documentStatus !== "READY" || r.pdf.signer?.onBehalfOf !== "NTRMN-6235972") throw new Error("…");
97
+ const signed = await eid.pdf.document(s.sessionId);
98
+ console.log(r.pdf.signer.claimedRole); // "Гүйцэтгэх захирал, Гэрэгэ Системс ХХК (NTRMN-6235972)"
99
+ } catch (e) {
100
+ if ((e instanceof ForbiddenError || e instanceof ApiError) && e.code) {
101
+ // REPRESENTATION_DENIED | REPRESENTATION_PENDING | REPRESENTATION_EXPIRED | ORG_NOT_ACTIVE |
102
+ // SIGNER_UNIDENTIFIED (403) · ORG_NOT_FOUND (404) · REPRESENTATION_REVOKED (403, document татах үед)
103
+ }
104
+ throw e;
105
+ }
106
+
107
+ // Raw digest гарын үсэгт ч мөн: eid.sign.digestByEtsi(id, digest, interactions, { onBehalfOf: "NTRMN-6235972" })
108
+ // — CMS/PDF-ийг та угсардаг тул мэдүүлсэн үүргийг (signer-attributes-v2) өөрөө нэмнэ.
109
+ ```
110
+
111
+ Олон талт гэрээ: А байгууллагын гаралтыг өөрчлөлтгүй Б-ийн `prepare`-д (`docID`, өөр `onBehalfOf`) илгээнэ.
112
+ Session-ий `onBehalfOf` блок (`role`/`rightType`) нь хариу угсрах үеийн бүртгэл; `pdf.signer.onBehalfOf`/`claimedRole`
113
+ нь баримтад бичигдсэн утга.
114
+
70
115
  ## 4. QR ба ижил төхөөрөмж — device link v3
71
116
 
72
117
  Иргэн өөр төхөөрөмжийн дэлгэц дээрх **QR**-ийг утсаараа уншина, эсвэл RP-ийн сайт/аппыг eID апптай **ижил
@@ -172,10 +217,10 @@ OK ирж болзошгүй. Иймд `ResponseValidator`:
172
217
  | Алдаа | Утга |
173
218
  |---|---|
174
219
  | `AuthenticationError` | 401 — API secret буруу/байхгүй |
175
- | `ForbiddenError` | 403 — IP allowlist / mTLS зөвшөөрөөгүй |
220
+ | `ForbiddenError` | 403 — IP allowlist / mTLS / RP-ийн эрх; onBehalfOf татгалзвал `.code` (`OnBehalfErrorCode`) |
176
221
  | `SessionFailedError` | endResult ≠ OK (TIMEOUT, USER_REFUSED…) — `.endResult`-оор салга |
177
222
  | `ValidationError` | cert chain/signature/level шалгалт бүтэлгүйтсэн |
178
- | `ApiError` | бусад HTTP алдаа (`.status`, `.body`) |
223
+ | `ApiError` | бусад HTTP алдаа (`.status`, `.body`, серверийн `.code` — ж: 404 `ORG_NOT_FOUND`) |
179
224
  | `NetworkError` | timeout/сүлжээ |
180
225
  | `DeviceLinkError` | device-link холбоос угсрах боломжгүй (`deviceLinkBase` https биш, token/secret/lang хэлбэр, төрөлд хэрэгтэй талбар дутуу) |
181
226
 
@@ -130,6 +130,61 @@ interface SessionResult {
130
130
  * RP энэ талбарыг л итгэж уншина (клиентийн сонголтод биш). docs/ORG_LOGIN.md.
131
131
  */
132
132
  onBehalfOf: OrgResult | null;
133
+ /**
134
+ * PAdES (`POST /pdf/sign/prepare`) session-ий `pdf` блок — PDF урсгал биш бол null (талбар алга = хуучин SDK-аар
135
+ * угсарсан объект). {@link PdfSessionBlock}.
136
+ */
137
+ pdf?: PdfSessionBlock | null;
138
+ }
139
+ /** PDF баримтын төлөв: PENDING → READY (эсвэл FAILED / EXPIRED). */
140
+ type PdfDocumentStatus = "PENDING" | "READY" | "FAILED" | "EXPIRED";
141
+ /** PAdES гаралтын гарын үсэг зурагч. */
142
+ interface PdfSigner {
143
+ etsi: string;
144
+ certSerial: string;
145
+ /** Байгууллагын нэрийн өмнөөс бол байгууллагын ETSI (`NTRMN-…`) — баримтад бичигдсэн утга. */
146
+ onBehalfOf?: string;
147
+ /** CMS signer-attributes-v2-д бичигдсэн мэдүүлсэн үүрэг: «<албан тушаал>, <нэр> (NTRMN-…)». */
148
+ claimedRole?: string;
149
+ }
150
+ /** GET /session/{id}-ийн `pdf` объект (PAdES, CA угсарна). */
151
+ interface PdfSessionBlock {
152
+ docID: string;
153
+ documentStatus: PdfDocumentStatus | string;
154
+ /** FAILED үед — ж: `REPRESENTATION_REVOKED` (prepare-ээс хойш эрх хасагдсан, баримт үүсээгүй). */
155
+ errorCode?: string;
156
+ /** Гарын үсэгтэй PDF-ийн SHA-256 (hex) — татсан файлтай тулгана. */
157
+ outSha256?: string;
158
+ size?: number;
159
+ expiresAt?: string;
160
+ signatureLevel?: string;
161
+ signer?: PdfSigner;
162
+ validation?: {
163
+ indication: string;
164
+ ltv: boolean;
165
+ signatures: number;
166
+ };
167
+ }
168
+ /** Иргэний төлөөлж чадах нэг байгууллага (GET /organization/representations/etsi/{personEtsi}). */
169
+ interface Representation {
170
+ /** `NTRMN-<бүртгэл>` — `onBehalfOf`-д өгөх утга. */
171
+ orgEtsi: string;
172
+ orgRegister: string;
173
+ orgName: string;
174
+ orgNameEn?: string;
175
+ /** Бүртгэлийн албан тушаал (ХУР-аас холбогдсонд `ceo` / `founder`, MANAGER-т ADMIN-ий бичсэн текст). */
176
+ role?: string;
177
+ rightType: "ADMIN" | "MANAGER" | string;
178
+ source: "REGISTRY" | "MANUAL" | string;
179
+ /** ISO 8601. */
180
+ validFrom: string;
181
+ /** ISO 8601; байхгүй = хугацаагүй. */
182
+ validTo?: string;
183
+ }
184
+ /** GET /organization/representations/etsi/{personEtsi} хариу. */
185
+ interface RepresentationsResponse {
186
+ personEtsi: string;
187
+ representations: Representation[];
133
188
  }
134
189
  /** Session-ий байгууллагын блок — байгууллага + тухайн иргэний эрх (бүртгэлээс real-time). */
135
190
  interface OrgResult {
@@ -206,15 +261,37 @@ declare class EidError extends Error {
206
261
  /** HTTP 401 — API secret буруу/байхгүй. Тохиргооны (RP secret) алдаа. */
207
262
  declare class AuthenticationError extends EidError {
208
263
  }
209
- /** HTTP 403 — IP allowlist/mTLS зөвшөөрөөгүй. Танай серверийн IP бүртгэгдээгүй байж магадгүй. */
264
+ /**
265
+ * HTTP 403 — IP allowlist/mTLS зөвшөөрөөгүй, RP-д эрх алга, эсвэл onBehalfOf-ийн төлөөлөл татгалзсан.
266
+ * Сервер `{"error","code"}` буцаасан бол `code` (ж: `REPRESENTATION_DENIED`) — салааллыг үүгээр хийнэ.
267
+ */
210
268
  declare class ForbiddenError extends EidError {
269
+ /** Серверийн тогтвортой алдааны код (`{"code"}`), байхгүй бол undefined. */
270
+ readonly code?: string | undefined;
271
+ constructor(message: string,
272
+ /** Серверийн тогтвортой алдааны код (`{"code"}`), байхгүй бол undefined. */
273
+ code?: string | undefined);
211
274
  }
212
275
  /** RP-API-аас ирсэн бусад HTTP алдаа (4xx/5xx). status ба хариуны биеийг агуулна. */
213
276
  declare class ApiError extends EidError {
214
277
  readonly status: number;
215
278
  readonly body: string;
216
- constructor(status: number, body: string);
279
+ /** Серверийн тогтвортой алдааны код (`{"error","code"}`-ийн `code`), байхгүй бол undefined. */
280
+ readonly code?: string | undefined;
281
+ constructor(status: number, body: string,
282
+ /** Серверийн тогтвортой алдааны код (`{"error","code"}`-ийн `code`), байхгүй бол undefined. */
283
+ code?: string | undefined);
217
284
  }
285
+ /**
286
+ * Байгууллагын нэрийн өмнөөс (`onBehalfOf`) урсгалын татгалзлын кодууд — `ForbiddenError.code` /
287
+ * `ApiError.code`-д ирнэ. 403: REPRESENTATION_DENIED (идэвхтэй төлөөлөгч биш), REPRESENTATION_PENDING (PIN2-оор
288
+ * баталгаажуулаагүй), REPRESENTATION_EXPIRED (хугацааны гадна), ORG_NOT_ACTIVE, SIGNER_UNIDENTIFIED (anonymous
289
+ * session); 404: ORG_NOT_FOUND; PDF баримт татахад 403 REPRESENTATION_REVOKED (prepare-ээс хойш эрх хасагдсан —
290
+ * баримт үүсээгүй).
291
+ */
292
+ type OnBehalfErrorCode = "REPRESENTATION_DENIED" | "REPRESENTATION_PENDING" | "REPRESENTATION_EXPIRED" | "ORG_NOT_ACTIVE" | "SIGNER_UNIDENTIFIED" | "ORG_NOT_FOUND" | "REPRESENTATION_REVOKED";
293
+ /** {@link OnBehalfErrorCode}-ийн бүх утга (runtime шалгалтад). */
294
+ declare const ON_BEHALF_ERROR_CODES: readonly OnBehalfErrorCode[];
218
295
  /** Сүлжээ/timeout — upstream хариу өгсөнгүй. */
219
296
  declare class NetworkError extends EidError {
220
297
  }
@@ -240,4 +317,4 @@ declare class ValidationError extends EidError {
240
317
  declare class DeviceLinkError extends EidError {
241
318
  }
242
319
 
243
- export { type AcspContext as A, type CertificateLevel as C, type DeviceLinkSession as D, EidError as E, type FlowType as F, type HashAlgorithm as H, type Interaction as I, type NotificationSession as N, type OpenOrShowQROptions as O, type RpCredentials as R, type SignatureAlgorithm as S, ValidationError as V, type SessionResult as a, ApiError as b, AuthenticationError as c, DeviceLinkError as d, type DeviceLinkInput as e, type EndResult as f, ForbiddenError as g, type NavigatorLike as h, NetworkError as i, SessionFailedError as j, type SessionState as k, type SignatureAlgorithmParameters as l, type SignatureProtocol as m, deviceLink as n, isMobileUserAgent as o, openOrShowQR as p };
320
+ export { type AcspContext as A, type CertificateLevel as C, type DeviceLinkSession as D, EidError as E, type FlowType as F, type HashAlgorithm as H, type Interaction as I, type NotificationSession as N, ON_BEHALF_ERROR_CODES as O, type PdfDocumentStatus as P, type RpCredentials as R, type SignatureAlgorithm as S, ValidationError as V, type SessionResult as a, type RepresentationsResponse as b, ApiError as c, AuthenticationError as d, DeviceLinkError as e, type DeviceLinkInput as f, type EndResult as g, ForbiddenError as h, type NavigatorLike as i, NetworkError as j, type OnBehalfErrorCode as k, type OpenOrShowQROptions as l, type OrgResult as m, type PdfSessionBlock as n, type PdfSigner as o, type Representation as p, SessionFailedError as q, type SessionState as r, type SignatureAlgorithmParameters as s, type SignatureProtocol as t, deviceLink as u, isMobileUserAgent as v, openOrShowQR as w };
@@ -130,6 +130,61 @@ interface SessionResult {
130
130
  * RP энэ талбарыг л итгэж уншина (клиентийн сонголтод биш). docs/ORG_LOGIN.md.
131
131
  */
132
132
  onBehalfOf: OrgResult | null;
133
+ /**
134
+ * PAdES (`POST /pdf/sign/prepare`) session-ий `pdf` блок — PDF урсгал биш бол null (талбар алга = хуучин SDK-аар
135
+ * угсарсан объект). {@link PdfSessionBlock}.
136
+ */
137
+ pdf?: PdfSessionBlock | null;
138
+ }
139
+ /** PDF баримтын төлөв: PENDING → READY (эсвэл FAILED / EXPIRED). */
140
+ type PdfDocumentStatus = "PENDING" | "READY" | "FAILED" | "EXPIRED";
141
+ /** PAdES гаралтын гарын үсэг зурагч. */
142
+ interface PdfSigner {
143
+ etsi: string;
144
+ certSerial: string;
145
+ /** Байгууллагын нэрийн өмнөөс бол байгууллагын ETSI (`NTRMN-…`) — баримтад бичигдсэн утга. */
146
+ onBehalfOf?: string;
147
+ /** CMS signer-attributes-v2-д бичигдсэн мэдүүлсэн үүрэг: «<албан тушаал>, <нэр> (NTRMN-…)». */
148
+ claimedRole?: string;
149
+ }
150
+ /** GET /session/{id}-ийн `pdf` объект (PAdES, CA угсарна). */
151
+ interface PdfSessionBlock {
152
+ docID: string;
153
+ documentStatus: PdfDocumentStatus | string;
154
+ /** FAILED үед — ж: `REPRESENTATION_REVOKED` (prepare-ээс хойш эрх хасагдсан, баримт үүсээгүй). */
155
+ errorCode?: string;
156
+ /** Гарын үсэгтэй PDF-ийн SHA-256 (hex) — татсан файлтай тулгана. */
157
+ outSha256?: string;
158
+ size?: number;
159
+ expiresAt?: string;
160
+ signatureLevel?: string;
161
+ signer?: PdfSigner;
162
+ validation?: {
163
+ indication: string;
164
+ ltv: boolean;
165
+ signatures: number;
166
+ };
167
+ }
168
+ /** Иргэний төлөөлж чадах нэг байгууллага (GET /organization/representations/etsi/{personEtsi}). */
169
+ interface Representation {
170
+ /** `NTRMN-<бүртгэл>` — `onBehalfOf`-д өгөх утга. */
171
+ orgEtsi: string;
172
+ orgRegister: string;
173
+ orgName: string;
174
+ orgNameEn?: string;
175
+ /** Бүртгэлийн албан тушаал (ХУР-аас холбогдсонд `ceo` / `founder`, MANAGER-т ADMIN-ий бичсэн текст). */
176
+ role?: string;
177
+ rightType: "ADMIN" | "MANAGER" | string;
178
+ source: "REGISTRY" | "MANUAL" | string;
179
+ /** ISO 8601. */
180
+ validFrom: string;
181
+ /** ISO 8601; байхгүй = хугацаагүй. */
182
+ validTo?: string;
183
+ }
184
+ /** GET /organization/representations/etsi/{personEtsi} хариу. */
185
+ interface RepresentationsResponse {
186
+ personEtsi: string;
187
+ representations: Representation[];
133
188
  }
134
189
  /** Session-ий байгууллагын блок — байгууллага + тухайн иргэний эрх (бүртгэлээс real-time). */
135
190
  interface OrgResult {
@@ -206,15 +261,37 @@ declare class EidError extends Error {
206
261
  /** HTTP 401 — API secret буруу/байхгүй. Тохиргооны (RP secret) алдаа. */
207
262
  declare class AuthenticationError extends EidError {
208
263
  }
209
- /** HTTP 403 — IP allowlist/mTLS зөвшөөрөөгүй. Танай серверийн IP бүртгэгдээгүй байж магадгүй. */
264
+ /**
265
+ * HTTP 403 — IP allowlist/mTLS зөвшөөрөөгүй, RP-д эрх алга, эсвэл onBehalfOf-ийн төлөөлөл татгалзсан.
266
+ * Сервер `{"error","code"}` буцаасан бол `code` (ж: `REPRESENTATION_DENIED`) — салааллыг үүгээр хийнэ.
267
+ */
210
268
  declare class ForbiddenError extends EidError {
269
+ /** Серверийн тогтвортой алдааны код (`{"code"}`), байхгүй бол undefined. */
270
+ readonly code?: string | undefined;
271
+ constructor(message: string,
272
+ /** Серверийн тогтвортой алдааны код (`{"code"}`), байхгүй бол undefined. */
273
+ code?: string | undefined);
211
274
  }
212
275
  /** RP-API-аас ирсэн бусад HTTP алдаа (4xx/5xx). status ба хариуны биеийг агуулна. */
213
276
  declare class ApiError extends EidError {
214
277
  readonly status: number;
215
278
  readonly body: string;
216
- constructor(status: number, body: string);
279
+ /** Серверийн тогтвортой алдааны код (`{"error","code"}`-ийн `code`), байхгүй бол undefined. */
280
+ readonly code?: string | undefined;
281
+ constructor(status: number, body: string,
282
+ /** Серверийн тогтвортой алдааны код (`{"error","code"}`-ийн `code`), байхгүй бол undefined. */
283
+ code?: string | undefined);
217
284
  }
285
+ /**
286
+ * Байгууллагын нэрийн өмнөөс (`onBehalfOf`) урсгалын татгалзлын кодууд — `ForbiddenError.code` /
287
+ * `ApiError.code`-д ирнэ. 403: REPRESENTATION_DENIED (идэвхтэй төлөөлөгч биш), REPRESENTATION_PENDING (PIN2-оор
288
+ * баталгаажуулаагүй), REPRESENTATION_EXPIRED (хугацааны гадна), ORG_NOT_ACTIVE, SIGNER_UNIDENTIFIED (anonymous
289
+ * session); 404: ORG_NOT_FOUND; PDF баримт татахад 403 REPRESENTATION_REVOKED (prepare-ээс хойш эрх хасагдсан —
290
+ * баримт үүсээгүй).
291
+ */
292
+ type OnBehalfErrorCode = "REPRESENTATION_DENIED" | "REPRESENTATION_PENDING" | "REPRESENTATION_EXPIRED" | "ORG_NOT_ACTIVE" | "SIGNER_UNIDENTIFIED" | "ORG_NOT_FOUND" | "REPRESENTATION_REVOKED";
293
+ /** {@link OnBehalfErrorCode}-ийн бүх утга (runtime шалгалтад). */
294
+ declare const ON_BEHALF_ERROR_CODES: readonly OnBehalfErrorCode[];
218
295
  /** Сүлжээ/timeout — upstream хариу өгсөнгүй. */
219
296
  declare class NetworkError extends EidError {
220
297
  }
@@ -240,4 +317,4 @@ declare class ValidationError extends EidError {
240
317
  declare class DeviceLinkError extends EidError {
241
318
  }
242
319
 
243
- export { type AcspContext as A, type CertificateLevel as C, type DeviceLinkSession as D, EidError as E, type FlowType as F, type HashAlgorithm as H, type Interaction as I, type NotificationSession as N, type OpenOrShowQROptions as O, type RpCredentials as R, type SignatureAlgorithm as S, ValidationError as V, type SessionResult as a, ApiError as b, AuthenticationError as c, DeviceLinkError as d, type DeviceLinkInput as e, type EndResult as f, ForbiddenError as g, type NavigatorLike as h, NetworkError as i, SessionFailedError as j, type SessionState as k, type SignatureAlgorithmParameters as l, type SignatureProtocol as m, deviceLink as n, isMobileUserAgent as o, openOrShowQR as p };
320
+ export { type AcspContext as A, type CertificateLevel as C, type DeviceLinkSession as D, EidError as E, type FlowType as F, type HashAlgorithm as H, type Interaction as I, type NotificationSession as N, ON_BEHALF_ERROR_CODES as O, type PdfDocumentStatus as P, type RpCredentials as R, type SignatureAlgorithm as S, ValidationError as V, type SessionResult as a, type RepresentationsResponse as b, ApiError as c, AuthenticationError as d, DeviceLinkError as e, type DeviceLinkInput as f, type EndResult as g, ForbiddenError as h, type NavigatorLike as i, NetworkError as j, type OnBehalfErrorCode as k, type OpenOrShowQROptions as l, type OrgResult as m, type PdfSessionBlock as n, type PdfSigner as o, type Representation as p, SessionFailedError as q, type SessionState as r, type SignatureAlgorithmParameters as s, type SignatureProtocol as t, deviceLink as u, isMobileUserAgent as v, openOrShowQR as w };
@@ -1 +1 @@
1
- export { d as DeviceLinkError, e as DeviceLinkInput, D as DeviceLinkSession, E as EidError, h as NavigatorLike, O as OpenOrShowQROptions, n as deviceLink, o as isMobileUserAgent, p as openOrShowQR } from './browser-BU9ShPWO.cjs';
1
+ export { e as DeviceLinkError, f as DeviceLinkInput, D as DeviceLinkSession, E as EidError, i as NavigatorLike, l as OpenOrShowQROptions, u as deviceLink, v as isMobileUserAgent, w as openOrShowQR } from './browser-Db82DlYN.cjs';
package/dist/browser.d.ts CHANGED
@@ -1 +1 @@
1
- export { d as DeviceLinkError, e as DeviceLinkInput, D as DeviceLinkSession, E as EidError, h as NavigatorLike, O as OpenOrShowQROptions, n as deviceLink, o as isMobileUserAgent, p as openOrShowQR } from './browser-BU9ShPWO.js';
1
+ export { e as DeviceLinkError, f as DeviceLinkInput, D as DeviceLinkSession, E as EidError, i as NavigatorLike, l as OpenOrShowQROptions, u as deviceLink, v as isMobileUserAgent, w as openOrShowQR } from './browser-Db82DlYN.js';
package/dist/browser.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  deviceLink,
5
5
  isMobileUserAgent,
6
6
  openOrShowQR
7
- } from "./chunk-Q5TQMCJC.js";
7
+ } from "./chunk-NXDKQZIW.js";
8
8
  export {
9
9
  DeviceLinkError,
10
10
  EidError,
@@ -8,16 +8,32 @@ var EidError = class extends Error {
8
8
  var AuthenticationError = class extends EidError {
9
9
  };
10
10
  var ForbiddenError = class extends EidError {
11
+ constructor(message, code) {
12
+ super(message);
13
+ this.code = code;
14
+ }
15
+ code;
11
16
  };
12
17
  var ApiError = class extends EidError {
13
- constructor(status, body) {
18
+ constructor(status, body, code) {
14
19
  super(`RP-API ${status}: ${body}`);
15
20
  this.status = status;
16
21
  this.body = body;
22
+ this.code = code;
17
23
  }
18
24
  status;
19
25
  body;
26
+ code;
20
27
  };
28
+ var ON_BEHALF_ERROR_CODES = [
29
+ "REPRESENTATION_DENIED",
30
+ "REPRESENTATION_PENDING",
31
+ "REPRESENTATION_EXPIRED",
32
+ "ORG_NOT_ACTIVE",
33
+ "SIGNER_UNIDENTIFIED",
34
+ "ORG_NOT_FOUND",
35
+ "REPRESENTATION_REVOKED"
36
+ ];
21
37
  var NetworkError = class extends EidError {
22
38
  };
23
39
  var SessionFailedError = class extends EidError {
@@ -79,6 +95,7 @@ export {
79
95
  AuthenticationError,
80
96
  ForbiddenError,
81
97
  ApiError,
98
+ ON_BEHALF_ERROR_CODES,
82
99
  NetworkError,
83
100
  SessionFailedError,
84
101
  ValidationError,
package/dist/index.cjs CHANGED
@@ -30,6 +30,9 @@ __export(index_exports, {
30
30
  ForbiddenError: () => ForbiddenError,
31
31
  Http: () => Http,
32
32
  NetworkError: () => NetworkError,
33
+ ON_BEHALF_ERROR_CODES: () => ON_BEHALF_ERROR_CODES,
34
+ OrganizationApi: () => OrganizationApi,
35
+ PdfApi: () => PdfApi,
33
36
  ResponseValidator: () => ResponseValidator,
34
37
  SessionApi: () => SessionApi,
35
38
  SessionFailedError: () => SessionFailedError,
@@ -63,16 +66,32 @@ var EidError = class extends Error {
63
66
  var AuthenticationError = class extends EidError {
64
67
  };
65
68
  var ForbiddenError = class extends EidError {
69
+ constructor(message, code) {
70
+ super(message);
71
+ this.code = code;
72
+ }
73
+ code;
66
74
  };
67
75
  var ApiError = class extends EidError {
68
- constructor(status, body) {
76
+ constructor(status, body, code) {
69
77
  super(`RP-API ${status}: ${body}`);
70
78
  this.status = status;
71
79
  this.body = body;
80
+ this.code = code;
72
81
  }
73
82
  status;
74
83
  body;
84
+ code;
75
85
  };
86
+ var ON_BEHALF_ERROR_CODES = [
87
+ "REPRESENTATION_DENIED",
88
+ "REPRESENTATION_PENDING",
89
+ "REPRESENTATION_EXPIRED",
90
+ "ORG_NOT_ACTIVE",
91
+ "SIGNER_UNIDENTIFIED",
92
+ "ORG_NOT_FOUND",
93
+ "REPRESENTATION_REVOKED"
94
+ ];
76
95
  var NetworkError = class extends EidError {
77
96
  };
78
97
  var SessionFailedError = class extends EidError {
@@ -95,21 +114,35 @@ var Http = class {
95
114
  }
96
115
  cfg;
97
116
  post(path, body, timeoutMs) {
98
- return this.request("POST", path, body, timeoutMs);
117
+ return this.json("POST", path, JSON.stringify(body), "application/json", timeoutMs);
99
118
  }
100
119
  get(path, timeoutMs) {
101
- return this.request("GET", path, void 0, timeoutMs);
120
+ return this.json("GET", path, void 0, void 0, timeoutMs);
121
+ }
122
+ /** multipart/form-data POST (Content-Type-ийг boundary-тай нь fetch тавина). JSON хариу. */
123
+ postMultipart(path, form, timeoutMs) {
124
+ return this.json("POST", path, form, void 0, timeoutMs);
125
+ }
126
+ /** GET — хариуг байтаар (ж: гарын үсэгтэй PDF). */
127
+ async getBytes(path, timeoutMs) {
128
+ const res = await this.send("GET", path, void 0, void 0, timeoutMs);
129
+ return new Uint8Array(await res.arrayBuffer());
130
+ }
131
+ async json(method, path, body, contentType, timeoutMs) {
132
+ const res = await this.send(method, path, body, contentType, timeoutMs);
133
+ const text = await res.text();
134
+ return text ? JSON.parse(text) : {};
102
135
  }
103
- async request(method, path, body, timeoutMs) {
136
+ async send(method, path, body, contentType, timeoutMs) {
104
137
  const f = this.cfg.fetchImpl ?? fetch;
105
138
  const url = this.cfg.baseUrl + path;
106
139
  const init = {
107
140
  method,
108
141
  headers: {
109
142
  Authorization: `Bearer ${this.cfg.apiSecret}`,
110
- ...body !== void 0 ? { "Content-Type": "application/json" } : {}
143
+ ...contentType ? { "Content-Type": contentType } : {}
111
144
  },
112
- body: body !== void 0 ? JSON.stringify(body) : void 0,
145
+ body,
113
146
  signal: AbortSignal.timeout(timeoutMs ?? this.cfg.timeoutMs ?? DEFAULT_TIMEOUT_MS)
114
147
  };
115
148
  if (this.cfg.dispatcher) init.dispatcher = this.cfg.dispatcher;
@@ -123,13 +156,32 @@ var Http = class {
123
156
  }
124
157
  throw new NetworkError(`\u0421\u04AF\u043B\u0436\u044D\u044D\u043D\u0438\u0439 \u0430\u043B\u0434\u0430\u0430 (${method} ${path}): ${err.message}`);
125
158
  }
159
+ if (res.status < 300) return res;
126
160
  const text = await res.text();
161
+ const { error, code } = errorBody(text);
127
162
  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)");
128
- if (res.status === 403) throw new ForbiddenError("IP allowlist \u044D\u0441\u0432\u044D\u043B mTLS \u0437\u04E9\u0432\u0448\u04E9\u04E9\u0440\u04E9\u04E9\u0433\u04AF\u0439 (403)");
129
- if (res.status >= 300) throw new ApiError(res.status, text);
130
- return text ? JSON.parse(text) : {};
163
+ if (res.status === 403) {
164
+ throw new ForbiddenError(
165
+ 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)",
166
+ code
167
+ );
168
+ }
169
+ throw new ApiError(res.status, text, code);
131
170
  }
132
171
  };
172
+ function errorBody(text) {
173
+ try {
174
+ const v = JSON.parse(text);
175
+ if (!v || typeof v !== "object") return {};
176
+ const o = v;
177
+ return {
178
+ error: typeof o.error === "string" && o.error ? o.error : void 0,
179
+ code: typeof o.code === "string" && o.code ? o.code : void 0
180
+ };
181
+ } catch {
182
+ return {};
183
+ }
184
+ }
133
185
 
134
186
  // src/crypto.ts
135
187
  var import_node_crypto = require("crypto");
@@ -341,9 +393,32 @@ function parseSessionResult(raw) {
341
393
  initialCallbackUrl: typeof sig.initialCallbackUrl === "string" ? sig.initialCallbackUrl : null,
342
394
  signatureAlgorithmParameters: parseSigParams(sig.signatureAlgorithmParameters),
343
395
  interactionTypeUsed: str(r.interactionTypeUsed),
344
- onBehalfOf: parseOrg(r.onBehalfOf)
396
+ onBehalfOf: parseOrg(r.onBehalfOf),
397
+ pdf: parsePdf(r.pdf)
345
398
  };
346
399
  }
400
+ function parsePdf(raw) {
401
+ const p = asRecord(raw);
402
+ const docID = str(p.docID);
403
+ if (!docID) return null;
404
+ const out = { docID, documentStatus: String(p.documentStatus ?? "") };
405
+ if (p.errorCode != null) out.errorCode = String(p.errorCode);
406
+ if (p.outSha256 != null) out.outSha256 = String(p.outSha256);
407
+ if (p.size != null) out.size = Number(p.size);
408
+ if (p.expiresAt != null) out.expiresAt = String(p.expiresAt);
409
+ if (p.signatureLevel != null) out.signatureLevel = String(p.signatureLevel);
410
+ const sg = asRecord(p.signer);
411
+ if (sg.etsi != null) {
412
+ out.signer = { etsi: String(sg.etsi), certSerial: String(sg.certSerial ?? "") };
413
+ if (sg.onBehalfOf) out.signer.onBehalfOf = String(sg.onBehalfOf);
414
+ if (sg.claimedRole) out.signer.claimedRole = String(sg.claimedRole);
415
+ }
416
+ const v = asRecord(p.validation);
417
+ if (v.indication != null) {
418
+ out.validation = { indication: String(v.indication), ltv: v.ltv === true, signatures: Number(v.signatures ?? 0) };
419
+ }
420
+ return out;
421
+ }
347
422
  function parseSigParams(raw) {
348
423
  const p = asRecord(raw);
349
424
  const hash = str(p.hashAlgorithm);
@@ -398,6 +473,76 @@ var SessionApi = class {
398
473
  }
399
474
  };
400
475
 
476
+ // src/pdf.ts
477
+ function rec3(v) {
478
+ return v && typeof v === "object" ? v : {};
479
+ }
480
+ var PdfApi = class {
481
+ constructor(http, creds, defaults) {
482
+ this.http = http;
483
+ this.creds = creds;
484
+ this.defaults = defaults;
485
+ }
486
+ http;
487
+ creds;
488
+ defaults;
489
+ /** POST /pdf/sign/prepare — multipart (`request` JSON + `pdf`). SIGN эрх шаардана. */
490
+ async prepare(input) {
491
+ const request = {
492
+ relyingPartyUUID: this.creds.rpUUID,
493
+ relyingPartyName: this.creds.rpName,
494
+ certificateLevel: input.certificateLevel ?? this.defaults.certificateLevel,
495
+ signer: input.signer,
496
+ flow: input.flow ?? "notification",
497
+ fileName: input.fileName
498
+ };
499
+ if (input.initialCallbackUrl?.trim()) request.initialCallbackUrl = input.initialCallbackUrl.trim();
500
+ if (input.docID?.trim()) request.docID = input.docID.trim();
501
+ if (input.onBehalfOf?.trim()) request.onBehalfOf = input.onBehalfOf.trim();
502
+ const form = new FormData();
503
+ form.append("request", new Blob([JSON.stringify(request)], { type: "application/json" }));
504
+ form.append("pdf", new Blob([input.pdf], { type: "application/pdf" }), input.fileName);
505
+ const r = rec3(await this.http.postMultipart("/pdf/sign/prepare", form, 6e4));
506
+ const receivedAt = Date.now();
507
+ const vc = rec3(r.vc).value;
508
+ const dl = rec3(r.deviceLink);
509
+ return {
510
+ sessionId: String(r.sessionID),
511
+ docId: String(r.docID),
512
+ vc: vc != null ? String(vc) : null,
513
+ deviceLink: dl.sessionToken ? {
514
+ sessionToken: String(dl.sessionToken),
515
+ sessionSecret: String(dl.sessionSecret),
516
+ deviceLinkBase: String(dl.deviceLinkBase),
517
+ digest: String(dl.digest),
518
+ interactions: String(dl.interactions)
519
+ } : null,
520
+ receivedAt
521
+ };
522
+ }
523
+ /**
524
+ * GET /pdf/sign/{sessionID}/document — гарын үсэгтэй PDF (READY-ээс хойш 30 мин). Татсан байтын SHA-256-ийг
525
+ * session-ий `pdf.outSha256`-тай тулга. Эрх хасагдсан бол `ForbiddenError` `.code = "REPRESENTATION_REVOKED"`.
526
+ */
527
+ document(sessionId) {
528
+ return this.http.getBytes(`/pdf/sign/${encodeURIComponent(sessionId)}/document`, 6e4);
529
+ }
530
+ };
531
+
532
+ // src/organization.ts
533
+ var OrganizationApi = class {
534
+ constructor(http) {
535
+ this.http = http;
536
+ }
537
+ http;
538
+ /** GET /organization/representations/etsi/{personEtsi}. Сонголт нь итгэх эх биш — сервер дахин шалгана. */
539
+ async getRepresentations(personEtsi) {
540
+ const raw = await this.http.get(`/organization/representations/etsi/${encodeURIComponent(personEtsi)}`);
541
+ const list = Array.isArray(raw.representations) ? raw.representations : [];
542
+ return { personEtsi: String(raw.personEtsi ?? personEtsi), representations: list };
543
+ }
544
+ };
545
+
401
546
  // src/validator.ts
402
547
  var import_node_crypto2 = require("crypto");
403
548
  var ResponseValidator = class {
@@ -752,6 +897,8 @@ function resolveBaseUrl(baseUrl) {
752
897
  var EidClient = class {
753
898
  auth;
754
899
  sign;
900
+ pdf;
901
+ organization;
755
902
  session;
756
903
  validator;
757
904
  constructor(cfg) {
@@ -767,6 +914,8 @@ var EidClient = class {
767
914
  const level = cfg.defaultCertificateLevel ?? "QUALIFIED";
768
915
  this.auth = new AuthApi(http, cfg.credentials, { certificateLevel: level });
769
916
  this.sign = new SignApi(http, cfg.credentials, { certificateLevel: level });
917
+ this.pdf = new PdfApi(http, cfg.credentials, { certificateLevel: level });
918
+ this.organization = new OrganizationApi(http);
770
919
  this.session = new SessionApi(http);
771
920
  this.validator = new ResponseValidator(cfg.trust ?? {});
772
921
  }
@@ -895,6 +1044,9 @@ function qrDeviceLinkTicker(input, onLink, opts) {
895
1044
  ForbiddenError,
896
1045
  Http,
897
1046
  NetworkError,
1047
+ ON_BEHALF_ERROR_CODES,
1048
+ OrganizationApi,
1049
+ PdfApi,
898
1050
  ResponseValidator,
899
1051
  SessionApi,
900
1052
  SessionFailedError,
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as RpCredentials, C as CertificateLevel, I as Interaction, S as SignatureAlgorithm, H as HashAlgorithm, N as NotificationSession, D as DeviceLinkSession, a as SessionResult, A as AcspContext, F as FlowType } from './browser-BU9ShPWO.cjs';
2
- export { b as ApiError, c as AuthenticationError, d as DeviceLinkError, e as DeviceLinkInput, E as EidError, f as EndResult, g as ForbiddenError, h as NavigatorLike, i as NetworkError, O as OpenOrShowQROptions, j as SessionFailedError, k as SessionState, l as SignatureAlgorithmParameters, m as SignatureProtocol, V as ValidationError, n as deviceLink, o as isMobileUserAgent, p as openOrShowQR } from './browser-BU9ShPWO.cjs';
1
+ import { R as RpCredentials, C as CertificateLevel, I as Interaction, S as SignatureAlgorithm, H as HashAlgorithm, N as NotificationSession, D as DeviceLinkSession, a as SessionResult, b as RepresentationsResponse, A as AcspContext, F as FlowType } from './browser-Db82DlYN.cjs';
2
+ export { c as ApiError, d as AuthenticationError, e as DeviceLinkError, f as DeviceLinkInput, E as EidError, g as EndResult, h as ForbiddenError, i as NavigatorLike, j as NetworkError, O as ON_BEHALF_ERROR_CODES, k as OnBehalfErrorCode, l as OpenOrShowQROptions, m as OrgResult, P as PdfDocumentStatus, n as PdfSessionBlock, o as PdfSigner, p as Representation, q as SessionFailedError, r as SessionState, s as SignatureAlgorithmParameters, t as SignatureProtocol, V as ValidationError, u as deviceLink, v as isMobileUserAgent, w as openOrShowQR } from './browser-Db82DlYN.cjs';
3
3
  import { X509Certificate, KeyObject } from 'node:crypto';
4
4
 
5
5
  interface HttpConfig {
@@ -23,7 +23,12 @@ declare class Http {
23
23
  constructor(cfg: HttpConfig);
24
24
  post(path: string, body: unknown, timeoutMs?: number): Promise<unknown>;
25
25
  get(path: string, timeoutMs?: number): Promise<unknown>;
26
- private request;
26
+ /** multipart/form-data POST (Content-Type-ийг boundary-тай нь fetch тавина). JSON хариу. */
27
+ postMultipart(path: string, form: FormData, timeoutMs?: number): Promise<unknown>;
28
+ /** GET — хариуг байтаар (ж: гарын үсэгтэй PDF). */
29
+ getBytes(path: string, timeoutMs?: number): Promise<Uint8Array>;
30
+ private json;
31
+ private send;
27
32
  }
28
33
 
29
34
  interface AuthDefaults {
@@ -131,6 +136,73 @@ declare class SessionApi {
131
136
  }): Promise<SessionResult>;
132
137
  }
133
138
 
139
+ interface PdfDefaults {
140
+ certificateLevel: CertificateLevel;
141
+ }
142
+ /** Гарын үсэг зурагч — яг нэг нь. */
143
+ type PdfSignerRef = {
144
+ etsi: string;
145
+ } | {
146
+ documentNumber: string;
147
+ } | {
148
+ certificateChoiceSessionId: string;
149
+ };
150
+ interface PdfPrepareInput {
151
+ /** Эх PDF (≤ 10 223 616 байт — `/.well-known/eid` `pdfSigning.maxInputBytes`). */
152
+ pdf: Uint8Array;
153
+ /** Баримтын нэр (≤ 120 тэмдэгт) — /verify хуудас, иргэний түүхэнд харагдана. */
154
+ fileName: string;
155
+ signer: PdfSignerRef;
156
+ /** `notification` (push) эсвэл `device-link` (QR/Web2App/App2App). Анхдагч notification. */
157
+ flow?: "notification" | "device-link";
158
+ certificateLevel?: CertificateLevel;
159
+ /** device-link Web2App/App2App буцах URL. */
160
+ initialCallbackUrl?: string;
161
+ /** Олон гарын үсэгтэй баримтад хүлээж буй docID (заавал биш — CA өөрөө олно). */
162
+ docID?: string;
163
+ /**
164
+ * Байгууллагын нэрийн өмнөөс: байгууллагын ETSI (`NTRMN-<бүртгэл>`). Сервер төлөөллийг prepare-д ба баримт
165
+ * угсрахын өмнө дахин шалгана; татгалзвал `ForbiddenError`/`ApiError` `.code` ({@link OnBehalfErrorCode}).
166
+ */
167
+ onBehalfOf?: string;
168
+ }
169
+ /** prepare-ийн хариу. device-link-д `deviceLink` (digest/interactions-ийг authCode-д ЯГ ХЭВЭЭР). */
170
+ interface PdfPrepareSession {
171
+ sessionId: string;
172
+ docId: string;
173
+ /** Иргэнд ХАРУУЛАХ баталгаажуулах код (ПИН биш). */
174
+ vc: string | null;
175
+ deviceLink: {
176
+ sessionToken: string;
177
+ sessionSecret: string;
178
+ deviceLinkBase: string;
179
+ digest: string;
180
+ interactions: string;
181
+ } | null;
182
+ /** SDK хариу хүлээн авсан мөч (ms epoch) — QR-ийн `receivedAt` ({@link buildDeviceLink}). */
183
+ receivedAt: number;
184
+ }
185
+ declare class PdfApi {
186
+ private readonly http;
187
+ private readonly creds;
188
+ private readonly defaults;
189
+ constructor(http: Http, creds: RpCredentials, defaults: PdfDefaults);
190
+ /** POST /pdf/sign/prepare — multipart (`request` JSON + `pdf`). SIGN эрх шаардана. */
191
+ prepare(input: PdfPrepareInput): Promise<PdfPrepareSession>;
192
+ /**
193
+ * GET /pdf/sign/{sessionID}/document — гарын үсэгтэй PDF (READY-ээс хойш 30 мин). Татсан байтын SHA-256-ийг
194
+ * session-ий `pdf.outSha256`-тай тулга. Эрх хасагдсан бол `ForbiddenError` `.code = "REPRESENTATION_REVOKED"`.
195
+ */
196
+ document(sessionId: string): Promise<Uint8Array>;
197
+ }
198
+
199
+ declare class OrganizationApi {
200
+ private readonly http;
201
+ constructor(http: Http);
202
+ /** GET /organization/representations/etsi/{personEtsi}. Сонголт нь итгэх эх биш — сервер дахин шалгана. */
203
+ getRepresentations(personEtsi: string): Promise<RepresentationsResponse>;
204
+ }
205
+
134
206
  /**
135
207
  * validateAuth-ийн нэмэлт шалгалт — RP ЯМАР урсгал эхлүүлснээ мэддэг тул түүнийг тулгана
136
208
  * (device-link relay хаалт).
@@ -283,6 +355,8 @@ declare function resolveBaseUrl(baseUrl: string | undefined): string;
283
355
  declare class EidClient {
284
356
  readonly auth: AuthApi;
285
357
  readonly sign: SignApi;
358
+ readonly pdf: PdfApi;
359
+ readonly organization: OrganizationApi;
286
360
  readonly session: SessionApi;
287
361
  readonly validator: ResponseValidator;
288
362
  constructor(cfg: ClientConfig);
@@ -383,4 +457,4 @@ interface QrDeviceLinkTickerOptions {
383
457
  */
384
458
  declare function qrDeviceLinkTicker(input: Omit<BuildDeviceLinkInput, "deviceLinkType" | "now">, onLink: (link: string) => void, opts?: QrDeviceLinkTickerOptions): () => void;
385
459
 
386
- export { AcspContext, type AcspV2Params, AuthApi, type AuthDefaults, type AuthOptions, type BuildDeviceLinkInput, CertificateLevel, type ClientConfig, DEFAULT_BASE_URL, DeviceLinkSession, type DeviceLinkSessionType, type DeviceLinkType, EidClient, FlowType, HashAlgorithm, Http, type HttpConfig, Interaction, NotificationSession, type QrDeviceLinkTickerOptions, ResponseValidator, type RevocationChecker, type RevocationStatus, RpCredentials, SessionApi, SessionResult, SignApi, type SignDefaults, type SignOptions, SignatureAlgorithm, type TrustConfig, type ValidateAuthOptions, type VerifiedIdentity, type VerifiedSignature, acspV2Digest, buildAcspV2Payload, buildDeviceLink, interactionsToBase64, parseSessionResult, qrDeviceLinkTicker, randomChallenge, resolveBaseUrl, sha256Base64, userChallengeOf, verifyPayloadSignature, verifyPrehashedSignature };
460
+ export { AcspContext, type AcspV2Params, AuthApi, type AuthDefaults, type AuthOptions, type BuildDeviceLinkInput, CertificateLevel, type ClientConfig, DEFAULT_BASE_URL, DeviceLinkSession, type DeviceLinkSessionType, type DeviceLinkType, EidClient, FlowType, HashAlgorithm, Http, type HttpConfig, Interaction, NotificationSession, OrganizationApi, PdfApi, type PdfDefaults, type PdfPrepareInput, type PdfPrepareSession, type PdfSignerRef, type QrDeviceLinkTickerOptions, RepresentationsResponse, ResponseValidator, type RevocationChecker, type RevocationStatus, RpCredentials, SessionApi, SessionResult, SignApi, type SignDefaults, type SignOptions, SignatureAlgorithm, type TrustConfig, type ValidateAuthOptions, type VerifiedIdentity, type VerifiedSignature, acspV2Digest, buildAcspV2Payload, buildDeviceLink, interactionsToBase64, parseSessionResult, qrDeviceLinkTicker, randomChallenge, resolveBaseUrl, sha256Base64, userChallengeOf, verifyPayloadSignature, verifyPrehashedSignature };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as RpCredentials, C as CertificateLevel, I as Interaction, S as SignatureAlgorithm, H as HashAlgorithm, N as NotificationSession, D as DeviceLinkSession, a as SessionResult, A as AcspContext, F as FlowType } from './browser-BU9ShPWO.js';
2
- export { b as ApiError, c as AuthenticationError, d as DeviceLinkError, e as DeviceLinkInput, E as EidError, f as EndResult, g as ForbiddenError, h as NavigatorLike, i as NetworkError, O as OpenOrShowQROptions, j as SessionFailedError, k as SessionState, l as SignatureAlgorithmParameters, m as SignatureProtocol, V as ValidationError, n as deviceLink, o as isMobileUserAgent, p as openOrShowQR } from './browser-BU9ShPWO.js';
1
+ import { R as RpCredentials, C as CertificateLevel, I as Interaction, S as SignatureAlgorithm, H as HashAlgorithm, N as NotificationSession, D as DeviceLinkSession, a as SessionResult, b as RepresentationsResponse, A as AcspContext, F as FlowType } from './browser-Db82DlYN.js';
2
+ export { c as ApiError, d as AuthenticationError, e as DeviceLinkError, f as DeviceLinkInput, E as EidError, g as EndResult, h as ForbiddenError, i as NavigatorLike, j as NetworkError, O as ON_BEHALF_ERROR_CODES, k as OnBehalfErrorCode, l as OpenOrShowQROptions, m as OrgResult, P as PdfDocumentStatus, n as PdfSessionBlock, o as PdfSigner, p as Representation, q as SessionFailedError, r as SessionState, s as SignatureAlgorithmParameters, t as SignatureProtocol, V as ValidationError, u as deviceLink, v as isMobileUserAgent, w as openOrShowQR } from './browser-Db82DlYN.js';
3
3
  import { X509Certificate, KeyObject } from 'node:crypto';
4
4
 
5
5
  interface HttpConfig {
@@ -23,7 +23,12 @@ declare class Http {
23
23
  constructor(cfg: HttpConfig);
24
24
  post(path: string, body: unknown, timeoutMs?: number): Promise<unknown>;
25
25
  get(path: string, timeoutMs?: number): Promise<unknown>;
26
- private request;
26
+ /** multipart/form-data POST (Content-Type-ийг boundary-тай нь fetch тавина). JSON хариу. */
27
+ postMultipart(path: string, form: FormData, timeoutMs?: number): Promise<unknown>;
28
+ /** GET — хариуг байтаар (ж: гарын үсэгтэй PDF). */
29
+ getBytes(path: string, timeoutMs?: number): Promise<Uint8Array>;
30
+ private json;
31
+ private send;
27
32
  }
28
33
 
29
34
  interface AuthDefaults {
@@ -131,6 +136,73 @@ declare class SessionApi {
131
136
  }): Promise<SessionResult>;
132
137
  }
133
138
 
139
+ interface PdfDefaults {
140
+ certificateLevel: CertificateLevel;
141
+ }
142
+ /** Гарын үсэг зурагч — яг нэг нь. */
143
+ type PdfSignerRef = {
144
+ etsi: string;
145
+ } | {
146
+ documentNumber: string;
147
+ } | {
148
+ certificateChoiceSessionId: string;
149
+ };
150
+ interface PdfPrepareInput {
151
+ /** Эх PDF (≤ 10 223 616 байт — `/.well-known/eid` `pdfSigning.maxInputBytes`). */
152
+ pdf: Uint8Array;
153
+ /** Баримтын нэр (≤ 120 тэмдэгт) — /verify хуудас, иргэний түүхэнд харагдана. */
154
+ fileName: string;
155
+ signer: PdfSignerRef;
156
+ /** `notification` (push) эсвэл `device-link` (QR/Web2App/App2App). Анхдагч notification. */
157
+ flow?: "notification" | "device-link";
158
+ certificateLevel?: CertificateLevel;
159
+ /** device-link Web2App/App2App буцах URL. */
160
+ initialCallbackUrl?: string;
161
+ /** Олон гарын үсэгтэй баримтад хүлээж буй docID (заавал биш — CA өөрөө олно). */
162
+ docID?: string;
163
+ /**
164
+ * Байгууллагын нэрийн өмнөөс: байгууллагын ETSI (`NTRMN-<бүртгэл>`). Сервер төлөөллийг prepare-д ба баримт
165
+ * угсрахын өмнө дахин шалгана; татгалзвал `ForbiddenError`/`ApiError` `.code` ({@link OnBehalfErrorCode}).
166
+ */
167
+ onBehalfOf?: string;
168
+ }
169
+ /** prepare-ийн хариу. device-link-д `deviceLink` (digest/interactions-ийг authCode-д ЯГ ХЭВЭЭР). */
170
+ interface PdfPrepareSession {
171
+ sessionId: string;
172
+ docId: string;
173
+ /** Иргэнд ХАРУУЛАХ баталгаажуулах код (ПИН биш). */
174
+ vc: string | null;
175
+ deviceLink: {
176
+ sessionToken: string;
177
+ sessionSecret: string;
178
+ deviceLinkBase: string;
179
+ digest: string;
180
+ interactions: string;
181
+ } | null;
182
+ /** SDK хариу хүлээн авсан мөч (ms epoch) — QR-ийн `receivedAt` ({@link buildDeviceLink}). */
183
+ receivedAt: number;
184
+ }
185
+ declare class PdfApi {
186
+ private readonly http;
187
+ private readonly creds;
188
+ private readonly defaults;
189
+ constructor(http: Http, creds: RpCredentials, defaults: PdfDefaults);
190
+ /** POST /pdf/sign/prepare — multipart (`request` JSON + `pdf`). SIGN эрх шаардана. */
191
+ prepare(input: PdfPrepareInput): Promise<PdfPrepareSession>;
192
+ /**
193
+ * GET /pdf/sign/{sessionID}/document — гарын үсэгтэй PDF (READY-ээс хойш 30 мин). Татсан байтын SHA-256-ийг
194
+ * session-ий `pdf.outSha256`-тай тулга. Эрх хасагдсан бол `ForbiddenError` `.code = "REPRESENTATION_REVOKED"`.
195
+ */
196
+ document(sessionId: string): Promise<Uint8Array>;
197
+ }
198
+
199
+ declare class OrganizationApi {
200
+ private readonly http;
201
+ constructor(http: Http);
202
+ /** GET /organization/representations/etsi/{personEtsi}. Сонголт нь итгэх эх биш — сервер дахин шалгана. */
203
+ getRepresentations(personEtsi: string): Promise<RepresentationsResponse>;
204
+ }
205
+
134
206
  /**
135
207
  * validateAuth-ийн нэмэлт шалгалт — RP ЯМАР урсгал эхлүүлснээ мэддэг тул түүнийг тулгана
136
208
  * (device-link relay хаалт).
@@ -283,6 +355,8 @@ declare function resolveBaseUrl(baseUrl: string | undefined): string;
283
355
  declare class EidClient {
284
356
  readonly auth: AuthApi;
285
357
  readonly sign: SignApi;
358
+ readonly pdf: PdfApi;
359
+ readonly organization: OrganizationApi;
286
360
  readonly session: SessionApi;
287
361
  readonly validator: ResponseValidator;
288
362
  constructor(cfg: ClientConfig);
@@ -383,4 +457,4 @@ interface QrDeviceLinkTickerOptions {
383
457
  */
384
458
  declare function qrDeviceLinkTicker(input: Omit<BuildDeviceLinkInput, "deviceLinkType" | "now">, onLink: (link: string) => void, opts?: QrDeviceLinkTickerOptions): () => void;
385
459
 
386
- export { AcspContext, type AcspV2Params, AuthApi, type AuthDefaults, type AuthOptions, type BuildDeviceLinkInput, CertificateLevel, type ClientConfig, DEFAULT_BASE_URL, DeviceLinkSession, type DeviceLinkSessionType, type DeviceLinkType, EidClient, FlowType, HashAlgorithm, Http, type HttpConfig, Interaction, NotificationSession, type QrDeviceLinkTickerOptions, ResponseValidator, type RevocationChecker, type RevocationStatus, RpCredentials, SessionApi, SessionResult, SignApi, type SignDefaults, type SignOptions, SignatureAlgorithm, type TrustConfig, type ValidateAuthOptions, type VerifiedIdentity, type VerifiedSignature, acspV2Digest, buildAcspV2Payload, buildDeviceLink, interactionsToBase64, parseSessionResult, qrDeviceLinkTicker, randomChallenge, resolveBaseUrl, sha256Base64, userChallengeOf, verifyPayloadSignature, verifyPrehashedSignature };
460
+ export { AcspContext, type AcspV2Params, AuthApi, type AuthDefaults, type AuthOptions, type BuildDeviceLinkInput, CertificateLevel, type ClientConfig, DEFAULT_BASE_URL, DeviceLinkSession, type DeviceLinkSessionType, type DeviceLinkType, EidClient, FlowType, HashAlgorithm, Http, type HttpConfig, Interaction, NotificationSession, OrganizationApi, PdfApi, type PdfDefaults, type PdfPrepareInput, type PdfPrepareSession, type PdfSignerRef, type QrDeviceLinkTickerOptions, RepresentationsResponse, ResponseValidator, type RevocationChecker, type RevocationStatus, RpCredentials, SessionApi, SessionResult, SignApi, type SignDefaults, type SignOptions, SignatureAlgorithm, type TrustConfig, type ValidateAuthOptions, type VerifiedIdentity, type VerifiedSignature, acspV2Digest, buildAcspV2Payload, buildDeviceLink, interactionsToBase64, parseSessionResult, qrDeviceLinkTicker, randomChallenge, resolveBaseUrl, sha256Base64, userChallengeOf, verifyPayloadSignature, verifyPrehashedSignature };
package/dist/index.js CHANGED
@@ -5,12 +5,13 @@ import {
5
5
  EidError,
6
6
  ForbiddenError,
7
7
  NetworkError,
8
+ ON_BEHALF_ERROR_CODES,
8
9
  SessionFailedError,
9
10
  ValidationError,
10
11
  deviceLink,
11
12
  isMobileUserAgent,
12
13
  openOrShowQR
13
- } from "./chunk-Q5TQMCJC.js";
14
+ } from "./chunk-NXDKQZIW.js";
14
15
 
15
16
  // src/http.ts
16
17
  var DEFAULT_TIMEOUT_MS = 15e3;
@@ -20,21 +21,35 @@ var Http = class {
20
21
  }
21
22
  cfg;
22
23
  post(path, body, timeoutMs) {
23
- return this.request("POST", path, body, timeoutMs);
24
+ return this.json("POST", path, JSON.stringify(body), "application/json", timeoutMs);
24
25
  }
25
26
  get(path, timeoutMs) {
26
- return this.request("GET", path, void 0, timeoutMs);
27
+ return this.json("GET", path, void 0, void 0, timeoutMs);
27
28
  }
28
- async request(method, path, body, timeoutMs) {
29
+ /** multipart/form-data POST (Content-Type-ийг boundary-тай нь fetch тавина). JSON хариу. */
30
+ postMultipart(path, form, timeoutMs) {
31
+ return this.json("POST", path, form, void 0, timeoutMs);
32
+ }
33
+ /** GET — хариуг байтаар (ж: гарын үсэгтэй PDF). */
34
+ async getBytes(path, timeoutMs) {
35
+ const res = await this.send("GET", path, void 0, void 0, timeoutMs);
36
+ return new Uint8Array(await res.arrayBuffer());
37
+ }
38
+ async json(method, path, body, contentType, timeoutMs) {
39
+ const res = await this.send(method, path, body, contentType, timeoutMs);
40
+ const text = await res.text();
41
+ return text ? JSON.parse(text) : {};
42
+ }
43
+ async send(method, path, body, contentType, timeoutMs) {
29
44
  const f = this.cfg.fetchImpl ?? fetch;
30
45
  const url = this.cfg.baseUrl + path;
31
46
  const init = {
32
47
  method,
33
48
  headers: {
34
49
  Authorization: `Bearer ${this.cfg.apiSecret}`,
35
- ...body !== void 0 ? { "Content-Type": "application/json" } : {}
50
+ ...contentType ? { "Content-Type": contentType } : {}
36
51
  },
37
- body: body !== void 0 ? JSON.stringify(body) : void 0,
52
+ body,
38
53
  signal: AbortSignal.timeout(timeoutMs ?? this.cfg.timeoutMs ?? DEFAULT_TIMEOUT_MS)
39
54
  };
40
55
  if (this.cfg.dispatcher) init.dispatcher = this.cfg.dispatcher;
@@ -48,13 +63,32 @@ var Http = class {
48
63
  }
49
64
  throw new NetworkError(`\u0421\u04AF\u043B\u0436\u044D\u044D\u043D\u0438\u0439 \u0430\u043B\u0434\u0430\u0430 (${method} ${path}): ${err.message}`);
50
65
  }
66
+ if (res.status < 300) return res;
51
67
  const text = await res.text();
68
+ const { error, code } = errorBody(text);
52
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)");
53
- if (res.status === 403) throw new ForbiddenError("IP allowlist \u044D\u0441\u0432\u044D\u043B mTLS \u0437\u04E9\u0432\u0448\u04E9\u04E9\u0440\u04E9\u04E9\u0433\u04AF\u0439 (403)");
54
- if (res.status >= 300) throw new ApiError(res.status, text);
55
- return text ? JSON.parse(text) : {};
70
+ if (res.status === 403) {
71
+ throw new ForbiddenError(
72
+ 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
74
+ );
75
+ }
76
+ throw new ApiError(res.status, text, code);
56
77
  }
57
78
  };
79
+ function errorBody(text) {
80
+ try {
81
+ const v = JSON.parse(text);
82
+ if (!v || typeof v !== "object") return {};
83
+ const o = v;
84
+ return {
85
+ error: typeof o.error === "string" && o.error ? o.error : void 0,
86
+ code: typeof o.code === "string" && o.code ? o.code : void 0
87
+ };
88
+ } catch {
89
+ return {};
90
+ }
91
+ }
58
92
 
59
93
  // src/crypto.ts
60
94
  import { createHash, randomBytes } from "crypto";
@@ -266,9 +300,32 @@ function parseSessionResult(raw) {
266
300
  initialCallbackUrl: typeof sig.initialCallbackUrl === "string" ? sig.initialCallbackUrl : null,
267
301
  signatureAlgorithmParameters: parseSigParams(sig.signatureAlgorithmParameters),
268
302
  interactionTypeUsed: str(r.interactionTypeUsed),
269
- onBehalfOf: parseOrg(r.onBehalfOf)
303
+ onBehalfOf: parseOrg(r.onBehalfOf),
304
+ pdf: parsePdf(r.pdf)
270
305
  };
271
306
  }
307
+ function parsePdf(raw) {
308
+ const p = asRecord(raw);
309
+ const docID = str(p.docID);
310
+ if (!docID) return null;
311
+ const out = { docID, documentStatus: String(p.documentStatus ?? "") };
312
+ if (p.errorCode != null) out.errorCode = String(p.errorCode);
313
+ if (p.outSha256 != null) out.outSha256 = String(p.outSha256);
314
+ if (p.size != null) out.size = Number(p.size);
315
+ if (p.expiresAt != null) out.expiresAt = String(p.expiresAt);
316
+ if (p.signatureLevel != null) out.signatureLevel = String(p.signatureLevel);
317
+ const sg = asRecord(p.signer);
318
+ if (sg.etsi != null) {
319
+ out.signer = { etsi: String(sg.etsi), certSerial: String(sg.certSerial ?? "") };
320
+ if (sg.onBehalfOf) out.signer.onBehalfOf = String(sg.onBehalfOf);
321
+ if (sg.claimedRole) out.signer.claimedRole = String(sg.claimedRole);
322
+ }
323
+ const v = asRecord(p.validation);
324
+ if (v.indication != null) {
325
+ out.validation = { indication: String(v.indication), ltv: v.ltv === true, signatures: Number(v.signatures ?? 0) };
326
+ }
327
+ return out;
328
+ }
272
329
  function parseSigParams(raw) {
273
330
  const p = asRecord(raw);
274
331
  const hash = str(p.hashAlgorithm);
@@ -323,6 +380,76 @@ var SessionApi = class {
323
380
  }
324
381
  };
325
382
 
383
+ // src/pdf.ts
384
+ function rec3(v) {
385
+ return v && typeof v === "object" ? v : {};
386
+ }
387
+ var PdfApi = class {
388
+ constructor(http, creds, defaults) {
389
+ this.http = http;
390
+ this.creds = creds;
391
+ this.defaults = defaults;
392
+ }
393
+ http;
394
+ creds;
395
+ defaults;
396
+ /** POST /pdf/sign/prepare — multipart (`request` JSON + `pdf`). SIGN эрх шаардана. */
397
+ async prepare(input) {
398
+ const request = {
399
+ relyingPartyUUID: this.creds.rpUUID,
400
+ relyingPartyName: this.creds.rpName,
401
+ certificateLevel: input.certificateLevel ?? this.defaults.certificateLevel,
402
+ signer: input.signer,
403
+ flow: input.flow ?? "notification",
404
+ fileName: input.fileName
405
+ };
406
+ if (input.initialCallbackUrl?.trim()) request.initialCallbackUrl = input.initialCallbackUrl.trim();
407
+ if (input.docID?.trim()) request.docID = input.docID.trim();
408
+ if (input.onBehalfOf?.trim()) request.onBehalfOf = input.onBehalfOf.trim();
409
+ const form = new FormData();
410
+ form.append("request", new Blob([JSON.stringify(request)], { type: "application/json" }));
411
+ form.append("pdf", new Blob([input.pdf], { type: "application/pdf" }), input.fileName);
412
+ const r = rec3(await this.http.postMultipart("/pdf/sign/prepare", form, 6e4));
413
+ const receivedAt = Date.now();
414
+ const vc = rec3(r.vc).value;
415
+ const dl = rec3(r.deviceLink);
416
+ return {
417
+ sessionId: String(r.sessionID),
418
+ docId: String(r.docID),
419
+ vc: vc != null ? String(vc) : null,
420
+ deviceLink: dl.sessionToken ? {
421
+ sessionToken: String(dl.sessionToken),
422
+ sessionSecret: String(dl.sessionSecret),
423
+ deviceLinkBase: String(dl.deviceLinkBase),
424
+ digest: String(dl.digest),
425
+ interactions: String(dl.interactions)
426
+ } : null,
427
+ receivedAt
428
+ };
429
+ }
430
+ /**
431
+ * GET /pdf/sign/{sessionID}/document — гарын үсэгтэй PDF (READY-ээс хойш 30 мин). Татсан байтын SHA-256-ийг
432
+ * session-ий `pdf.outSha256`-тай тулга. Эрх хасагдсан бол `ForbiddenError` `.code = "REPRESENTATION_REVOKED"`.
433
+ */
434
+ document(sessionId) {
435
+ return this.http.getBytes(`/pdf/sign/${encodeURIComponent(sessionId)}/document`, 6e4);
436
+ }
437
+ };
438
+
439
+ // src/organization.ts
440
+ var OrganizationApi = class {
441
+ constructor(http) {
442
+ this.http = http;
443
+ }
444
+ http;
445
+ /** GET /organization/representations/etsi/{personEtsi}. Сонголт нь итгэх эх биш — сервер дахин шалгана. */
446
+ async getRepresentations(personEtsi) {
447
+ const raw = await this.http.get(`/organization/representations/etsi/${encodeURIComponent(personEtsi)}`);
448
+ const list = Array.isArray(raw.representations) ? raw.representations : [];
449
+ return { personEtsi: String(raw.personEtsi ?? personEtsi), representations: list };
450
+ }
451
+ };
452
+
326
453
  // src/validator.ts
327
454
  import { X509Certificate, constants, createHash as createHash2, publicDecrypt, verify } from "crypto";
328
455
  var ResponseValidator = class {
@@ -677,6 +804,8 @@ function resolveBaseUrl(baseUrl) {
677
804
  var EidClient = class {
678
805
  auth;
679
806
  sign;
807
+ pdf;
808
+ organization;
680
809
  session;
681
810
  validator;
682
811
  constructor(cfg) {
@@ -692,6 +821,8 @@ var EidClient = class {
692
821
  const level = cfg.defaultCertificateLevel ?? "QUALIFIED";
693
822
  this.auth = new AuthApi(http, cfg.credentials, { certificateLevel: level });
694
823
  this.sign = new SignApi(http, cfg.credentials, { certificateLevel: level });
824
+ this.pdf = new PdfApi(http, cfg.credentials, { certificateLevel: level });
825
+ this.organization = new OrganizationApi(http);
695
826
  this.session = new SessionApi(http);
696
827
  this.validator = new ResponseValidator(cfg.trust ?? {});
697
828
  }
@@ -777,6 +908,9 @@ export {
777
908
  ForbiddenError,
778
909
  Http,
779
910
  NetworkError,
911
+ ON_BEHALF_ERROR_CODES,
912
+ OrganizationApi,
913
+ PdfApi,
780
914
  ResponseValidator,
781
915
  SessionApi,
782
916
  SessionFailedError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gerege-systems/eid-mongolia-sdk",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "e-ID Mongolia Relying Party SDK — иргэнээр утсаар нь нэвтрэлт ба хууль ёсны цахим гарын үсэг (RP-API v3, Smart-ID нийцтэй).",
5
5
  "license": "MIT",
6
6
  "type": "module",