@gerege-systems/eid-mongolia-sdk 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -6
- package/dist/{browser-B_XtyuFG.d.cts → browser-BWCDj8xO.d.cts} +104 -5
- package/dist/{browser-B_XtyuFG.d.ts → browser-BWCDj8xO.d.ts} +104 -5
- package/dist/browser.d.cts +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +1 -1
- package/dist/{chunk-NXDKQZIW.js → chunk-YDVPZMAL.js} +71 -2
- package/dist/index.cjs +553 -45
- package/dist/index.d.cts +347 -7
- package/dist/index.d.ts +347 -7
- package/dist/index.js +474 -39
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -23,7 +23,9 @@ __export(index_exports, {
|
|
|
23
23
|
ApiError: () => ApiError,
|
|
24
24
|
AuthApi: () => AuthApi,
|
|
25
25
|
AuthenticationError: () => AuthenticationError,
|
|
26
|
+
CITIZEN_ISSUING_CA_SKI: () => CITIZEN_ISSUING_CA_SKI,
|
|
26
27
|
DEFAULT_BASE_URL: () => DEFAULT_BASE_URL,
|
|
28
|
+
DEFAULT_PDF_TIMEOUT_MS: () => DEFAULT_PDF_TIMEOUT_MS,
|
|
27
29
|
DeviceLinkError: () => DeviceLinkError,
|
|
28
30
|
EID_ACR_ADVANCED: () => EID_ACR_ADVANCED,
|
|
29
31
|
EID_ACR_QUALIFIED: () => EID_ACR_QUALIFIED,
|
|
@@ -38,10 +40,13 @@ __export(index_exports, {
|
|
|
38
40
|
ON_BEHALF_ERROR_CODES: () => ON_BEHALF_ERROR_CODES,
|
|
39
41
|
OrganizationApi: () => OrganizationApi,
|
|
40
42
|
PdfApi: () => PdfApi,
|
|
43
|
+
PersonApi: () => PersonApi,
|
|
44
|
+
RETRYABLE_ERROR_CODES: () => RETRYABLE_ERROR_CODES,
|
|
41
45
|
ResponseValidator: () => ResponseValidator,
|
|
42
46
|
SessionApi: () => SessionApi,
|
|
43
47
|
SessionFailedError: () => SessionFailedError,
|
|
44
48
|
SignApi: () => SignApi,
|
|
49
|
+
TokenApi: () => TokenApi,
|
|
45
50
|
ValidationError: () => ValidationError,
|
|
46
51
|
acspV2Digest: () => acspV2Digest,
|
|
47
52
|
buildAcspV2Payload: () => buildAcspV2Payload,
|
|
@@ -50,16 +55,21 @@ __export(index_exports, {
|
|
|
50
55
|
interactionsToBase64: () => interactionsToBase64,
|
|
51
56
|
isMobileUserAgent: () => isMobileUserAgent,
|
|
52
57
|
openOrShowQR: () => openOrShowQR,
|
|
58
|
+
parseRetryAfter: () => parseRetryAfter,
|
|
53
59
|
parseSessionResult: () => parseSessionResult,
|
|
54
60
|
parseSubject: () => parseSubject,
|
|
61
|
+
personMatchesCertificate: () => personMatchesCertificate,
|
|
55
62
|
pkceChallenge: () => pkceChallenge,
|
|
56
63
|
qrDeviceLinkTicker: () => qrDeviceLinkTicker,
|
|
57
64
|
randomChallenge: () => randomChallenge,
|
|
58
65
|
resolveBaseUrl: () => resolveBaseUrl,
|
|
59
66
|
sha256Base64: () => sha256Base64,
|
|
67
|
+
tokenAuthDigest: () => tokenAuthDigest,
|
|
68
|
+
tokenLimitOf: () => tokenLimitOf,
|
|
60
69
|
userChallengeOf: () => userChallengeOf,
|
|
61
70
|
verifyPayloadSignature: () => verifyPayloadSignature,
|
|
62
|
-
verifyPrehashedSignature: () => verifyPrehashedSignature
|
|
71
|
+
verifyPrehashedSignature: () => verifyPrehashedSignature,
|
|
72
|
+
verifyTokenCertificates: () => verifyTokenCertificates
|
|
63
73
|
});
|
|
64
74
|
module.exports = __toCommonJS(index_exports);
|
|
65
75
|
|
|
@@ -71,25 +81,79 @@ var EidError = class extends Error {
|
|
|
71
81
|
}
|
|
72
82
|
};
|
|
73
83
|
var AuthenticationError = class extends EidError {
|
|
84
|
+
/** Серверийн `{"code"}`; байхгүй / JSON биш (proxy) бол undefined. */
|
|
85
|
+
code;
|
|
86
|
+
/** Серверийн `{"error"}` текст (логт); JSON биш (proxy) бол undefined. */
|
|
87
|
+
serverMessage;
|
|
88
|
+
/** Хариуны бие (таслагдаагүй текст). */
|
|
89
|
+
body;
|
|
90
|
+
headers;
|
|
91
|
+
constructor(message, init) {
|
|
92
|
+
super(message);
|
|
93
|
+
this.code = init?.code;
|
|
94
|
+
this.serverMessage = init?.serverMessage;
|
|
95
|
+
this.body = init?.body;
|
|
96
|
+
this.headers = init?.headers;
|
|
97
|
+
}
|
|
74
98
|
};
|
|
75
99
|
var ForbiddenError = class extends EidError {
|
|
76
|
-
constructor(message, code) {
|
|
100
|
+
constructor(message, code, init) {
|
|
77
101
|
super(message);
|
|
78
102
|
this.code = code;
|
|
103
|
+
this.headers = init?.headers;
|
|
104
|
+
this.retryAfterMs = init?.retryAfterMs;
|
|
79
105
|
}
|
|
80
106
|
code;
|
|
107
|
+
headers;
|
|
108
|
+
/** Retry-After (мс), байхгүй бол undefined. */
|
|
109
|
+
retryAfterMs;
|
|
81
110
|
};
|
|
82
111
|
var ApiError = class extends EidError {
|
|
83
|
-
constructor(status, body, code) {
|
|
112
|
+
constructor(status, body, code, init) {
|
|
84
113
|
super(`RP-API ${status}: ${body}`);
|
|
85
114
|
this.status = status;
|
|
86
115
|
this.body = body;
|
|
87
116
|
this.code = code;
|
|
117
|
+
this.headers = init?.headers;
|
|
118
|
+
this.retryAfterMs = init?.retryAfterMs;
|
|
88
119
|
}
|
|
89
120
|
status;
|
|
90
121
|
body;
|
|
91
122
|
code;
|
|
123
|
+
headers;
|
|
124
|
+
/**
|
|
125
|
+
* Retry-After (мс, секунд эсвэл HTTP-огноо задалсан); байхгүй/буруу бол undefined. CA түр алдаанд (429
|
|
126
|
+
* PDF_PREPARE_BUSY / RATE_LIMITED, 503 DOCUMENT_PENDING / TSA_UNAVAILABLE / LTV_UNAVAILABLE г.м.) тавина.
|
|
127
|
+
*/
|
|
128
|
+
retryAfterMs;
|
|
129
|
+
/**
|
|
130
|
+
* CA-ийн түр алдаа ({@link RETRYABLE_ERROR_CODES}-ийн нэг, эсвэл 429/503 + Retry-After): `retryAfterMs`-ийн дараа
|
|
131
|
+
* ижил хүсэлтийг давтаж болно.
|
|
132
|
+
*/
|
|
133
|
+
get retryable() {
|
|
134
|
+
if (this.code && RETRYABLE_ERROR_CODES.includes(this.code)) return true;
|
|
135
|
+
return (this.status === 429 || this.status === 503) && (this.retryAfterMs ?? 0) > 0;
|
|
136
|
+
}
|
|
92
137
|
};
|
|
138
|
+
var RETRYABLE_ERROR_CODES = [
|
|
139
|
+
"PDF_PREPARE_BUSY",
|
|
140
|
+
"DOCUMENT_PENDING",
|
|
141
|
+
"TSA_UNAVAILABLE",
|
|
142
|
+
"LTV_UNAVAILABLE",
|
|
143
|
+
"RATE_LIMITED",
|
|
144
|
+
"STATE_UNAVAILABLE",
|
|
145
|
+
"ISSUANCE_PENDING"
|
|
146
|
+
];
|
|
147
|
+
function parseRetryAfter(value, now = Date.now()) {
|
|
148
|
+
const v = (value ?? "").trim();
|
|
149
|
+
if (!v) return void 0;
|
|
150
|
+
if (/^-?\d+$/.test(v)) {
|
|
151
|
+
const n = Number(v);
|
|
152
|
+
return n > 0 && n <= 86400 ? n * 1e3 : void 0;
|
|
153
|
+
}
|
|
154
|
+
const t = Date.parse(v);
|
|
155
|
+
return Number.isNaN(t) || t <= now ? void 0 : t - now;
|
|
156
|
+
}
|
|
93
157
|
var ON_BEHALF_ERROR_CODES = [
|
|
94
158
|
"REPRESENTATION_DENIED",
|
|
95
159
|
"REPRESENTATION_PENDING",
|
|
@@ -100,7 +164,19 @@ var ON_BEHALF_ERROR_CODES = [
|
|
|
100
164
|
"REPRESENTATION_REVOKED"
|
|
101
165
|
];
|
|
102
166
|
var NetworkError = class extends EidError {
|
|
167
|
+
/** "GET /session/…" (таних тэмдэг хасагдсан зам). */
|
|
168
|
+
op;
|
|
169
|
+
/** Хүсэлт хугацаа хэтэрсэн. */
|
|
170
|
+
timeout;
|
|
171
|
+
constructor(message, init) {
|
|
172
|
+
super(message);
|
|
173
|
+
this.op = init?.op;
|
|
174
|
+
this.timeout = init?.timeout;
|
|
175
|
+
}
|
|
103
176
|
};
|
|
177
|
+
function maskPath(path) {
|
|
178
|
+
return path.split(/[?#]/)[0].replace(/\/(etsi|document)\/[^/?#\s]+/g, "/$1/\u2026");
|
|
179
|
+
}
|
|
104
180
|
var SessionFailedError = class extends EidError {
|
|
105
181
|
constructor(endResult) {
|
|
106
182
|
super(`Session \u0430\u043C\u0436\u0438\u043B\u0442\u0433\u04AF\u0439: ${endResult}`);
|
|
@@ -154,26 +230,32 @@ var Http = class {
|
|
|
154
230
|
};
|
|
155
231
|
if (this.cfg.dispatcher) init.dispatcher = this.cfg.dispatcher;
|
|
156
232
|
let res;
|
|
233
|
+
const op = `${method} ${maskPath(path)}`;
|
|
157
234
|
try {
|
|
158
235
|
res = await f(url, init);
|
|
159
236
|
} catch (e) {
|
|
160
237
|
const err = e;
|
|
161
238
|
if (err.name === "TimeoutError" || err.name === "AbortError") {
|
|
162
|
-
throw new NetworkError(`\u0425\u04AF\u0441\u044D\u043B\u0442 \u0445\u0443\u0433\u0430\u0446\u0430\u0430 \u0445\u044D\u0442\u044D\u0440\u043B\u044D\u044D: ${
|
|
239
|
+
throw new NetworkError(`\u0425\u04AF\u0441\u044D\u043B\u0442 \u0445\u0443\u0433\u0430\u0446\u0430\u0430 \u0445\u044D\u0442\u044D\u0440\u043B\u044D\u044D: ${op}`, { op, timeout: true });
|
|
163
240
|
}
|
|
164
|
-
throw new NetworkError(`\u0421\u04AF\u043B\u0436\u044D\u044D\u043D\u0438\u0439 \u0430\u043B\u0434\u0430\u0430 (${
|
|
241
|
+
throw new NetworkError(`\u0421\u04AF\u043B\u0436\u044D\u044D\u043D\u0438\u0439 \u0430\u043B\u0434\u0430\u0430 (${op}): ${err.message.split(url).join(op)}`, { op, timeout: false });
|
|
165
242
|
}
|
|
166
243
|
if (res.status < 300) return res;
|
|
167
244
|
const text = await res.text();
|
|
168
245
|
const { error, code } = errorBody(text);
|
|
169
|
-
|
|
246
|
+
const headers = res.headers;
|
|
247
|
+
const retryAfterMs = parseRetryAfter(headers.get("Retry-After"));
|
|
248
|
+
if (res.status === 401) {
|
|
249
|
+
throw new AuthenticationError(`API secret \u0431\u0443\u0440\u0443\u0443 \u044D\u0441\u0432\u044D\u043B \u0431\u0430\u0439\u0445\u0433\u04AF\u0439 (401${code ? ` ${code}` : ""})`, { code, serverMessage: error, body: text, headers });
|
|
250
|
+
}
|
|
170
251
|
if (res.status === 403) {
|
|
171
252
|
throw new ForbiddenError(
|
|
172
253
|
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)",
|
|
173
|
-
code
|
|
254
|
+
code,
|
|
255
|
+
{ headers, retryAfterMs }
|
|
174
256
|
);
|
|
175
257
|
}
|
|
176
|
-
throw new ApiError(res.status, text, code);
|
|
258
|
+
throw new ApiError(res.status, text, code, { headers, retryAfterMs });
|
|
177
259
|
}
|
|
178
260
|
};
|
|
179
261
|
function errorBody(text) {
|
|
@@ -190,6 +272,48 @@ function errorBody(text) {
|
|
|
190
272
|
}
|
|
191
273
|
}
|
|
192
274
|
|
|
275
|
+
// src/contract.ts
|
|
276
|
+
var RP_ROUTES = {
|
|
277
|
+
authNotificationEtsi: { method: "POST", path: "/authentication/notification/etsi/{semanticsIdentifier}" },
|
|
278
|
+
authNotificationDocument: { method: "POST", path: "/authentication/notification/document/{documentNumber}" },
|
|
279
|
+
authDeviceLinkAnonymous: { method: "POST", path: "/authentication/device-link/anonymous" },
|
|
280
|
+
authDeviceLinkEtsi: { method: "POST", path: "/authentication/device-link/etsi/{semanticsIdentifier}" },
|
|
281
|
+
signNotificationEtsi: { method: "POST", path: "/signature/notification/etsi/{semanticsIdentifier}" },
|
|
282
|
+
signNotificationDocument: { method: "POST", path: "/signature/notification/document/{documentNumber}" },
|
|
283
|
+
pdfPrepare: { method: "POST", path: "/pdf/sign/prepare" },
|
|
284
|
+
pdfDocument: { method: "GET", path: "/pdf/sign/{sessionID}/document" },
|
|
285
|
+
orgRepresentations: { method: "GET", path: "/organization/representations/etsi/{personEtsi}" },
|
|
286
|
+
session: { method: "GET", path: "/session/{sessionId}" },
|
|
287
|
+
personCertificates: { method: "GET", path: "/certificates/etsi/{personEtsi}" },
|
|
288
|
+
personDevices: { method: "GET", path: "/devices/etsi/{personEtsi}" },
|
|
289
|
+
personActivity: { method: "GET", path: "/rp/activity/etsi/{personEtsi}" },
|
|
290
|
+
personSummary: { method: "GET", path: "/person/summary/etsi/{personEtsi}" },
|
|
291
|
+
personChildren: { method: "GET", path: "/person/children/etsi/{personEtsi}" },
|
|
292
|
+
tokenCertificate: { method: "POST", path: "/token/certificate" },
|
|
293
|
+
tokenCertificateResult: { method: "GET", path: "/token/certificate/{sessionID}" }
|
|
294
|
+
};
|
|
295
|
+
var RP_ROUTES_UNDISCOVERED = {
|
|
296
|
+
tokenAuthChallenge: { method: "POST", path: "/token/auth/challenge" },
|
|
297
|
+
tokenAuthVerify: { method: "POST", path: "/token/auth/verify" }
|
|
298
|
+
};
|
|
299
|
+
function routePath(route, ...args) {
|
|
300
|
+
let i = 0;
|
|
301
|
+
const out = route.path.replace(/\{[^}]*\}/g, () => {
|
|
302
|
+
const a = args[i++];
|
|
303
|
+
if (a === void 0) throw new Error(`eid: route ${route.path}: \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 \u0434\u0443\u0442\u0443\u0443`);
|
|
304
|
+
return encodeURIComponent(a);
|
|
305
|
+
});
|
|
306
|
+
if (i !== args.length) throw new Error(`eid: route ${route.path}: \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 \u0438\u043B\u04AF\u04AF`);
|
|
307
|
+
return out;
|
|
308
|
+
}
|
|
309
|
+
var OIDC_EXPECT = {
|
|
310
|
+
defaultScope: "openid",
|
|
311
|
+
pkceMethod: "S256",
|
|
312
|
+
idTokenAlg: "ES256",
|
|
313
|
+
/** `Authorization: Basic` (RFC 6749 §2.3.1). */
|
|
314
|
+
tokenEndpointAuthMethod: "client_secret_basic"
|
|
315
|
+
};
|
|
316
|
+
|
|
193
317
|
// src/crypto.ts
|
|
194
318
|
var import_node_crypto = require("crypto");
|
|
195
319
|
function sha256Base64(data) {
|
|
@@ -292,25 +416,25 @@ var AuthApi = class {
|
|
|
292
416
|
/** Push — РД/иргэний дугаар/ETSI-ээр (сервер төрлийг таьна). Иргэний утас руу мэдэгдэл очно. */
|
|
293
417
|
async notificationByEtsi(id, interactions, opts) {
|
|
294
418
|
const { body, acsp } = this.baseBody(randomChallenge(), interactions, ["Notification"], opts);
|
|
295
|
-
const raw = await this.http.post(
|
|
419
|
+
const raw = await this.http.post(routePath(RP_ROUTES.authNotificationEtsi, id), body);
|
|
296
420
|
return this.parseNotification(raw, acsp);
|
|
297
421
|
}
|
|
298
422
|
/** Push — баримтын дугаараар (тодорхой төхөөрөмж рүү). */
|
|
299
423
|
async notificationByDocument(documentNumber, interactions, opts) {
|
|
300
424
|
const { body, acsp } = this.baseBody(randomChallenge(), interactions, ["Notification"], opts);
|
|
301
|
-
const raw = await this.http.post(
|
|
425
|
+
const raw = await this.http.post(routePath(RP_ROUTES.authNotificationDocument, documentNumber), body);
|
|
302
426
|
return this.parseNotification(raw, acsp);
|
|
303
427
|
}
|
|
304
428
|
/** QR/App2App — нэр томьёололгүй (иргэн хэн болохыг урьдчилж мэдэхгүй). QR код үүсгэхэд. */
|
|
305
429
|
async deviceLinkAnonymous(interactions, opts) {
|
|
306
430
|
const { body, acsp } = this.baseBody(randomChallenge(), interactions, ["QR"], opts);
|
|
307
|
-
const raw = await this.http.post(
|
|
431
|
+
const raw = await this.http.post(routePath(RP_ROUTES.authDeviceLinkAnonymous), body);
|
|
308
432
|
return this.parseDeviceLink(raw, acsp);
|
|
309
433
|
}
|
|
310
434
|
/** QR/App2App — тодорхой иргэн (РД/civil_id/ETSI). */
|
|
311
435
|
async deviceLinkByEtsi(id, interactions, opts) {
|
|
312
436
|
const { body, acsp } = this.baseBody(randomChallenge(), interactions, ["QR"], opts);
|
|
313
|
-
const raw = await this.http.post(
|
|
437
|
+
const raw = await this.http.post(routePath(RP_ROUTES.authDeviceLinkEtsi, id), body);
|
|
314
438
|
return this.parseDeviceLink(raw, acsp);
|
|
315
439
|
}
|
|
316
440
|
};
|
|
@@ -343,6 +467,7 @@ var SignApi = class {
|
|
|
343
467
|
};
|
|
344
468
|
if (opts?.callbackUrl?.trim()) body.initialCallbackUrl = opts.callbackUrl.trim();
|
|
345
469
|
if (opts?.onBehalfOf?.trim()) body.onBehalfOf = opts.onBehalfOf.trim();
|
|
470
|
+
if (opts?.fileName?.trim()) body.fileName = opts.fileName.trim();
|
|
346
471
|
return body;
|
|
347
472
|
}
|
|
348
473
|
parse(raw) {
|
|
@@ -355,12 +480,12 @@ var SignApi = class {
|
|
|
355
480
|
* digest-ийг гаднаас бэлдэж өгнө — текст бус бинар баримтад тохиромжтой.
|
|
356
481
|
*/
|
|
357
482
|
async digestByEtsi(id, digestB64, interactions, opts) {
|
|
358
|
-
const raw = await this.http.post(
|
|
483
|
+
const raw = await this.http.post(routePath(RP_ROUTES.signNotificationEtsi, id), this.body(digestB64, interactions, opts));
|
|
359
484
|
return this.parse(raw);
|
|
360
485
|
}
|
|
361
486
|
/** Бэлэн digest-ээр push sign — баримтын дугаараар (auth-аас гарсан documentNumber дээр). */
|
|
362
487
|
async digestByDocument(documentNumber, digestB64, interactions, opts) {
|
|
363
|
-
const raw = await this.http.post(
|
|
488
|
+
const raw = await this.http.post(routePath(RP_ROUTES.signNotificationDocument, documentNumber), this.body(digestB64, interactions, opts));
|
|
364
489
|
return this.parse(raw);
|
|
365
490
|
}
|
|
366
491
|
/** Текстийн SHA-256 digest-ийг дотроо тооцоод push sign (богино текст баримтад тохиромжтой). */
|
|
@@ -401,9 +526,44 @@ function parseSessionResult(raw) {
|
|
|
401
526
|
signatureAlgorithmParameters: parseSigParams(sig.signatureAlgorithmParameters),
|
|
402
527
|
interactionTypeUsed: str(r.interactionTypeUsed),
|
|
403
528
|
onBehalfOf: parseOrg(r.onBehalfOf),
|
|
404
|
-
pdf: parsePdf(r.pdf)
|
|
529
|
+
pdf: parsePdf(r.pdf),
|
|
530
|
+
person: parsePerson(r.person),
|
|
531
|
+
tokenCertificate: parseTokenCert(r.tokenCertificate),
|
|
532
|
+
raw
|
|
405
533
|
};
|
|
406
534
|
}
|
|
535
|
+
var PERSON_STRINGS = [
|
|
536
|
+
"firstName",
|
|
537
|
+
"lastName",
|
|
538
|
+
"familyName",
|
|
539
|
+
"firstNameEn",
|
|
540
|
+
"lastNameEn",
|
|
541
|
+
"civilId",
|
|
542
|
+
"regNo",
|
|
543
|
+
"birthDate",
|
|
544
|
+
"gender"
|
|
545
|
+
];
|
|
546
|
+
function parsePerson(raw) {
|
|
547
|
+
const p = asRecord(raw);
|
|
548
|
+
const out = {};
|
|
549
|
+
for (const k of PERSON_STRINGS) if (typeof p[k] === "string" && p[k]) out[k] = p[k];
|
|
550
|
+
if (typeof p.geID === "number" && p.geID !== 0) out.geID = p.geID;
|
|
551
|
+
return Object.keys(out).length ? out : null;
|
|
552
|
+
}
|
|
553
|
+
function parseTokenCert(raw) {
|
|
554
|
+
const t = asRecord(raw);
|
|
555
|
+
const status = str(t.status);
|
|
556
|
+
if (!status) return null;
|
|
557
|
+
const out = { status };
|
|
558
|
+
if (t.errorCode) out.errorCode = String(t.errorCode);
|
|
559
|
+
if (t.authSerialHex) out.authSerialHex = String(t.authSerialHex);
|
|
560
|
+
if (t.signSerialHex) out.signSerialHex = String(t.signSerialHex);
|
|
561
|
+
return out;
|
|
562
|
+
}
|
|
563
|
+
function personMatchesCertificate(person, subject) {
|
|
564
|
+
const serial = typeof subject === "string" ? subject : subject?.serialNumber;
|
|
565
|
+
return !!person?.civilId && !!serial && serial === `PNOMN-${person.civilId}`;
|
|
566
|
+
}
|
|
407
567
|
function parsePdf(raw) {
|
|
408
568
|
const p = asRecord(raw);
|
|
409
569
|
const docID = str(p.docID);
|
|
@@ -457,7 +617,7 @@ var SessionApi = class {
|
|
|
457
617
|
/** Нэг удаагийн long-poll. serverPollMs хүртэл хүлээнэ. */
|
|
458
618
|
async poll(sessionId, serverPollMs = SERVER_POLL_MS) {
|
|
459
619
|
const raw = await this.http.get(
|
|
460
|
-
|
|
620
|
+
`${routePath(RP_ROUTES.session, sessionId)}?timeoutMs=${serverPollMs}`,
|
|
461
621
|
serverPollMs + 1e4
|
|
462
622
|
// HTTP timeout нь серверийн poll-оос урт
|
|
463
623
|
);
|
|
@@ -482,6 +642,7 @@ var SessionApi = class {
|
|
|
482
642
|
|
|
483
643
|
// src/pdf.ts
|
|
484
644
|
var import_node_crypto2 = require("crypto");
|
|
645
|
+
var DEFAULT_PDF_TIMEOUT_MS = 8e4;
|
|
485
646
|
function rec3(v) {
|
|
486
647
|
return v && typeof v === "object" ? v : {};
|
|
487
648
|
}
|
|
@@ -494,7 +655,10 @@ var PdfApi = class {
|
|
|
494
655
|
http;
|
|
495
656
|
creds;
|
|
496
657
|
defaults;
|
|
497
|
-
/**
|
|
658
|
+
/**
|
|
659
|
+
* POST /pdf/sign/prepare — multipart (`request` JSON + `pdf`). SIGN эрх шаардана. Түр алдаа: `ApiError` 429
|
|
660
|
+
* PDF_PREPARE_BUSY, 503 LTV_UNAVAILABLE / TSA_UNAVAILABLE — `retryAfterMs`-ийн дараа дахин (`retryable`).
|
|
661
|
+
*/
|
|
498
662
|
async prepare(input) {
|
|
499
663
|
const request = {
|
|
500
664
|
relyingPartyUUID: this.creds.rpUUID,
|
|
@@ -510,7 +674,7 @@ var PdfApi = class {
|
|
|
510
674
|
const form = new FormData();
|
|
511
675
|
form.append("request", new Blob([JSON.stringify(request)], { type: "application/json" }));
|
|
512
676
|
form.append("pdf", new Blob([input.pdf], { type: "application/pdf" }), input.fileName);
|
|
513
|
-
const r = rec3(await this.http.postMultipart(
|
|
677
|
+
const r = rec3(await this.http.postMultipart(routePath(RP_ROUTES.pdfPrepare), form, this.defaults.timeoutMs ?? DEFAULT_PDF_TIMEOUT_MS));
|
|
514
678
|
const receivedAt = Date.now();
|
|
515
679
|
const vc = rec3(r.vc).value;
|
|
516
680
|
const dl = rec3(r.deviceLink);
|
|
@@ -532,10 +696,12 @@ var PdfApi = class {
|
|
|
532
696
|
}
|
|
533
697
|
/**
|
|
534
698
|
* GET /pdf/sign/{sessionID}/document — гарын үсэгтэй PDF (READY-ээс хойш 30 мин). Татсан байтын SHA-256-ийг
|
|
535
|
-
* session-ий `pdf.outSha256`-тай тулга. Эрх хасагдсан бол `ForbiddenError` `.code = "REPRESENTATION_REVOKED"`.
|
|
699
|
+
* session-ий `pdf.outSha256`-тай тулга. Эрх хасагдсан бол `ForbiddenError` `.code = "REPRESENTATION_REVOKED"`. Баримт
|
|
700
|
+
* бэлэн болоогүй бол `ApiError` 503 `.code` DOCUMENT_PENDING / TSA_UNAVAILABLE / LTV_UNAVAILABLE — `retryAfterMs`-ийн
|
|
701
|
+
* дараа дахин татна (`retryable`).
|
|
536
702
|
*/
|
|
537
703
|
document(sessionId) {
|
|
538
|
-
return this.http.getBytes(
|
|
704
|
+
return this.http.getBytes(routePath(RP_ROUTES.pdfDocument, sessionId), this.defaults.timeoutMs ?? DEFAULT_PDF_TIMEOUT_MS);
|
|
539
705
|
}
|
|
540
706
|
/**
|
|
541
707
|
* {@link document} + тулгалт: татсан байтын SHA-256 нь session poll-ийн `pdf.outSha256`-тай тэнцүү байх ёстой.
|
|
@@ -567,13 +733,53 @@ var OrganizationApi = class {
|
|
|
567
733
|
http;
|
|
568
734
|
/** GET /organization/representations/etsi/{personEtsi}. Сонголт нь итгэх эх биш — сервер дахин шалгана. */
|
|
569
735
|
async getRepresentations(personEtsi) {
|
|
570
|
-
const raw = await this.http.get(
|
|
736
|
+
const raw = await this.http.get(routePath(RP_ROUTES.orgRepresentations, personEtsi));
|
|
571
737
|
const list = Array.isArray(raw.representations) ? raw.representations : [];
|
|
572
738
|
return { personEtsi: String(raw.personEtsi ?? personEtsi), representations: list };
|
|
573
739
|
}
|
|
574
740
|
};
|
|
575
741
|
|
|
576
|
-
// src/
|
|
742
|
+
// src/person.ts
|
|
743
|
+
function rec4(v) {
|
|
744
|
+
return v && typeof v === "object" ? v : {};
|
|
745
|
+
}
|
|
746
|
+
var PersonApi = class {
|
|
747
|
+
constructor(http) {
|
|
748
|
+
this.http = http;
|
|
749
|
+
}
|
|
750
|
+
http;
|
|
751
|
+
async get(path) {
|
|
752
|
+
const raw = await this.http.get(path);
|
|
753
|
+
return { ...rec4(raw), raw };
|
|
754
|
+
}
|
|
755
|
+
/** Иргэний бүх гэрчилгээ + статусын тоолол. */
|
|
756
|
+
certificates(personEtsi) {
|
|
757
|
+
return this.get(routePath(RP_ROUTES.personCertificates, personEtsi));
|
|
758
|
+
}
|
|
759
|
+
/** Иргэний холбоотой төхөөрөмжүүд (идэвхтэй + идэвхгүй). */
|
|
760
|
+
devices(personEtsi) {
|
|
761
|
+
return this.get(routePath(RP_ROUTES.personDevices, personEtsi));
|
|
762
|
+
}
|
|
763
|
+
/** Иргэний үйл ажиллагааны түүх. */
|
|
764
|
+
activity(personEtsi, query) {
|
|
765
|
+
const q = new URLSearchParams();
|
|
766
|
+
if (query?.flow) q.set("flow", query.flow);
|
|
767
|
+
if (query?.limit && query.limit > 0) q.set("limit", String(query.limit));
|
|
768
|
+
if (query?.offset && query.offset > 0) q.set("offset", String(query.offset));
|
|
769
|
+
const qs = q.toString();
|
|
770
|
+
return this.get(routePath(RP_ROUTES.personActivity, personEtsi) + (qs ? `?${qs}` : ""));
|
|
771
|
+
}
|
|
772
|
+
/** Самбарын нэгдсэн тоо. */
|
|
773
|
+
summary(personEtsi) {
|
|
774
|
+
return this.get(routePath(RP_ROUTES.personSummary, personEtsi));
|
|
775
|
+
}
|
|
776
|
+
/** Асран хамгаалж буй хүүхдүүд (зөвхөн унших). */
|
|
777
|
+
children(personEtsi) {
|
|
778
|
+
return this.get(routePath(RP_ROUTES.personChildren, personEtsi));
|
|
779
|
+
}
|
|
780
|
+
};
|
|
781
|
+
|
|
782
|
+
// src/token.ts
|
|
577
783
|
var import_node_crypto4 = require("crypto");
|
|
578
784
|
|
|
579
785
|
// src/subject.ts
|
|
@@ -680,8 +886,207 @@ function parseSubject(cert) {
|
|
|
680
886
|
out.name = full || out.commonName || null;
|
|
681
887
|
return out;
|
|
682
888
|
}
|
|
889
|
+
function certExtension(der, oid) {
|
|
890
|
+
const top = tlv(der, 0, der.length);
|
|
891
|
+
const tbs = tlv(der, top.start, top.end);
|
|
892
|
+
let f = tlv(der, tbs.start, tbs.end);
|
|
893
|
+
if (f.tag === 160) f = tlv(der, f.end, tbs.end);
|
|
894
|
+
for (let k = 0; k < 5; k++) f = tlv(der, f.end, tbs.end);
|
|
895
|
+
for (let pos = f.end; pos < tbs.end; ) {
|
|
896
|
+
const t = tlv(der, pos, tbs.end);
|
|
897
|
+
if (t.tag === 163) {
|
|
898
|
+
const seq = tlv(der, t.start, t.end);
|
|
899
|
+
for (let ep = seq.start; ep < seq.end; ) {
|
|
900
|
+
const ext = tlv(der, ep, seq.end);
|
|
901
|
+
const id = tlv(der, ext.start, ext.end);
|
|
902
|
+
let v = tlv(der, id.end, ext.end);
|
|
903
|
+
if (v.tag === 1) v = tlv(der, v.end, ext.end);
|
|
904
|
+
if (v.tag !== 4) throw new ValidationError("extension: extnValue OCTET STRING \u0431\u0438\u0448");
|
|
905
|
+
if (oidOf(der, id) === oid) return der.subarray(v.start, v.end);
|
|
906
|
+
ep = ext.end;
|
|
907
|
+
}
|
|
908
|
+
return void 0;
|
|
909
|
+
}
|
|
910
|
+
pos = t.end;
|
|
911
|
+
}
|
|
912
|
+
return void 0;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
// src/token.ts
|
|
916
|
+
function rec5(v) {
|
|
917
|
+
return v && typeof v === "object" ? v : {};
|
|
918
|
+
}
|
|
919
|
+
function hexSha256Equal(hex, data) {
|
|
920
|
+
if (typeof hex !== "string" || !/^[0-9a-fA-F]{64}$/.test(hex)) return false;
|
|
921
|
+
return (0, import_node_crypto4.timingSafeEqual)((0, import_node_crypto4.createHash)("sha256").update(data).digest(), Buffer.from(hex, "hex"));
|
|
922
|
+
}
|
|
923
|
+
var TOKEN_RESULT_TIMEOUT_MS = 3e4;
|
|
924
|
+
var TokenApi = class {
|
|
925
|
+
constructor(http, creds) {
|
|
926
|
+
this.http = http;
|
|
927
|
+
this.creds = creds;
|
|
928
|
+
}
|
|
929
|
+
http;
|
|
930
|
+
creds;
|
|
931
|
+
/**
|
|
932
|
+
* POST /token/certificate — токены AUTH + SIGN гэрчилгээ олголт эхлүүлнэ (иргэн утсаараа батална). CA-ийн буцаасан
|
|
933
|
+
* CSR hash-ууд илгээсэнтэй таарахгүй бол ValidationError.
|
|
934
|
+
*/
|
|
935
|
+
async issueCertificate(input) {
|
|
936
|
+
if (!input.signerEtsi?.trim() || !input.csrAuth?.length || !input.csrSign?.length || !input.tokenSerial?.trim()) {
|
|
937
|
+
throw new Error("token: signerEtsi, csrAuth, csrSign, tokenSerial \u0448\u0430\u0430\u0440\u0434\u043B\u0430\u0433\u0430\u0442\u0430\u0439");
|
|
938
|
+
}
|
|
939
|
+
const body = {
|
|
940
|
+
relyingPartyUUID: this.creds.rpUUID,
|
|
941
|
+
relyingPartyName: this.creds.rpName,
|
|
942
|
+
signer: { etsi: input.signerEtsi.trim() },
|
|
943
|
+
csrAuth: Buffer.from(input.csrAuth).toString("base64"),
|
|
944
|
+
csrSign: Buffer.from(input.csrSign).toString("base64"),
|
|
945
|
+
tokenSerial: input.tokenSerial.trim(),
|
|
946
|
+
onBehalfOf: input.onBehalfOf?.trim() ?? "",
|
|
947
|
+
flow: "notification"
|
|
948
|
+
};
|
|
949
|
+
if (input.tokenLabel) body.tokenLabel = input.tokenLabel;
|
|
950
|
+
const r = rec5(await this.http.post(routePath(RP_ROUTES.tokenCertificate), body));
|
|
951
|
+
if (!hexSha256Equal(r.csrAuthSha256, input.csrAuth) || !hexSha256Equal(r.csrSignSha256, input.csrSign)) {
|
|
952
|
+
throw new ValidationError("token: CA-\u0438\u0439\u043D csrAuthSha256/csrSignSha256 \u0438\u043B\u0433\u044D\u044D\u0441\u044D\u043D CSR-\u0442\u044D\u0439 \u0442\u0430\u0430\u0440\u0441\u0430\u043D\u0433\u04AF\u0439");
|
|
953
|
+
}
|
|
954
|
+
const vc = rec5(r.vc).value ?? (typeof r.vc === "string" ? r.vc : void 0);
|
|
955
|
+
return {
|
|
956
|
+
sessionId: String(r.sessionID ?? ""),
|
|
957
|
+
vc: vc != null ? String(vc) : null,
|
|
958
|
+
csrAuthSha256: String(r.csrAuthSha256).toLowerCase(),
|
|
959
|
+
csrSignSha256: String(r.csrSignSha256).toLowerCase(),
|
|
960
|
+
...typeof r.expiresAt === "string" ? { expiresAt: r.expiresAt } : {}
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* GET /token/certificate/{sessionID} — олгосон AUTH + SIGN гэрчилгээ + гинж. Токенд бичихээс өмнө
|
|
965
|
+
* {@link verifyTokenCertificates}-ээр CSR-ийн түлхүүртэй тулга.
|
|
966
|
+
*/
|
|
967
|
+
async certificateResult(sessionId) {
|
|
968
|
+
const raw = await this.http.get(routePath(RP_ROUTES.tokenCertificateResult, sessionId), TOKEN_RESULT_TIMEOUT_MS);
|
|
969
|
+
const r = rec5(raw);
|
|
970
|
+
const item = (v) => v && typeof v === "object" ? v : null;
|
|
971
|
+
return {
|
|
972
|
+
status: String(r.status ?? ""),
|
|
973
|
+
auth: item(r.auth),
|
|
974
|
+
sign: item(r.sign),
|
|
975
|
+
chain: Array.isArray(r.chain) ? r.chain.map(String) : [],
|
|
976
|
+
chainPem: String(r.chainPem ?? ""),
|
|
977
|
+
organization: r.organization && typeof r.organization === "object" ? r.organization : null,
|
|
978
|
+
keyMedium: String(r.keyMedium ?? ""),
|
|
979
|
+
certificateLevel: String(r.certificateLevel ?? ""),
|
|
980
|
+
raw
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
/** POST /token/auth/challenge — энэ RP-д холбогдсон нэг удаагийн challenge (2 мин). */
|
|
984
|
+
async authChallenge() {
|
|
985
|
+
const r = rec5(
|
|
986
|
+
await this.http.post(routePath(RP_ROUTES_UNDISCOVERED.tokenAuthChallenge), {
|
|
987
|
+
relyingPartyUUID: this.creds.rpUUID,
|
|
988
|
+
relyingPartyName: this.creds.rpName
|
|
989
|
+
})
|
|
990
|
+
);
|
|
991
|
+
return {
|
|
992
|
+
challengeId: String(r.challengeId ?? ""),
|
|
993
|
+
challenge: String(r.challenge ?? ""),
|
|
994
|
+
rpUUID: String(r.rpUUID ?? ""),
|
|
995
|
+
expiresAt: String(r.expiresAt ?? "")
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* POST /token/auth/verify — токены AUTH гэрчилгээ (DER) + challenge дээрх түүхий r||s. CA гинж, төлөв, цуцлалт, гарын
|
|
1000
|
+
* үсгийг шалгана; SDK нэмж endResult === OK ба identity.etsi === гэрчилгээний SERIALNUMBER-ийг тулгана (эс бөгөөс
|
|
1001
|
+
* ValidationError). Challenge нэг удаагийн — алдааны дараа шинээр авна.
|
|
1002
|
+
*/
|
|
1003
|
+
async authVerify(challengeId, certDer, signature) {
|
|
1004
|
+
let serial;
|
|
1005
|
+
try {
|
|
1006
|
+
serial = parseSubject(new import_node_crypto4.X509Certificate(certDer)).serialNumber;
|
|
1007
|
+
} catch {
|
|
1008
|
+
throw new Error("token auth: \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D \u0437\u0430\u0434\u0430\u0440\u0441\u0430\u043D\u0433\u04AF\u0439");
|
|
1009
|
+
}
|
|
1010
|
+
if (!challengeId || !signature?.length) throw new Error("token auth: challengeId \u0431\u0430 signature \u0448\u0430\u0430\u0440\u0434\u043B\u0430\u0433\u0430\u0442\u0430\u0439");
|
|
1011
|
+
const raw = await this.http.post(routePath(RP_ROUTES_UNDISCOVERED.tokenAuthVerify), {
|
|
1012
|
+
challengeId,
|
|
1013
|
+
certificate: Buffer.from(certDer).toString("base64"),
|
|
1014
|
+
signature: Buffer.from(signature).toString("base64")
|
|
1015
|
+
});
|
|
1016
|
+
const r = rec5(raw);
|
|
1017
|
+
if (rec5(r.result).endResult !== "OK") throw new ValidationError("token auth: endResult OK \u0431\u0438\u0448");
|
|
1018
|
+
const id = rec5(r.identity);
|
|
1019
|
+
if (!id.etsi || id.etsi !== serial) throw new ValidationError("token auth: identity.etsi \u043D\u044C \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D\u043D\u0438\u0439 SERIALNUMBER \u0431\u0438\u0448");
|
|
1020
|
+
const org = rec5(r.organization);
|
|
1021
|
+
const cert = rec5(r.cert);
|
|
1022
|
+
return {
|
|
1023
|
+
identity: { etsi: String(id.etsi), givenName: String(id.givenName ?? ""), surname: String(id.surname ?? "") },
|
|
1024
|
+
serialHex: String(cert.serialHex ?? ""),
|
|
1025
|
+
notAfter: String(cert.notAfter ?? ""),
|
|
1026
|
+
organization: org.etsi ? { etsi: String(org.etsi), name: String(org.name ?? "") } : null,
|
|
1027
|
+
raw
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
};
|
|
1031
|
+
function csrSpki(csr) {
|
|
1032
|
+
const top = tlv(csr, 0, csr.length);
|
|
1033
|
+
const info = tlv(csr, top.start, top.end);
|
|
1034
|
+
const version = tlv(csr, info.start, info.end);
|
|
1035
|
+
const subject = tlv(csr, version.end, info.end);
|
|
1036
|
+
const spki = tlv(csr, subject.end, info.end);
|
|
1037
|
+
return (0, import_node_crypto4.createPublicKey)({ key: Buffer.from(csr.subarray(subject.end, spki.end)), format: "der", type: "spki" }).export({
|
|
1038
|
+
format: "der",
|
|
1039
|
+
type: "spki"
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
function verifyTokenCertificates(result, csrAuth, csrSign) {
|
|
1043
|
+
if (!result?.auth || !result.sign) throw new ValidationError("token: auth/sign \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D \u0430\u043B\u0433\u0430");
|
|
1044
|
+
for (const [name, item, csr] of [
|
|
1045
|
+
["auth", result.auth, csrAuth],
|
|
1046
|
+
["sign", result.sign, csrSign]
|
|
1047
|
+
]) {
|
|
1048
|
+
let want;
|
|
1049
|
+
try {
|
|
1050
|
+
want = csrSpki(csr);
|
|
1051
|
+
} catch {
|
|
1052
|
+
throw new Error(`token: ${name} CSR \u0437\u0430\u0434\u0430\u0440\u0441\u0430\u043D\u0433\u04AF\u0439`);
|
|
1053
|
+
}
|
|
1054
|
+
let got;
|
|
1055
|
+
try {
|
|
1056
|
+
got = new import_node_crypto4.X509Certificate(Buffer.from(item.certificate, "base64")).publicKey.export({ format: "der", type: "spki" });
|
|
1057
|
+
} catch {
|
|
1058
|
+
throw new ValidationError(`token: ${name} \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D \u0437\u0430\u0434\u0430\u0440\u0441\u0430\u043D\u0433\u04AF\u0439`);
|
|
1059
|
+
}
|
|
1060
|
+
if (!got.equals(want)) throw new ValidationError(`token: ${name} \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D\u043D\u0438\u0439 \u0442\u04AF\u043B\u0445\u04AF\u04AF\u0440 CSR-\u0438\u0439\u043D\u0445\u0442\u044D\u0439 \u0442\u0430\u0430\u0440\u0441\u0430\u043D\u0433\u04AF\u0439`);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
function tokenAuthDigest(rpUUID, challengeB64) {
|
|
1064
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(rpUUID)) {
|
|
1065
|
+
throw new Error("token auth: rpUUID \u0436\u0438\u0436\u0438\u0433 \u04AF\u0441\u0433\u0438\u0439\u043D 36 \u0442\u044D\u043C\u0434\u044D\u0433\u0442 UUID \u0431\u0438\u0448");
|
|
1066
|
+
}
|
|
1067
|
+
const ch = Buffer.from(challengeB64, "base64");
|
|
1068
|
+
if (ch.length !== 32 || ch.toString("base64") !== challengeB64) throw new Error("token auth: challenge 32 \u0431\u0430\u0439\u0442 base64 \u0431\u0438\u0448");
|
|
1069
|
+
return (0, import_node_crypto4.createHash)("sha256").update("eID-TOKEN-AUTH", "ascii").update(rpUUID, "ascii").update(ch).digest();
|
|
1070
|
+
}
|
|
1071
|
+
function tokenLimitOf(err) {
|
|
1072
|
+
if (!(err instanceof ApiError) || err.code !== "TOKEN_LIMIT_REACHED") return void 0;
|
|
1073
|
+
let b;
|
|
1074
|
+
try {
|
|
1075
|
+
b = rec5(JSON.parse(err.body));
|
|
1076
|
+
} catch {
|
|
1077
|
+
return void 0;
|
|
1078
|
+
}
|
|
1079
|
+
if (typeof b.limit !== "number" || !Number.isInteger(b.limit) || b.limit < 0) return void 0;
|
|
1080
|
+
if (b.kind === "PERSONAL") return { kind: "PERSONAL", limit: b.limit };
|
|
1081
|
+
if (b.kind === "ORGANIZATION" && typeof b.organization === "string" && b.organization.startsWith("NTRMN-")) {
|
|
1082
|
+
return { kind: "ORGANIZATION", limit: b.limit, organization: b.organization };
|
|
1083
|
+
}
|
|
1084
|
+
return void 0;
|
|
1085
|
+
}
|
|
683
1086
|
|
|
684
1087
|
// src/validator.ts
|
|
1088
|
+
var import_node_crypto5 = require("crypto");
|
|
1089
|
+
var CITIZEN_ISSUING_CA_SKI = "b8a37f2c64ad677912db7d9e34f246358a2fb909";
|
|
685
1090
|
var ResponseValidator = class {
|
|
686
1091
|
anchors;
|
|
687
1092
|
intermediates;
|
|
@@ -690,14 +1095,28 @@ var ResponseValidator = class {
|
|
|
690
1095
|
allowUntrusted;
|
|
691
1096
|
revocation;
|
|
692
1097
|
revocationMode;
|
|
1098
|
+
allowedIssuers;
|
|
1099
|
+
checkKeyUsage;
|
|
1100
|
+
schemeName;
|
|
693
1101
|
constructor(trust = {}) {
|
|
694
|
-
this.anchors = (trust.trustAnchorsPem ?? []).map((p) => new
|
|
695
|
-
this.intermediates = (trust.intermediatesPem ?? []).map((p) => new
|
|
1102
|
+
this.anchors = (trust.trustAnchorsPem ?? []).map((p) => new import_node_crypto5.X509Certificate(p));
|
|
1103
|
+
this.intermediates = (trust.intermediatesPem ?? []).map((p) => new import_node_crypto5.X509Certificate(p));
|
|
696
1104
|
this.requiredLevel = trust.requiredLevel ?? "QUALIFIED";
|
|
697
1105
|
this.skew = trust.clockSkewMs ?? 0;
|
|
698
1106
|
this.allowUntrusted = trust.allowUntrusted ?? false;
|
|
699
1107
|
this.revocation = trust.revocation;
|
|
700
1108
|
this.revocationMode = trust.revocationMode ?? "hard-fail";
|
|
1109
|
+
if (trust.allowedIssuerSkis?.length) {
|
|
1110
|
+
this.allowedIssuers = new Set(
|
|
1111
|
+
trust.allowedIssuerSkis.map((s) => {
|
|
1112
|
+
const ski = s.trim().replace(/:/g, "").toLowerCase();
|
|
1113
|
+
if (!/^(?:[0-9a-f]{2})+$/.test(ski)) throw new Error(`ResponseValidator: allowedIssuerSkis: "${s}" hex key identifier \u0431\u0438\u0448`);
|
|
1114
|
+
return ski;
|
|
1115
|
+
})
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
1118
|
+
this.checkKeyUsage = trust.checkKeyUsage ?? false;
|
|
1119
|
+
this.schemeName = trust.schemeName;
|
|
701
1120
|
}
|
|
702
1121
|
// ── нийтийн API ──
|
|
703
1122
|
/**
|
|
@@ -725,6 +1144,7 @@ var ResponseValidator = class {
|
|
|
725
1144
|
if (!interactionTypeUsed) throw new ValidationError("interactionTypeUsed \u0430\u043B\u0433\u0430");
|
|
726
1145
|
const { alg, hash } = this.algorithmOf(result, leaf, acsp.signatureAlgorithm, acsp.hashAlgorithm);
|
|
727
1146
|
const payload = buildAcspV2Payload({
|
|
1147
|
+
...this.schemeName ? { schemeName: this.schemeName } : {},
|
|
728
1148
|
serverRandom,
|
|
729
1149
|
rpChallenge: acsp.rpChallenge,
|
|
730
1150
|
userChallenge,
|
|
@@ -748,10 +1168,14 @@ var ResponseValidator = class {
|
|
|
748
1168
|
}
|
|
749
1169
|
/**
|
|
750
1170
|
* Sign session-ийн хариуг бүрэн шалгаж, баталгаажсан гарын үсгийг буцаана.
|
|
751
|
-
* @param
|
|
1171
|
+
* @param opts — `hashAlgorithm` мөр (хуучин хэлбэр; default SHA-256) эсвэл {@link ValidateSignOptions}.
|
|
752
1172
|
*/
|
|
753
|
-
validateSign(result, digestB64,
|
|
1173
|
+
validateSign(result, digestB64, opts = {}) {
|
|
1174
|
+
const { hashAlgorithm = "SHA-256", expectedFlowType } = typeof opts === "string" ? { hashAlgorithm: opts } : opts;
|
|
754
1175
|
const leaf = this.precheck(result, "RAW_DIGEST_SIGNATURE");
|
|
1176
|
+
if (expectedFlowType && result.flowType !== expectedFlowType) {
|
|
1177
|
+
throw new ValidationError(`flowType ${result.flowType} \u2260 \u044D\u0445\u043B\u04AF\u04AF\u043B\u0441\u044D\u043D \u0443\u0440\u0441\u0433\u0430\u043B ${expectedFlowType} (relay \u0431\u0430\u0439\u0436 \u0431\u043E\u043B\u0437\u043E\u0448\u0433\u04AF\u0439)`);
|
|
1178
|
+
}
|
|
755
1179
|
const digest = Buffer.from(digestB64, "base64");
|
|
756
1180
|
const { alg, hash } = this.algorithmOf(result, leaf, null, hashAlgorithm);
|
|
757
1181
|
if (digest.length !== hashLen(hash)) throw new ValidationError("digest \u0443\u0440\u0442 hashAlgorithm-\u0442\u0430\u0439 \u0442\u0430\u0430\u0440\u0430\u0445\u0433\u04AF\u0439");
|
|
@@ -824,9 +1248,10 @@ var ResponseValidator = class {
|
|
|
824
1248
|
if (!result.certificateDerB64) throw new ValidationError("\u0425\u0430\u0440\u0438\u0443\u043D\u0434 \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D \u0430\u043B\u0433\u0430");
|
|
825
1249
|
if (!result.signatureValueB64) throw new ValidationError("\u0425\u0430\u0440\u0438\u0443\u043D\u0434 \u0433\u0430\u0440\u044B\u043D \u04AF\u0441\u044D\u0433 \u0430\u043B\u0433\u0430");
|
|
826
1250
|
if (!result.documentNumber) throw new ValidationError("\u0425\u0430\u0440\u0438\u0443\u043D\u0434 documentNumber \u0430\u043B\u0433\u0430");
|
|
827
|
-
const leaf = new
|
|
1251
|
+
const leaf = new import_node_crypto5.X509Certificate(Buffer.from(result.certificateDerB64, "base64"));
|
|
828
1252
|
this.checkValidity(leaf);
|
|
829
1253
|
this.checkLevel(String(result.certificateLevel ?? ""));
|
|
1254
|
+
this.checkPurpose(leaf, protocol);
|
|
830
1255
|
if (this.anchors.length === 0) {
|
|
831
1256
|
if (!this.allowUntrusted) {
|
|
832
1257
|
throw new ValidationError(
|
|
@@ -836,8 +1261,35 @@ var ResponseValidator = class {
|
|
|
836
1261
|
} else {
|
|
837
1262
|
this.verifyChain(leaf);
|
|
838
1263
|
}
|
|
1264
|
+
this.checkIssuer(leaf);
|
|
839
1265
|
return leaf;
|
|
840
1266
|
}
|
|
1267
|
+
/**
|
|
1268
|
+
* checkKeyUsage үед гэрчилгээний зориулалт протоколд тохирох эсэх (CA crypto/ca_profile.go): ACSP_V2 (auth) → KU
|
|
1269
|
+
* digitalSignature + EKU clientAuth (эсвэл anyExtendedKeyUsage); RAW_DIGEST_SIGNATURE (sign) → KU contentCommitment.
|
|
1270
|
+
*/
|
|
1271
|
+
checkPurpose(leaf, protocol) {
|
|
1272
|
+
if (!this.checkKeyUsage) return;
|
|
1273
|
+
const ku = keyUsageBits(leaf);
|
|
1274
|
+
if (protocol === "ACSP_V2") {
|
|
1275
|
+
if (!(ku & 128)) throw new ValidationError("\u041D\u044D\u0432\u0442\u0440\u044D\u043B\u0442\u0438\u0439\u043D \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D\u043D\u0434 KeyUsage digitalSignature \u0430\u043B\u0433\u0430");
|
|
1276
|
+
const eku = extKeyUsage(leaf);
|
|
1277
|
+
if (!eku.includes("1.3.6.1.5.5.7.3.2") && !eku.includes("2.5.29.37.0")) {
|
|
1278
|
+
throw new ValidationError("\u041D\u044D\u0432\u0442\u0440\u044D\u043B\u0442\u0438\u0439\u043D \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D\u043D\u0434 ExtKeyUsage clientAuth \u0430\u043B\u0433\u0430 (\u0433\u0430\u0440\u044B\u043D \u04AF\u0441\u0433\u0438\u0439\u043D \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D \u0431\u0430\u0439\u0436 \u0431\u043E\u043B\u0437\u043E\u0448\u0433\u04AF\u0439)");
|
|
1279
|
+
}
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
if (!(ku & 64)) throw new ValidationError("\u0413\u0430\u0440\u044B\u043D \u04AF\u0441\u0433\u0438\u0439\u043D \u0433\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D\u043D\u0434 KeyUsage nonRepudiation (contentCommitment) \u0430\u043B\u0433\u0430");
|
|
1283
|
+
}
|
|
1284
|
+
/** allowedIssuerSkis үед leaf-ийн шууд issuer-ийн SKI (issuer-т алга бол leaf-ийн AKI) жагсаалтад байх ёстой. */
|
|
1285
|
+
checkIssuer(leaf) {
|
|
1286
|
+
if (!this.allowedIssuers) return;
|
|
1287
|
+
const issuer = this.findIssuer(leaf);
|
|
1288
|
+
const ski = (issuer && subjectKeyId(issuer)) ?? authorityKeyId(leaf);
|
|
1289
|
+
if (!ski || !this.allowedIssuers.has(ski)) {
|
|
1290
|
+
throw new ValidationError("\u0413\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D\u0433 \u0433\u0430\u0440\u0433\u0430\u0441\u0430\u043D CA \u0437\u04E9\u0432\u0448\u04E9\u04E9\u0440\u04E9\u0433\u0434\u0441\u04E9\u043D \u0438\u0440\u0433\u044D\u043D\u0438\u0439 issuer \u0431\u0438\u0448 (allowedIssuerSkis)");
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
841
1293
|
checkValidity(cert) {
|
|
842
1294
|
const now = Date.now();
|
|
843
1295
|
const from = Date.parse(cert.validFrom);
|
|
@@ -880,14 +1332,23 @@ var ResponseValidator = class {
|
|
|
880
1332
|
* дуудна (тэдгээр нь sync; revocation нь сүлжээ I/O тул async).
|
|
881
1333
|
*/
|
|
882
1334
|
async checkRevocation(cert) {
|
|
883
|
-
|
|
1335
|
+
await this.checkRevocationStatus(cert);
|
|
1336
|
+
}
|
|
1337
|
+
/**
|
|
1338
|
+
* {@link checkRevocation} + юу шийдсэнийг буцаана (лог/аудитад): "good" | "unchecked" (unknown, soft-fail) |
|
|
1339
|
+
* "skipped" (hook алга). "revoked", мөн hard-fail горимд "unknown" → ValidationError; hook-ийн алдаа дамжина.
|
|
1340
|
+
*/
|
|
1341
|
+
async checkRevocationStatus(cert) {
|
|
1342
|
+
if (!this.revocation) return "skipped";
|
|
884
1343
|
const status = await this.revocation(cert, this.findIssuer(cert));
|
|
1344
|
+
if (status === "good") return "good";
|
|
885
1345
|
if (status === "revoked") {
|
|
886
1346
|
throw new ValidationError("\u0413\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D \u0446\u0443\u0446\u043B\u0430\u0433\u0434\u0441\u0430\u043D (revoked)");
|
|
887
1347
|
}
|
|
888
|
-
if (
|
|
1348
|
+
if (this.revocationMode === "hard-fail") {
|
|
889
1349
|
throw new ValidationError("\u0413\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D\u043D\u0438\u0439 revocation \u0442\u04E9\u043B\u04E9\u0432 \u0442\u043E\u0433\u0442\u043E\u043E\u0433\u0434\u0441\u043E\u043D\u0433\u04AF\u0439 (hard-fail)");
|
|
890
1350
|
}
|
|
1351
|
+
return "unchecked";
|
|
891
1352
|
}
|
|
892
1353
|
};
|
|
893
1354
|
function signedCallbackUrl(fromResponse, rpCallback, flowType) {
|
|
@@ -902,16 +1363,16 @@ function signedCallbackUrl(fromResponse, rpCallback, flowType) {
|
|
|
902
1363
|
return fromResponse;
|
|
903
1364
|
}
|
|
904
1365
|
function userChallengeOf(verifier) {
|
|
905
|
-
return (0,
|
|
1366
|
+
return (0, import_node_crypto5.createHash)("sha256").update(verifier, "utf8").digest("base64url");
|
|
906
1367
|
}
|
|
907
1368
|
function verifyPayloadSignature(payload, sigB64, key, alg, hash) {
|
|
908
1369
|
const sig = Buffer.from(sigB64, "base64");
|
|
909
1370
|
const h = nodeHash(hash);
|
|
910
1371
|
if (alg === "rsassa-pss") {
|
|
911
|
-
return (0,
|
|
1372
|
+
return (0, import_node_crypto5.verify)(h, payload, { key, padding: import_node_crypto5.constants.RSA_PKCS1_PSS_PADDING, saltLength: hashLen(hash) }, sig);
|
|
912
1373
|
}
|
|
913
|
-
if (alg === "ecdsa-with-SHA256") return (0,
|
|
914
|
-
return (0,
|
|
1374
|
+
if (alg === "ecdsa-with-SHA256") return (0, import_node_crypto5.verify)(h, payload, { key, dsaEncoding: "der" }, sig);
|
|
1375
|
+
return (0, import_node_crypto5.verify)(h, payload, { key, padding: import_node_crypto5.constants.RSA_PKCS1_PADDING }, sig);
|
|
915
1376
|
}
|
|
916
1377
|
function verifyPrehashedSignature(digest, sigB64, key, alg, hash) {
|
|
917
1378
|
const sig = Buffer.from(sigB64, "base64");
|
|
@@ -920,7 +1381,7 @@ function verifyPrehashedSignature(digest, sigB64, key, alg, hash) {
|
|
|
920
1381
|
if (!modBits || sig.length !== Math.ceil(modBits / 8)) return false;
|
|
921
1382
|
let em;
|
|
922
1383
|
try {
|
|
923
|
-
em = (0,
|
|
1384
|
+
em = (0, import_node_crypto5.publicDecrypt)({ key, padding: import_node_crypto5.constants.RSA_NO_PADDING }, sig);
|
|
924
1385
|
} catch {
|
|
925
1386
|
return false;
|
|
926
1387
|
}
|
|
@@ -1004,7 +1465,7 @@ function mgf1(hash, seed, length) {
|
|
|
1004
1465
|
for (let i = 0, n = 0; n < length; i++, n += hashLen(hash)) {
|
|
1005
1466
|
const c = Buffer.alloc(4);
|
|
1006
1467
|
c.writeUInt32BE(i, 0);
|
|
1007
|
-
out.push((0,
|
|
1468
|
+
out.push((0, import_node_crypto5.createHash)(nodeHash(hash)).update(seed).update(c).digest());
|
|
1008
1469
|
}
|
|
1009
1470
|
return Buffer.concat(out).subarray(0, length);
|
|
1010
1471
|
}
|
|
@@ -1028,9 +1489,42 @@ function emsaPssVerify(mHash, em, emBits, hash) {
|
|
|
1028
1489
|
const psLen = emLen - hLen - sLen - 2;
|
|
1029
1490
|
if (!db.subarray(0, psLen).every((b) => b === 0) || db[psLen] !== 1) return false;
|
|
1030
1491
|
const salt = db.subarray(psLen + 1);
|
|
1031
|
-
const h2 = (0,
|
|
1492
|
+
const h2 = (0, import_node_crypto5.createHash)(nodeHash(hash)).update(Buffer.alloc(8)).update(mHash).update(salt).digest();
|
|
1032
1493
|
return h.equals(h2);
|
|
1033
1494
|
}
|
|
1495
|
+
function keyUsageBits(cert) {
|
|
1496
|
+
const v = certExtension(cert.raw, "2.5.29.15");
|
|
1497
|
+
if (!v) return 0;
|
|
1498
|
+
const bs = tlv(v, 0, v.length);
|
|
1499
|
+
if (bs.tag !== 3 || bs.end - bs.start < 2) return 0;
|
|
1500
|
+
return v[bs.start + 1];
|
|
1501
|
+
}
|
|
1502
|
+
function extKeyUsage(cert) {
|
|
1503
|
+
const v = certExtension(cert.raw, "2.5.29.37");
|
|
1504
|
+
if (!v) return [];
|
|
1505
|
+
const seq = tlv(v, 0, v.length);
|
|
1506
|
+
const out = [];
|
|
1507
|
+
for (let p = seq.start; p < seq.end; ) {
|
|
1508
|
+
const t = tlv(v, p, seq.end);
|
|
1509
|
+
out.push(oidOf(v, t));
|
|
1510
|
+
p = t.end;
|
|
1511
|
+
}
|
|
1512
|
+
return out;
|
|
1513
|
+
}
|
|
1514
|
+
function subjectKeyId(cert) {
|
|
1515
|
+
const v = certExtension(cert.raw, "2.5.29.14");
|
|
1516
|
+
if (!v) return void 0;
|
|
1517
|
+
const o = tlv(v, 0, v.length);
|
|
1518
|
+
return o.tag === 4 && o.end > o.start ? Buffer.from(v.subarray(o.start, o.end)).toString("hex") : void 0;
|
|
1519
|
+
}
|
|
1520
|
+
function authorityKeyId(cert) {
|
|
1521
|
+
const v = certExtension(cert.raw, "2.5.29.35");
|
|
1522
|
+
if (!v) return void 0;
|
|
1523
|
+
const seq = tlv(v, 0, v.length);
|
|
1524
|
+
if (seq.start >= seq.end) return void 0;
|
|
1525
|
+
const k = tlv(v, seq.start, seq.end);
|
|
1526
|
+
return k.tag === 128 && k.end > k.start ? Buffer.from(v.subarray(k.start, k.end)).toString("hex") : void 0;
|
|
1527
|
+
}
|
|
1034
1528
|
function isCa(cert) {
|
|
1035
1529
|
return cert.ca === true;
|
|
1036
1530
|
}
|
|
@@ -1058,6 +1552,8 @@ var EidClient = class {
|
|
|
1058
1552
|
sign;
|
|
1059
1553
|
pdf;
|
|
1060
1554
|
organization;
|
|
1555
|
+
person;
|
|
1556
|
+
token;
|
|
1061
1557
|
session;
|
|
1062
1558
|
validator;
|
|
1063
1559
|
constructor(cfg) {
|
|
@@ -1073,8 +1569,10 @@ var EidClient = class {
|
|
|
1073
1569
|
const level = cfg.defaultCertificateLevel ?? "QUALIFIED";
|
|
1074
1570
|
this.auth = new AuthApi(http, cfg.credentials, { certificateLevel: level });
|
|
1075
1571
|
this.sign = new SignApi(http, cfg.credentials, { certificateLevel: level });
|
|
1076
|
-
this.pdf = new PdfApi(http, cfg.credentials, { certificateLevel: level });
|
|
1572
|
+
this.pdf = new PdfApi(http, cfg.credentials, { certificateLevel: level, timeoutMs: cfg.pdfTimeoutMs ?? DEFAULT_PDF_TIMEOUT_MS });
|
|
1077
1573
|
this.organization = new OrganizationApi(http);
|
|
1574
|
+
this.person = new PersonApi(http);
|
|
1575
|
+
this.token = new TokenApi(http, cfg.credentials);
|
|
1078
1576
|
this.session = new SessionApi(http);
|
|
1079
1577
|
this.validator = new ResponseValidator(cfg.trust ?? {});
|
|
1080
1578
|
}
|
|
@@ -1123,7 +1621,7 @@ function openOrShowQR(session, opts) {
|
|
|
1123
1621
|
}
|
|
1124
1622
|
|
|
1125
1623
|
// src/devicelinkv3.ts
|
|
1126
|
-
var
|
|
1624
|
+
var import_node_crypto6 = require("crypto");
|
|
1127
1625
|
var TYPES = ["QR", "Web2App", "App2App"];
|
|
1128
1626
|
var PROTOCOL = { auth: "ACSP_V2", sign: "RAW_DIGEST_SIGNATURE", cert: "" };
|
|
1129
1627
|
var TOKEN_RE = /^[A-Za-z0-9_-]+$/;
|
|
@@ -1180,7 +1678,7 @@ function buildDeviceLink(p) {
|
|
|
1180
1678
|
callback,
|
|
1181
1679
|
unprotected
|
|
1182
1680
|
].join("|");
|
|
1183
|
-
const authCode = (0,
|
|
1681
|
+
const authCode = (0, import_node_crypto6.createHmac)("sha256", Buffer.from(p.sessionSecret, "base64")).update(payload, "utf8").digest("base64url");
|
|
1184
1682
|
return `${unprotected}&authCode=${authCode}`;
|
|
1185
1683
|
}
|
|
1186
1684
|
function qrDeviceLinkTicker(input, onLink, opts) {
|
|
@@ -1193,7 +1691,7 @@ function qrDeviceLinkTicker(input, onLink, opts) {
|
|
|
1193
1691
|
}
|
|
1194
1692
|
|
|
1195
1693
|
// src/oidc.ts
|
|
1196
|
-
var
|
|
1694
|
+
var import_node_crypto7 = require("crypto");
|
|
1197
1695
|
var EID_OIDC_ISSUER = "https://login.eidmongolia.mn";
|
|
1198
1696
|
var EID_ACR_QUALIFIED = "urn:eidmongolia:loa:qualified";
|
|
1199
1697
|
var EID_ACR_ADVANCED = "urn:eidmongolia:loa:advanced";
|
|
@@ -1230,7 +1728,7 @@ var EidOidcClient = class {
|
|
|
1230
1728
|
if (!cfg.clientSecret) throw new EidError("EidOidcClient: clientSecret \u0445\u043E\u043E\u0441\u043E\u043D");
|
|
1231
1729
|
if (!/^https:\/\//.test(cfg.redirectUri ?? "")) throw new EidError("EidOidcClient: redirectUri https:// \u0431\u0430\u0439\u0445 \u0451\u0441\u0442\u043E\u0439");
|
|
1232
1730
|
this.issuer = (cfg.issuer ?? EID_OIDC_ISSUER).replace(/\/+$/, "");
|
|
1233
|
-
this.scope = cfg.scope ??
|
|
1731
|
+
this.scope = cfg.scope ?? OIDC_EXPECT.defaultScope;
|
|
1234
1732
|
}
|
|
1235
1733
|
cfg;
|
|
1236
1734
|
issuer;
|
|
@@ -1278,7 +1776,7 @@ var EidOidcClient = class {
|
|
|
1278
1776
|
state,
|
|
1279
1777
|
nonce,
|
|
1280
1778
|
code_challenge: await pkceChallenge(codeVerifier),
|
|
1281
|
-
code_challenge_method:
|
|
1779
|
+
code_challenge_method: OIDC_EXPECT.pkceMethod
|
|
1282
1780
|
});
|
|
1283
1781
|
if (opts.display) p.set("display", opts.display);
|
|
1284
1782
|
u.search = p.toString();
|
|
@@ -1357,7 +1855,7 @@ var EidOidcClient = class {
|
|
|
1357
1855
|
const header = decodeJsonPart(parts[0]);
|
|
1358
1856
|
const payload = decodeJsonPart(parts[1]);
|
|
1359
1857
|
if (!header || !payload) throw bad("\u0445\u044D\u043B\u0431\u044D\u0440 \u0431\u0443\u0440\u0443\u0443");
|
|
1360
|
-
if (header.alg !==
|
|
1858
|
+
if (header.alg !== OIDC_EXPECT.idTokenAlg) throw bad("alg ES256 \u0431\u0438\u0448");
|
|
1361
1859
|
const kid = typeof header.kid === "string" ? header.kid : "";
|
|
1362
1860
|
if (!kid) throw bad("kid \u0430\u043B\u0433\u0430");
|
|
1363
1861
|
const pick = (ks) => ks.find(
|
|
@@ -1450,7 +1948,7 @@ function formEncode(s) {
|
|
|
1450
1948
|
function timingSafeEqualStr(a, b) {
|
|
1451
1949
|
const x = Buffer.from(a, "utf8");
|
|
1452
1950
|
const y = Buffer.from(b, "utf8");
|
|
1453
|
-
return x.length === y.length && (0,
|
|
1951
|
+
return x.length === y.length && (0, import_node_crypto7.timingSafeEqual)(x, y);
|
|
1454
1952
|
}
|
|
1455
1953
|
function decodeJsonPart(s) {
|
|
1456
1954
|
try {
|
|
@@ -1473,7 +1971,9 @@ function callbackParams(input) {
|
|
|
1473
1971
|
ApiError,
|
|
1474
1972
|
AuthApi,
|
|
1475
1973
|
AuthenticationError,
|
|
1974
|
+
CITIZEN_ISSUING_CA_SKI,
|
|
1476
1975
|
DEFAULT_BASE_URL,
|
|
1976
|
+
DEFAULT_PDF_TIMEOUT_MS,
|
|
1477
1977
|
DeviceLinkError,
|
|
1478
1978
|
EID_ACR_ADVANCED,
|
|
1479
1979
|
EID_ACR_QUALIFIED,
|
|
@@ -1488,10 +1988,13 @@ function callbackParams(input) {
|
|
|
1488
1988
|
ON_BEHALF_ERROR_CODES,
|
|
1489
1989
|
OrganizationApi,
|
|
1490
1990
|
PdfApi,
|
|
1991
|
+
PersonApi,
|
|
1992
|
+
RETRYABLE_ERROR_CODES,
|
|
1491
1993
|
ResponseValidator,
|
|
1492
1994
|
SessionApi,
|
|
1493
1995
|
SessionFailedError,
|
|
1494
1996
|
SignApi,
|
|
1997
|
+
TokenApi,
|
|
1495
1998
|
ValidationError,
|
|
1496
1999
|
acspV2Digest,
|
|
1497
2000
|
buildAcspV2Payload,
|
|
@@ -1500,14 +2003,19 @@ function callbackParams(input) {
|
|
|
1500
2003
|
interactionsToBase64,
|
|
1501
2004
|
isMobileUserAgent,
|
|
1502
2005
|
openOrShowQR,
|
|
2006
|
+
parseRetryAfter,
|
|
1503
2007
|
parseSessionResult,
|
|
1504
2008
|
parseSubject,
|
|
2009
|
+
personMatchesCertificate,
|
|
1505
2010
|
pkceChallenge,
|
|
1506
2011
|
qrDeviceLinkTicker,
|
|
1507
2012
|
randomChallenge,
|
|
1508
2013
|
resolveBaseUrl,
|
|
1509
2014
|
sha256Base64,
|
|
2015
|
+
tokenAuthDigest,
|
|
2016
|
+
tokenLimitOf,
|
|
1510
2017
|
userChallengeOf,
|
|
1511
2018
|
verifyPayloadSignature,
|
|
1512
|
-
verifyPrehashedSignature
|
|
2019
|
+
verifyPrehashedSignature,
|
|
2020
|
+
verifyTokenCertificates
|
|
1513
2021
|
});
|