@gerege-systems/eid-mongolia-sdk 0.6.0 → 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 +50 -2
- package/dist/{browser-PhjcbuAN.d.cts → browser-Db82DlYN.d.cts} +86 -3
- package/dist/{browser-PhjcbuAN.d.ts → browser-Db82DlYN.d.ts} +86 -3
- package/dist/browser.d.cts +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +1 -1
- package/dist/{chunk-Q5TQMCJC.js → chunk-NXDKQZIW.js} +18 -1
- package/dist/index.cjs +177 -11
- package/dist/index.d.cts +78 -4
- package/dist/index.d.ts +78 -4
- package/dist/index.js +159 -11
- package/package.json +1 -1
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 апптай **ижил
|
|
@@ -160,6 +205,9 @@ OK ирж болзошгүй. Иймд `ResponseValidator`:
|
|
|
160
205
|
4. Гарын үсгийг иргэний public key-ээр шалгана:
|
|
161
206
|
- **auth** → ACSP_V2 payload (`smart-id|ACSP_V2|serverRandom|rpChallenge|userChallenge|B64(rpName)|B64(brokeredRpName)|B64(SHA-256(interactions))|interactionTypeUsed|initialCallbackUrl|flowType`, hash = хүсэлтийн `hashAlgorithm`) дээр — `flowType`, `userChallenge`, `serverRandom` ба алгоритмын downgrade-ийг мөн шалгана;
|
|
162
207
|
`expectedFlowType` өгвөл `flowType` эхлүүлсэн урсгалтай ЯГ таарах, Web2App/App2App-д `userChallengeVerifier` заавал
|
|
208
|
+
`initialCallbackUrl`-ийг хариуны `signature.initialCallbackUrl`-аас авна (CA урсгалаар шийднэ: QR/Notification →
|
|
209
|
+
`""`, Web2App/App2App → таны callback; шилжилтийн үед legacy `/dl` ба push-д таны callback) — зөвхөн `""` эсвэл
|
|
210
|
+
таны илгээсэн callback байхад, Web2App/App2App-д заавал таны callback; өөр утга → татгалзана
|
|
163
211
|
- **sign** → RP-ийн өгсөн digest дээр
|
|
164
212
|
|
|
165
213
|
Аль нэг алхам бүтэлгүйтвэл `ValidationError` шиднэ — хариунд **итгэхгүй**.
|
|
@@ -169,10 +217,10 @@ OK ирж болзошгүй. Иймд `ResponseValidator`:
|
|
|
169
217
|
| Алдаа | Утга |
|
|
170
218
|
|---|---|
|
|
171
219
|
| `AuthenticationError` | 401 — API secret буруу/байхгүй |
|
|
172
|
-
| `ForbiddenError` | 403 — IP allowlist / mTLS
|
|
220
|
+
| `ForbiddenError` | 403 — IP allowlist / mTLS / RP-ийн эрх; onBehalfOf татгалзвал `.code` (`OnBehalfErrorCode`) |
|
|
173
221
|
| `SessionFailedError` | endResult ≠ OK (TIMEOUT, USER_REFUSED…) — `.endResult`-оор салга |
|
|
174
222
|
| `ValidationError` | cert chain/signature/level шалгалт бүтэлгүйтсэн |
|
|
175
|
-
| `ApiError` | бусад HTTP алдаа (`.status`, `.body`) |
|
|
223
|
+
| `ApiError` | бусад HTTP алдаа (`.status`, `.body`, серверийн `.code` — ж: 404 `ORG_NOT_FOUND`) |
|
|
176
224
|
| `NetworkError` | timeout/сүлжээ |
|
|
177
225
|
| `DeviceLinkError` | device-link холбоос угсрах боломжгүй (`deviceLinkBase` https биш, token/secret/lang хэлбэр, төрөлд хэрэгтэй талбар дутуу) |
|
|
178
226
|
|
|
@@ -116,6 +116,12 @@ interface SessionResult {
|
|
|
116
116
|
serverRandom: string | null;
|
|
117
117
|
userChallenge: string | null;
|
|
118
118
|
flowType: string | null;
|
|
119
|
+
/**
|
|
120
|
+
* ACSP_V2 payload-д гарын үсэг зурагдсан `initialCallbackUrl` (хариуны `signature.initialCallbackUrl`,
|
|
121
|
+
* eID нэмэлт). Smart-ID: QR/Notification → `""`, Web2App/App2App → RP-ийн илгээсэн URL. null = хариунд
|
|
122
|
+
* талбар алга (хуучин CA). Validator үүнийг зөвхөн `""` эсвэл RP-ийн өөрийн callback байхад л хүлээн авна.
|
|
123
|
+
*/
|
|
124
|
+
initialCallbackUrl?: string | null;
|
|
119
125
|
signatureAlgorithmParameters: SignatureAlgorithmParameters | null;
|
|
120
126
|
interactionTypeUsed: string | null;
|
|
121
127
|
/**
|
|
@@ -124,6 +130,61 @@ interface SessionResult {
|
|
|
124
130
|
* RP энэ талбарыг л итгэж уншина (клиентийн сонголтод биш). docs/ORG_LOGIN.md.
|
|
125
131
|
*/
|
|
126
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[];
|
|
127
188
|
}
|
|
128
189
|
/** Session-ий байгууллагын блок — байгууллага + тухайн иргэний эрх (бүртгэлээс real-time). */
|
|
129
190
|
interface OrgResult {
|
|
@@ -200,15 +261,37 @@ declare class EidError extends Error {
|
|
|
200
261
|
/** HTTP 401 — API secret буруу/байхгүй. Тохиргооны (RP secret) алдаа. */
|
|
201
262
|
declare class AuthenticationError extends EidError {
|
|
202
263
|
}
|
|
203
|
-
/**
|
|
264
|
+
/**
|
|
265
|
+
* HTTP 403 — IP allowlist/mTLS зөвшөөрөөгүй, RP-д эрх алга, эсвэл onBehalfOf-ийн төлөөлөл татгалзсан.
|
|
266
|
+
* Сервер `{"error","code"}` буцаасан бол `code` (ж: `REPRESENTATION_DENIED`) — салааллыг үүгээр хийнэ.
|
|
267
|
+
*/
|
|
204
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);
|
|
205
274
|
}
|
|
206
275
|
/** RP-API-аас ирсэн бусад HTTP алдаа (4xx/5xx). status ба хариуны биеийг агуулна. */
|
|
207
276
|
declare class ApiError extends EidError {
|
|
208
277
|
readonly status: number;
|
|
209
278
|
readonly body: string;
|
|
210
|
-
|
|
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);
|
|
211
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[];
|
|
212
295
|
/** Сүлжээ/timeout — upstream хариу өгсөнгүй. */
|
|
213
296
|
declare class NetworkError extends EidError {
|
|
214
297
|
}
|
|
@@ -234,4 +317,4 @@ declare class ValidationError extends EidError {
|
|
|
234
317
|
declare class DeviceLinkError extends EidError {
|
|
235
318
|
}
|
|
236
319
|
|
|
237
|
-
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
|
|
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 };
|
|
@@ -116,6 +116,12 @@ interface SessionResult {
|
|
|
116
116
|
serverRandom: string | null;
|
|
117
117
|
userChallenge: string | null;
|
|
118
118
|
flowType: string | null;
|
|
119
|
+
/**
|
|
120
|
+
* ACSP_V2 payload-д гарын үсэг зурагдсан `initialCallbackUrl` (хариуны `signature.initialCallbackUrl`,
|
|
121
|
+
* eID нэмэлт). Smart-ID: QR/Notification → `""`, Web2App/App2App → RP-ийн илгээсэн URL. null = хариунд
|
|
122
|
+
* талбар алга (хуучин CA). Validator үүнийг зөвхөн `""` эсвэл RP-ийн өөрийн callback байхад л хүлээн авна.
|
|
123
|
+
*/
|
|
124
|
+
initialCallbackUrl?: string | null;
|
|
119
125
|
signatureAlgorithmParameters: SignatureAlgorithmParameters | null;
|
|
120
126
|
interactionTypeUsed: string | null;
|
|
121
127
|
/**
|
|
@@ -124,6 +130,61 @@ interface SessionResult {
|
|
|
124
130
|
* RP энэ талбарыг л итгэж уншина (клиентийн сонголтод биш). docs/ORG_LOGIN.md.
|
|
125
131
|
*/
|
|
126
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[];
|
|
127
188
|
}
|
|
128
189
|
/** Session-ий байгууллагын блок — байгууллага + тухайн иргэний эрх (бүртгэлээс real-time). */
|
|
129
190
|
interface OrgResult {
|
|
@@ -200,15 +261,37 @@ declare class EidError extends Error {
|
|
|
200
261
|
/** HTTP 401 — API secret буруу/байхгүй. Тохиргооны (RP secret) алдаа. */
|
|
201
262
|
declare class AuthenticationError extends EidError {
|
|
202
263
|
}
|
|
203
|
-
/**
|
|
264
|
+
/**
|
|
265
|
+
* HTTP 403 — IP allowlist/mTLS зөвшөөрөөгүй, RP-д эрх алга, эсвэл onBehalfOf-ийн төлөөлөл татгалзсан.
|
|
266
|
+
* Сервер `{"error","code"}` буцаасан бол `code` (ж: `REPRESENTATION_DENIED`) — салааллыг үүгээр хийнэ.
|
|
267
|
+
*/
|
|
204
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);
|
|
205
274
|
}
|
|
206
275
|
/** RP-API-аас ирсэн бусад HTTP алдаа (4xx/5xx). status ба хариуны биеийг агуулна. */
|
|
207
276
|
declare class ApiError extends EidError {
|
|
208
277
|
readonly status: number;
|
|
209
278
|
readonly body: string;
|
|
210
|
-
|
|
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);
|
|
211
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[];
|
|
212
295
|
/** Сүлжээ/timeout — upstream хариу өгсөнгүй. */
|
|
213
296
|
declare class NetworkError extends EidError {
|
|
214
297
|
}
|
|
@@ -234,4 +317,4 @@ declare class ValidationError extends EidError {
|
|
|
234
317
|
declare class DeviceLinkError extends EidError {
|
|
235
318
|
}
|
|
236
319
|
|
|
237
|
-
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
|
|
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 };
|
package/dist/browser.d.cts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
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 {
|
|
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
|
@@ -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.
|
|
117
|
+
return this.json("POST", path, JSON.stringify(body), "application/json", timeoutMs);
|
|
99
118
|
}
|
|
100
119
|
get(path, timeoutMs) {
|
|
101
|
-
return this.
|
|
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
|
|
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
|
-
...
|
|
143
|
+
...contentType ? { "Content-Type": contentType } : {}
|
|
111
144
|
},
|
|
112
|
-
body
|
|
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)
|
|
129
|
-
|
|
130
|
-
|
|
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");
|
|
@@ -336,11 +388,37 @@ function parseSessionResult(raw) {
|
|
|
336
388
|
serverRandom: str(sig.serverRandom),
|
|
337
389
|
userChallenge: str(sig.userChallenge),
|
|
338
390
|
flowType: str(sig.flowType),
|
|
391
|
+
// CA-ийн eID нэмэлт (f59d14ee-оос): гарын үсэг зурагдсан ACSP_V2 initialCallbackUrl. "" нь утга —
|
|
392
|
+
// null/undefined-аас ялгана (талбар алга = хуучин CA).
|
|
393
|
+
initialCallbackUrl: typeof sig.initialCallbackUrl === "string" ? sig.initialCallbackUrl : null,
|
|
339
394
|
signatureAlgorithmParameters: parseSigParams(sig.signatureAlgorithmParameters),
|
|
340
395
|
interactionTypeUsed: str(r.interactionTypeUsed),
|
|
341
|
-
onBehalfOf: parseOrg(r.onBehalfOf)
|
|
396
|
+
onBehalfOf: parseOrg(r.onBehalfOf),
|
|
397
|
+
pdf: parsePdf(r.pdf)
|
|
342
398
|
};
|
|
343
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
|
+
}
|
|
344
422
|
function parseSigParams(raw) {
|
|
345
423
|
const p = asRecord(raw);
|
|
346
424
|
const hash = str(p.hashAlgorithm);
|
|
@@ -395,6 +473,76 @@ var SessionApi = class {
|
|
|
395
473
|
}
|
|
396
474
|
};
|
|
397
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
|
+
|
|
398
546
|
// src/validator.ts
|
|
399
547
|
var import_node_crypto2 = require("crypto");
|
|
400
548
|
var ResponseValidator = class {
|
|
@@ -447,7 +595,7 @@ var ResponseValidator = class {
|
|
|
447
595
|
brokeredRpName: acsp.brokeredRpName,
|
|
448
596
|
interactions: acsp.interactions,
|
|
449
597
|
interactionTypeUsed,
|
|
450
|
-
initialCallbackUrl: acsp.initialCallbackUrl,
|
|
598
|
+
initialCallbackUrl: signedCallbackUrl(result.initialCallbackUrl, acsp.initialCallbackUrl, flowType),
|
|
451
599
|
flowType
|
|
452
600
|
});
|
|
453
601
|
if (!verifyPayloadSignature(payload, result.signatureValueB64, leaf.publicKey, alg, hash)) {
|
|
@@ -584,6 +732,17 @@ var ResponseValidator = class {
|
|
|
584
732
|
}
|
|
585
733
|
}
|
|
586
734
|
};
|
|
735
|
+
function signedCallbackUrl(fromResponse, rpCallback, flowType) {
|
|
736
|
+
const rp = rpCallback ?? "";
|
|
737
|
+
if (fromResponse == null) return rp;
|
|
738
|
+
if (fromResponse !== "" && fromResponse !== rp) {
|
|
739
|
+
throw new ValidationError("signature.initialCallbackUrl \u043D\u044C \u0445\u043E\u043E\u0441\u043E\u043D \u0447 \u0431\u0438\u0448, \u0438\u043B\u0433\u044D\u044D\u0441\u044D\u043D callback \u0447 \u0431\u0438\u0448 (\u0445\u0443\u0443\u0440\u0430\u043C\u0447 \u0445\u0430\u0440\u0438\u0443 \u0431\u0430\u0439\u0436 \u0431\u043E\u043B\u0437\u043E\u0448\u0433\u04AF\u0439)");
|
|
740
|
+
}
|
|
741
|
+
if ((flowType === "Web2App" || flowType === "App2App") && fromResponse !== rp) {
|
|
742
|
+
throw new ValidationError(`${flowType} \u0443\u0440\u0441\u0433\u0430\u043B\u0434 signature.initialCallbackUrl \u043D\u044C \u0438\u043B\u0433\u044D\u044D\u0441\u044D\u043D callback \u0431\u0430\u0439\u0445 \u0451\u0441\u0442\u043E\u0439`);
|
|
743
|
+
}
|
|
744
|
+
return fromResponse;
|
|
745
|
+
}
|
|
587
746
|
function userChallengeOf(verifier) {
|
|
588
747
|
return (0, import_node_crypto2.createHash)("sha256").update(verifier, "utf8").digest("base64url");
|
|
589
748
|
}
|
|
@@ -738,6 +897,8 @@ function resolveBaseUrl(baseUrl) {
|
|
|
738
897
|
var EidClient = class {
|
|
739
898
|
auth;
|
|
740
899
|
sign;
|
|
900
|
+
pdf;
|
|
901
|
+
organization;
|
|
741
902
|
session;
|
|
742
903
|
validator;
|
|
743
904
|
constructor(cfg) {
|
|
@@ -753,6 +914,8 @@ var EidClient = class {
|
|
|
753
914
|
const level = cfg.defaultCertificateLevel ?? "QUALIFIED";
|
|
754
915
|
this.auth = new AuthApi(http, cfg.credentials, { certificateLevel: level });
|
|
755
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);
|
|
756
919
|
this.session = new SessionApi(http);
|
|
757
920
|
this.validator = new ResponseValidator(cfg.trust ?? {});
|
|
758
921
|
}
|
|
@@ -881,6 +1044,9 @@ function qrDeviceLinkTicker(input, onLink, opts) {
|
|
|
881
1044
|
ForbiddenError,
|
|
882
1045
|
Http,
|
|
883
1046
|
NetworkError,
|
|
1047
|
+
ON_BEHALF_ERROR_CODES,
|
|
1048
|
+
OrganizationApi,
|
|
1049
|
+
PdfApi,
|
|
884
1050
|
ResponseValidator,
|
|
885
1051
|
SessionApi,
|
|
886
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-
|
|
2
|
-
export {
|
|
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
|
-
|
|
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-
|
|
2
|
-
export {
|
|
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
|
-
|
|
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-
|
|
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.
|
|
24
|
+
return this.json("POST", path, JSON.stringify(body), "application/json", timeoutMs);
|
|
24
25
|
}
|
|
25
26
|
get(path, timeoutMs) {
|
|
26
|
-
return this.
|
|
27
|
+
return this.json("GET", path, void 0, void 0, timeoutMs);
|
|
27
28
|
}
|
|
28
|
-
|
|
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
|
-
...
|
|
50
|
+
...contentType ? { "Content-Type": contentType } : {}
|
|
36
51
|
},
|
|
37
|
-
body
|
|
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)
|
|
54
|
-
|
|
55
|
-
|
|
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";
|
|
@@ -261,11 +295,37 @@ function parseSessionResult(raw) {
|
|
|
261
295
|
serverRandom: str(sig.serverRandom),
|
|
262
296
|
userChallenge: str(sig.userChallenge),
|
|
263
297
|
flowType: str(sig.flowType),
|
|
298
|
+
// CA-ийн eID нэмэлт (f59d14ee-оос): гарын үсэг зурагдсан ACSP_V2 initialCallbackUrl. "" нь утга —
|
|
299
|
+
// null/undefined-аас ялгана (талбар алга = хуучин CA).
|
|
300
|
+
initialCallbackUrl: typeof sig.initialCallbackUrl === "string" ? sig.initialCallbackUrl : null,
|
|
264
301
|
signatureAlgorithmParameters: parseSigParams(sig.signatureAlgorithmParameters),
|
|
265
302
|
interactionTypeUsed: str(r.interactionTypeUsed),
|
|
266
|
-
onBehalfOf: parseOrg(r.onBehalfOf)
|
|
303
|
+
onBehalfOf: parseOrg(r.onBehalfOf),
|
|
304
|
+
pdf: parsePdf(r.pdf)
|
|
267
305
|
};
|
|
268
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
|
+
}
|
|
269
329
|
function parseSigParams(raw) {
|
|
270
330
|
const p = asRecord(raw);
|
|
271
331
|
const hash = str(p.hashAlgorithm);
|
|
@@ -320,6 +380,76 @@ var SessionApi = class {
|
|
|
320
380
|
}
|
|
321
381
|
};
|
|
322
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
|
+
|
|
323
453
|
// src/validator.ts
|
|
324
454
|
import { X509Certificate, constants, createHash as createHash2, publicDecrypt, verify } from "crypto";
|
|
325
455
|
var ResponseValidator = class {
|
|
@@ -372,7 +502,7 @@ var ResponseValidator = class {
|
|
|
372
502
|
brokeredRpName: acsp.brokeredRpName,
|
|
373
503
|
interactions: acsp.interactions,
|
|
374
504
|
interactionTypeUsed,
|
|
375
|
-
initialCallbackUrl: acsp.initialCallbackUrl,
|
|
505
|
+
initialCallbackUrl: signedCallbackUrl(result.initialCallbackUrl, acsp.initialCallbackUrl, flowType),
|
|
376
506
|
flowType
|
|
377
507
|
});
|
|
378
508
|
if (!verifyPayloadSignature(payload, result.signatureValueB64, leaf.publicKey, alg, hash)) {
|
|
@@ -509,6 +639,17 @@ var ResponseValidator = class {
|
|
|
509
639
|
}
|
|
510
640
|
}
|
|
511
641
|
};
|
|
642
|
+
function signedCallbackUrl(fromResponse, rpCallback, flowType) {
|
|
643
|
+
const rp = rpCallback ?? "";
|
|
644
|
+
if (fromResponse == null) return rp;
|
|
645
|
+
if (fromResponse !== "" && fromResponse !== rp) {
|
|
646
|
+
throw new ValidationError("signature.initialCallbackUrl \u043D\u044C \u0445\u043E\u043E\u0441\u043E\u043D \u0447 \u0431\u0438\u0448, \u0438\u043B\u0433\u044D\u044D\u0441\u044D\u043D callback \u0447 \u0431\u0438\u0448 (\u0445\u0443\u0443\u0440\u0430\u043C\u0447 \u0445\u0430\u0440\u0438\u0443 \u0431\u0430\u0439\u0436 \u0431\u043E\u043B\u0437\u043E\u0448\u0433\u04AF\u0439)");
|
|
647
|
+
}
|
|
648
|
+
if ((flowType === "Web2App" || flowType === "App2App") && fromResponse !== rp) {
|
|
649
|
+
throw new ValidationError(`${flowType} \u0443\u0440\u0441\u0433\u0430\u043B\u0434 signature.initialCallbackUrl \u043D\u044C \u0438\u043B\u0433\u044D\u044D\u0441\u044D\u043D callback \u0431\u0430\u0439\u0445 \u0451\u0441\u0442\u043E\u0439`);
|
|
650
|
+
}
|
|
651
|
+
return fromResponse;
|
|
652
|
+
}
|
|
512
653
|
function userChallengeOf(verifier) {
|
|
513
654
|
return createHash2("sha256").update(verifier, "utf8").digest("base64url");
|
|
514
655
|
}
|
|
@@ -663,6 +804,8 @@ function resolveBaseUrl(baseUrl) {
|
|
|
663
804
|
var EidClient = class {
|
|
664
805
|
auth;
|
|
665
806
|
sign;
|
|
807
|
+
pdf;
|
|
808
|
+
organization;
|
|
666
809
|
session;
|
|
667
810
|
validator;
|
|
668
811
|
constructor(cfg) {
|
|
@@ -678,6 +821,8 @@ var EidClient = class {
|
|
|
678
821
|
const level = cfg.defaultCertificateLevel ?? "QUALIFIED";
|
|
679
822
|
this.auth = new AuthApi(http, cfg.credentials, { certificateLevel: level });
|
|
680
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);
|
|
681
826
|
this.session = new SessionApi(http);
|
|
682
827
|
this.validator = new ResponseValidator(cfg.trust ?? {});
|
|
683
828
|
}
|
|
@@ -763,6 +908,9 @@ export {
|
|
|
763
908
|
ForbiddenError,
|
|
764
909
|
Http,
|
|
765
910
|
NetworkError,
|
|
911
|
+
ON_BEHALF_ERROR_CODES,
|
|
912
|
+
OrganizationApi,
|
|
913
|
+
PdfApi,
|
|
766
914
|
ResponseValidator,
|
|
767
915
|
SessionApi,
|
|
768
916
|
SessionFailedError,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gerege-systems/eid-mongolia-sdk",
|
|
3
|
-
"version": "0.
|
|
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",
|