@gerege-systems/eid-mongolia-sdk 0.14.0 → 0.14.1
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 +105 -4
- package/dist/{browser-C6JHm0lR.d.cts → browser-CuMH5IVG.d.cts} +40 -1
- package/dist/{browser-C6JHm0lR.d.ts → browser-CuMH5IVG.d.ts} +40 -1
- package/dist/browser.d.cts +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/button.cjs +72 -19
- package/dist/button.d.cts +17 -3
- package/dist/button.d.ts +17 -3
- package/dist/button.js +70 -18
- package/dist/index.cjs +244 -26
- package/dist/index.d.cts +136 -8
- package/dist/index.d.ts +136 -8
- package/dist/index.js +240 -26
- package/package.json +2 -2
package/dist/button.js
CHANGED
|
@@ -436,7 +436,7 @@ var T = {
|
|
|
436
436
|
minute: "1 minute left"
|
|
437
437
|
}
|
|
438
438
|
};
|
|
439
|
-
var
|
|
439
|
+
var CSS_BASE = `
|
|
440
440
|
:host { display: inline-block;
|
|
441
441
|
--eid-bg: hsl(0 0% 100%); --eid-bg-subtle: hsl(210 40% 98%); --eid-bg-muted: hsl(210 40% 96%);
|
|
442
442
|
--eid-fg: hsl(222 47% 11%); --eid-fg-muted: hsl(215 19% 35%); --eid-fg-subtle: hsl(215 16% 45%);
|
|
@@ -444,22 +444,26 @@ var CSS = `
|
|
|
444
444
|
--eid-accent: oklch(0.534 0.2 259); --eid-accent-fg: #fff; --eid-accent-subtle: oklch(0.97 0.03 259);
|
|
445
445
|
--eid-accent-subtle-fg: oklch(0.474 0.19 259); --eid-ring: oklch(0.604 0.2 259);
|
|
446
446
|
--eid-warning-bg: hsl(48 100% 96%); --eid-warning-border: hsl(45 95% 78%); --eid-warning-fg: hsl(23 75% 33%);
|
|
447
|
-
--eid-danger-fg: hsl(0 74% 42%); --eid-radius: 6px; --eid-radius-lg: 8px; font-family: inherit; }
|
|
448
|
-
|
|
447
|
+
--eid-danger-fg: hsl(0 74% 42%); --eid-radius: 6px; --eid-radius-lg: 8px; font-family: inherit; color-scheme: light; }
|
|
448
|
+
button, input { font-family: inherit; }
|
|
449
|
+
`;
|
|
450
|
+
var DARK_CSS = `:host { color-scheme: dark;
|
|
449
451
|
--eid-bg: hsl(229 50% 6%); --eid-bg-subtle: hsl(222 47% 9%); --eid-bg-muted: hsl(217 33% 13%);
|
|
450
452
|
--eid-fg: hsl(210 40% 98%); --eid-fg-muted: hsl(213 27% 84%); --eid-fg-subtle: hsl(215 20% 65%);
|
|
451
453
|
--eid-border: hsl(217 33% 17%); --eid-border-strong: hsl(215 25% 27%);
|
|
452
454
|
--eid-accent: oklch(0.64 0.15 259); --eid-accent-fg: hsl(229 50% 6%); --eid-accent-subtle: oklch(0.24 0.06 259);
|
|
453
455
|
--eid-accent-subtle-fg: oklch(0.82 0.1 259); --eid-ring: oklch(0.68 0.15 259);
|
|
454
456
|
--eid-warning-bg: hsl(20 60% 10%); --eid-warning-border: hsl(22 70% 26%); --eid-warning-fg: hsl(40 92% 65%);
|
|
455
|
-
--eid-danger-fg: hsl(0 91% 71%); }
|
|
457
|
+
--eid-danger-fg: hsl(0 91% 71%); }
|
|
458
|
+
`;
|
|
459
|
+
var CSS_REST = `
|
|
456
460
|
*, *::before, *::after { box-sizing: border-box; }
|
|
457
461
|
[hidden] { display: none !important; }
|
|
458
462
|
h2, p, ol { margin: 0; }
|
|
459
463
|
:focus-visible { outline: 2px solid var(--eid-ring); outline-offset: 2px; }
|
|
460
464
|
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
|
|
461
465
|
.btn { display: inline-flex; width: 100%; align-items: center; justify-content: center; gap: 8px; min-height: 44px; padding: 0 20px;
|
|
462
|
-
border-radius: var(--eid-radius); font: 500 1rem
|
|
466
|
+
border-radius: var(--eid-radius); font-family: inherit; font-weight: 500; font-size: 1rem; line-height: 1.25; text-decoration: none; border: 1px solid transparent; cursor: pointer;
|
|
463
467
|
transition: background-color 160ms cubic-bezier(0,0,0.2,1); }
|
|
464
468
|
.btn:disabled { opacity: 0.6; cursor: progress; }
|
|
465
469
|
.btn-primary { background: var(--eid-accent); color: var(--eid-accent-fg); }
|
|
@@ -496,7 +500,7 @@ h2, p, ol { margin: 0; }
|
|
|
496
500
|
.field { display: grid; gap: 8px; }
|
|
497
501
|
.field label { font-weight: 500; }
|
|
498
502
|
.field input { width: 100%; height: 44px; padding: 0 12px; border: 1px solid var(--eid-border-strong); border-radius: var(--eid-radius);
|
|
499
|
-
background: var(--eid-bg); color: var(--eid-fg); font: 1rem
|
|
503
|
+
background: var(--eid-bg); color: var(--eid-fg); font-family: inherit; font-size: 1rem; line-height: 1.5rem; letter-spacing: 0.02em; text-transform: uppercase; }
|
|
500
504
|
.field input::placeholder { color: var(--eid-fg-subtle); text-transform: none; }
|
|
501
505
|
.field input[aria-invalid="true"] { border-color: var(--eid-danger-fg); }
|
|
502
506
|
.hint { font-size: 0.8125rem; color: var(--eid-fg-subtle); }
|
|
@@ -516,6 +520,17 @@ h2, p, ol { margin: 0; }
|
|
|
516
520
|
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { transition: none !important; }
|
|
517
521
|
.spinner { animation: none; border-right-color: currentColor; opacity: 0.6; } }
|
|
518
522
|
`;
|
|
523
|
+
var CSS = CSS_BASE + CSS_REST;
|
|
524
|
+
function resolveEidTheme(theme, doc = document) {
|
|
525
|
+
if (theme === "light" || theme === "dark") return theme;
|
|
526
|
+
const html = doc.documentElement;
|
|
527
|
+
const dt = html?.getAttribute("data-theme");
|
|
528
|
+
if (dt === "dark" || dt === "light") return dt;
|
|
529
|
+
if (html?.classList.contains("dark")) return "dark";
|
|
530
|
+
if (html?.classList.contains("light")) return "light";
|
|
531
|
+
const mq = doc.defaultView?.matchMedia?.("(prefers-color-scheme: dark)");
|
|
532
|
+
return mq?.matches ? "dark" : "light";
|
|
533
|
+
}
|
|
519
534
|
var ID_RE = /^([А-ЯЁӨҮ]{2}[0-9]{8}|[0-9]{8,14})$/;
|
|
520
535
|
var FOCUSABLE = 'button:not([disabled]), input:not([disabled]), a[href], [tabindex]:not([tabindex="-1"])';
|
|
521
536
|
function el(tag, attrs = {}, text) {
|
|
@@ -527,7 +542,7 @@ function el(tag, attrs = {}, text) {
|
|
|
527
542
|
var Base = typeof HTMLElement === "undefined" ? class {
|
|
528
543
|
} : HTMLElement;
|
|
529
544
|
var EidLoginButton = class extends Base {
|
|
530
|
-
static observedAttributes = ["lang"];
|
|
545
|
+
static observedAttributes = ["lang", "theme"];
|
|
531
546
|
/** Тест/дотоод: гар утас эсэхийг дарж бичих. */
|
|
532
547
|
isMobile = () => isMobileUserAgent();
|
|
533
548
|
/** Тест/дотоод: шилжилт (location.assign). */
|
|
@@ -544,6 +559,10 @@ var EidLoginButton = class extends Base {
|
|
|
544
559
|
gen = 0;
|
|
545
560
|
// цонх нээх бүрд нэмэгдэнэ — хуучин fetch-ийн хариуг үл тооно
|
|
546
561
|
lastFocus = null;
|
|
562
|
+
darkStyle;
|
|
563
|
+
themeObs = null;
|
|
564
|
+
mq = null;
|
|
565
|
+
onScheme = () => this.applyTheme();
|
|
547
566
|
get t() {
|
|
548
567
|
return T[this.getAttribute("lang") === "en" ? "en" : "mn"];
|
|
549
568
|
}
|
|
@@ -551,21 +570,52 @@ var EidLoginButton = class extends Base {
|
|
|
551
570
|
return this.getAttribute(name) ?? def;
|
|
552
571
|
}
|
|
553
572
|
connectedCallback() {
|
|
554
|
-
if (this.root)
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
573
|
+
if (!this.root) {
|
|
574
|
+
this.root = this.attachShadow({ mode: "open" });
|
|
575
|
+
const style = el("style");
|
|
576
|
+
style.textContent = CSS;
|
|
577
|
+
this.darkStyle = el("style", { "data-eid-theme": "dark" });
|
|
578
|
+
this.trigger = el("button", { type: "button", class: "btn btn-outline trigger", part: "button", "aria-haspopup": "dialog" });
|
|
579
|
+
this.trigger.append(el("img", { src: EID_LOGO_DATA_URI, alt: "", width: "20", height: "20" }), el("span", {}, this.t.button));
|
|
580
|
+
this.trigger.addEventListener("click", () => this.start());
|
|
581
|
+
this.root.append(style, this.darkStyle, this.trigger);
|
|
582
|
+
}
|
|
583
|
+
this.watchTheme();
|
|
584
|
+
this.applyTheme();
|
|
585
|
+
}
|
|
586
|
+
attributeChangedCallback(name) {
|
|
587
|
+
if (name === "theme") {
|
|
588
|
+
if (this.root) this.applyTheme();
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
564
591
|
const label = this.trigger?.querySelector("span");
|
|
565
592
|
if (label) label.textContent = this.t.button;
|
|
566
593
|
}
|
|
567
594
|
disconnectedCallback() {
|
|
568
595
|
this.close();
|
|
596
|
+
this.themeObs?.disconnect();
|
|
597
|
+
this.themeObs = null;
|
|
598
|
+
this.mq?.removeEventListener?.("change", this.onScheme);
|
|
599
|
+
this.mq = null;
|
|
600
|
+
}
|
|
601
|
+
/** Одоогийн бодит схем (`theme` + auto үед хуудас/OS). */
|
|
602
|
+
get scheme() {
|
|
603
|
+
return resolveEidTheme(this.getAttribute("theme"));
|
|
604
|
+
}
|
|
605
|
+
applyTheme() {
|
|
606
|
+
const css = this.scheme === "dark" ? DARK_CSS : "";
|
|
607
|
+
if (this.darkStyle.textContent !== css) this.darkStyle.textContent = css;
|
|
608
|
+
}
|
|
609
|
+
/** auto үед хуудасны html[data-theme]/class ба OS-ийн схемийн өөрчлөлтийг дагана. */
|
|
610
|
+
watchTheme() {
|
|
611
|
+
if (!this.themeObs && typeof MutationObserver !== "undefined" && document.documentElement) {
|
|
612
|
+
this.themeObs = new MutationObserver(this.onScheme);
|
|
613
|
+
this.themeObs.observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme", "class"] });
|
|
614
|
+
}
|
|
615
|
+
if (!this.mq && typeof matchMedia === "function") {
|
|
616
|
+
this.mq = matchMedia("(prefers-color-scheme: dark)");
|
|
617
|
+
this.mq.addEventListener?.("change", this.onScheme);
|
|
618
|
+
}
|
|
569
619
|
}
|
|
570
620
|
/** Товчны үйлдэл (программаар ч дуудаж болно). */
|
|
571
621
|
start() {
|
|
@@ -909,6 +959,7 @@ function mountEidLoginButton(target, opts) {
|
|
|
909
959
|
b.setAttribute("status-url", opts.statusUrl);
|
|
910
960
|
b.setAttribute("lang", opts.lang ?? "mn");
|
|
911
961
|
if (opts.mode) b.setAttribute("mode", opts.mode);
|
|
962
|
+
if (opts.theme) b.setAttribute("theme", opts.theme);
|
|
912
963
|
if (opts.successUrl) b.setAttribute("success-url", opts.successUrl);
|
|
913
964
|
if (opts.onSuccess) b.addEventListener("eid-login-success", (e) => opts.onSuccess(e.detail));
|
|
914
965
|
if (opts.onError) b.addEventListener("eid-login-error", (e) => opts.onError(e.detail.error));
|
|
@@ -919,5 +970,6 @@ defineEidLoginButton();
|
|
|
919
970
|
export {
|
|
920
971
|
EidLoginButton,
|
|
921
972
|
defineEidLoginButton,
|
|
922
|
-
mountEidLoginButton
|
|
973
|
+
mountEidLoginButton,
|
|
974
|
+
resolveEidTheme
|
|
923
975
|
};
|
package/dist/index.cjs
CHANGED
|
@@ -27,6 +27,7 @@ __export(index_exports, {
|
|
|
27
27
|
DEFAULT_BASE_URL: () => DEFAULT_BASE_URL,
|
|
28
28
|
DEFAULT_PDF_MAX_BYTES: () => DEFAULT_PDF_MAX_BYTES,
|
|
29
29
|
DEFAULT_PDF_TIMEOUT_MS: () => DEFAULT_PDF_TIMEOUT_MS,
|
|
30
|
+
DEFAULT_SIGN_SESSION_TTL_MS: () => DEFAULT_SIGN_SESSION_TTL_MS,
|
|
30
31
|
DeviceLinkError: () => DeviceLinkError,
|
|
31
32
|
EidClient: () => EidClient,
|
|
32
33
|
EidError: () => EidError,
|
|
@@ -50,6 +51,8 @@ __export(index_exports, {
|
|
|
50
51
|
buildDeviceLink: () => buildDeviceLink,
|
|
51
52
|
createEidLogin: () => createEidLogin,
|
|
52
53
|
deviceLink: () => deviceLink,
|
|
54
|
+
deviceLinkInput: () => deviceLinkInput,
|
|
55
|
+
forwardedClientIp: () => forwardedClientIp,
|
|
53
56
|
interactionsToBase64: () => interactionsToBase64,
|
|
54
57
|
isMobileUserAgent: () => isMobileUserAgent,
|
|
55
58
|
memoryLoginStore: () => memoryLoginStore,
|
|
@@ -61,6 +64,7 @@ __export(index_exports, {
|
|
|
61
64
|
qrDeviceLinkTicker: () => qrDeviceLinkTicker,
|
|
62
65
|
randomChallenge: () => randomChallenge,
|
|
63
66
|
resolveBaseUrl: () => resolveBaseUrl,
|
|
67
|
+
sessionExpiry: () => sessionExpiry,
|
|
64
68
|
sha256Base64: () => sha256Base64,
|
|
65
69
|
tokenAuthDigest: () => tokenAuthDigest,
|
|
66
70
|
tokenLimitOf: () => tokenLimitOf,
|
|
@@ -318,6 +322,11 @@ var RP_ROUTES = {
|
|
|
318
322
|
authDeviceLinkEtsi: { method: "POST", path: "/authentication/device-link/etsi/{semanticsIdentifier}" },
|
|
319
323
|
signNotificationEtsi: { method: "POST", path: "/signature/notification/etsi/{semanticsIdentifier}" },
|
|
320
324
|
signNotificationDocument: { method: "POST", path: "/signature/notification/document/{documentNumber}" },
|
|
325
|
+
signNotificationLinked: { method: "POST", path: "/signature/notification/linked/{documentNumber}" },
|
|
326
|
+
signDeviceLinkEtsi: { method: "POST", path: "/signature/device-link/etsi/{semanticsIdentifier}" },
|
|
327
|
+
signDeviceLinkDocument: { method: "POST", path: "/signature/device-link/document/{documentNumber}" },
|
|
328
|
+
certChoiceDeviceLink: { method: "POST", path: "/signature/certificate-choice/device-link/anonymous" },
|
|
329
|
+
certChoiceEtsi: { method: "POST", path: "/signature/certificate-choice/notification/etsi/{semanticsIdentifier}" },
|
|
321
330
|
pdfPrepare: { method: "POST", path: "/pdf/sign/prepare" },
|
|
322
331
|
pdfDocument: { method: "GET", path: "/pdf/sign/{sessionID}/document" },
|
|
323
332
|
orgRepresentations: { method: "GET", path: "/organization/representations/etsi/{personEtsi}" },
|
|
@@ -444,7 +453,8 @@ var AuthApi = class {
|
|
|
444
453
|
vc: vc != null ? String(vc) : null,
|
|
445
454
|
rpChallenge: acsp.rpChallenge,
|
|
446
455
|
acsp,
|
|
447
|
-
receivedAt: Date.now()
|
|
456
|
+
receivedAt: Date.now(),
|
|
457
|
+
sessionType: "auth"
|
|
448
458
|
};
|
|
449
459
|
}
|
|
450
460
|
/** Push — РД/иргэний дугаар/ETSI-ээр (сервер төрлийг таьна). Иргэний утас руу мэдэгдэл очно. */
|
|
@@ -473,10 +483,20 @@ var AuthApi = class {
|
|
|
473
483
|
}
|
|
474
484
|
};
|
|
475
485
|
|
|
486
|
+
// src/types.ts
|
|
487
|
+
var DEFAULT_SIGN_SESSION_TTL_MS = 6e5;
|
|
488
|
+
function sessionExpiry(sentAtMs, ttlMs) {
|
|
489
|
+
return sentAtMs + (ttlMs && ttlMs > 0 ? ttlMs : DEFAULT_SIGN_SESSION_TTL_MS);
|
|
490
|
+
}
|
|
491
|
+
|
|
476
492
|
// src/sign.ts
|
|
477
493
|
function rec2(v) {
|
|
478
494
|
return v && typeof v === "object" ? v : {};
|
|
479
495
|
}
|
|
496
|
+
var vcOf = (r) => {
|
|
497
|
+
const vc = typeof r.vc === "object" && r.vc !== null ? rec2(r.vc).value : r.vc;
|
|
498
|
+
return vc != null ? String(vc) : null;
|
|
499
|
+
};
|
|
480
500
|
var SignApi = class {
|
|
481
501
|
constructor(http, creds, defaults) {
|
|
482
502
|
this.http = http;
|
|
@@ -486,46 +506,155 @@ var SignApi = class {
|
|
|
486
506
|
http;
|
|
487
507
|
creds;
|
|
488
508
|
defaults;
|
|
489
|
-
body(digestB64, interactions, opts) {
|
|
509
|
+
body(digestB64, interactions, opts, defAlg) {
|
|
510
|
+
const sign = {
|
|
511
|
+
digest: digestB64,
|
|
512
|
+
hashAlgorithm: opts?.hashAlgorithm ?? "SHA-256",
|
|
513
|
+
signatureAlgorithm: opts?.signatureAlgorithm ?? defAlg,
|
|
514
|
+
relyingPartyName: this.creds.rpName,
|
|
515
|
+
interactions: interactionsToBase64(interactions),
|
|
516
|
+
initialCallbackUrl: opts?.callbackUrl?.trim() ?? ""
|
|
517
|
+
};
|
|
490
518
|
const body = {
|
|
491
519
|
relyingPartyUUID: this.creds.rpUUID,
|
|
492
|
-
relyingPartyName:
|
|
520
|
+
relyingPartyName: sign.relyingPartyName,
|
|
493
521
|
certificateLevel: opts?.certificateLevel ?? this.defaults.certificateLevel,
|
|
494
522
|
signatureProtocol: "RAW_DIGEST_SIGNATURE",
|
|
495
523
|
signatureProtocolParameters: {
|
|
496
524
|
digest: digestB64,
|
|
497
|
-
signatureAlgorithm:
|
|
498
|
-
signatureAlgorithmParameters: { hashAlgorithm:
|
|
525
|
+
signatureAlgorithm: sign.signatureAlgorithm,
|
|
526
|
+
signatureAlgorithmParameters: { hashAlgorithm: sign.hashAlgorithm }
|
|
499
527
|
},
|
|
500
|
-
interactions:
|
|
528
|
+
interactions: sign.interactions
|
|
501
529
|
};
|
|
502
|
-
if (
|
|
530
|
+
if (sign.initialCallbackUrl) body.initialCallbackUrl = sign.initialCallbackUrl;
|
|
503
531
|
if (opts?.onBehalfOf?.trim()) body.onBehalfOf = opts.onBehalfOf.trim();
|
|
504
532
|
if (opts?.fileName?.trim()) body.fileName = opts.fileName.trim();
|
|
505
|
-
return body;
|
|
533
|
+
return { body, sign };
|
|
534
|
+
}
|
|
535
|
+
async notification(path, body, sign) {
|
|
536
|
+
const sent = Date.now();
|
|
537
|
+
const r = rec2(await this.http.post(path, body));
|
|
538
|
+
return {
|
|
539
|
+
sessionId: String(r.sessionID),
|
|
540
|
+
vc: vcOf(r),
|
|
541
|
+
rpChallenge: "",
|
|
542
|
+
...sign ? { sign } : {},
|
|
543
|
+
expiresAt: sessionExpiry(sent, this.defaults.sessionTtlMs)
|
|
544
|
+
};
|
|
506
545
|
}
|
|
507
|
-
|
|
508
|
-
const
|
|
509
|
-
const
|
|
510
|
-
return {
|
|
546
|
+
async deviceLink(path, body, extra) {
|
|
547
|
+
const sent = Date.now();
|
|
548
|
+
const r = rec2(await this.http.post(path, body));
|
|
549
|
+
return {
|
|
550
|
+
sessionId: String(r.sessionID),
|
|
551
|
+
sessionToken: r.sessionToken != null ? String(r.sessionToken) : null,
|
|
552
|
+
sessionSecret: r.sessionSecret != null ? String(r.sessionSecret) : null,
|
|
553
|
+
deviceLinkBase: r.deviceLinkBase != null ? String(r.deviceLinkBase) : null,
|
|
554
|
+
vc: vcOf(r),
|
|
555
|
+
rpChallenge: "",
|
|
556
|
+
receivedAt: Date.now(),
|
|
557
|
+
...extra,
|
|
558
|
+
expiresAt: sessionExpiry(sent, this.defaults.sessionTtlMs)
|
|
559
|
+
};
|
|
511
560
|
}
|
|
512
561
|
/**
|
|
513
562
|
* Бэлэн digest-ээр push sign (РД/civil_id/ETSI). Баримт (PDF/файл)-ын байтын SHA-256
|
|
514
563
|
* digest-ийг гаднаас бэлдэж өгнө — текст бус бинар баримтад тохиромжтой.
|
|
564
|
+
*
|
|
565
|
+
* ⚠️ Олон утастай иргэнд CA etsi-г ЭХНИЙ идэвхтэй төхөөрөмж рүү шийднэ. Гэрчилгээг урьдчилж мэдэх шаардлагатай бол
|
|
566
|
+
* (PAdES/CMS) {@link certificateChoiceByEtsi} → {@link digestForSigner}.
|
|
515
567
|
*/
|
|
516
568
|
async digestByEtsi(id, digestB64, interactions, opts) {
|
|
517
|
-
const
|
|
518
|
-
return this.
|
|
569
|
+
const { body, sign } = this.body(digestB64, interactions, opts, "rsassa-pss");
|
|
570
|
+
return this.notification(routePath(RP_ROUTES.signNotificationEtsi, id), body, sign);
|
|
519
571
|
}
|
|
520
|
-
/** Бэлэн digest-ээр push sign — баримтын дугаараар (auth
|
|
572
|
+
/** Бэлэн digest-ээр push sign — баримтын дугаараар (auth / certificate-choice-оос гарсан documentNumber дээр). */
|
|
521
573
|
async digestByDocument(documentNumber, digestB64, interactions, opts) {
|
|
522
|
-
const
|
|
523
|
-
return this.
|
|
574
|
+
const { body, sign } = this.body(digestB64, interactions, opts, "rsassa-pss");
|
|
575
|
+
return this.notification(routePath(RP_ROUTES.signNotificationDocument, documentNumber), body, sign);
|
|
524
576
|
}
|
|
525
577
|
/** Текстийн SHA-256 digest-ийг дотроо тооцоод push sign (богино текст баримтад тохиромжтой). */
|
|
526
578
|
async textByEtsi(id, text, interactions, opts) {
|
|
527
579
|
return this.digestByEtsi(id, sha256Base64(text), interactions, opts);
|
|
528
580
|
}
|
|
581
|
+
/**
|
|
582
|
+
* QR / Web2App / App2App sign — тодорхой иргэн (РД/civil_id/ETSI). Холбоосыг backend-д
|
|
583
|
+
* `buildDeviceLink(deviceLinkInput(s, type, lang))`-ээр; Web2App/App2App-д `callbackUrl` заавал.
|
|
584
|
+
* `validateSign`-д `expectedFlowType` = харуулсан холбоосын төрөл.
|
|
585
|
+
*/
|
|
586
|
+
async deviceLinkByEtsi(id, digestB64, interactions, opts) {
|
|
587
|
+
const { body, sign } = this.body(digestB64, interactions, opts, "rsassa-pss");
|
|
588
|
+
return this.deviceLink(routePath(RP_ROUTES.signDeviceLinkEtsi, id), body, { sessionType: "sign", sign });
|
|
589
|
+
}
|
|
590
|
+
/** QR / Web2App / App2App sign — баримтын дугаараар (тодорхой төхөөрөмж). */
|
|
591
|
+
async deviceLinkByDocument(documentNumber, digestB64, interactions, opts) {
|
|
592
|
+
const { body, sign } = this.body(digestB64, interactions, opts, "rsassa-pss");
|
|
593
|
+
return this.deviceLink(routePath(RP_ROUTES.signDeviceLinkDocument, documentNumber), body, { sessionType: "sign", sign });
|
|
594
|
+
}
|
|
595
|
+
// ── Certificate-choice ──
|
|
596
|
+
certChoiceBody(opts) {
|
|
597
|
+
return {
|
|
598
|
+
relyingPartyUUID: this.creds.rpUUID,
|
|
599
|
+
relyingPartyName: this.creds.rpName,
|
|
600
|
+
certificateLevel: opts?.certificateLevel ?? this.defaults.certificateLevel
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Push: иргэний (РД/civil_id/ETSI) утас руу гарын үсгийн гэрчилгээ сонгуулах хүсэлт (PIN-гүй). Үр дүнг
|
|
605
|
+
* `session.waitForResult` → `validator.validateCertificateChoice`. CERT эрх шаардана.
|
|
606
|
+
*/
|
|
607
|
+
async certificateChoiceByEtsi(id, opts) {
|
|
608
|
+
return this.notification(routePath(RP_ROUTES.certChoiceEtsi, id), this.certChoiceBody(opts));
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* QR: иргэн хэн болохыг урьдчилж мэдэхгүй үед гэрчилгээ сонгуулах. CA-ийн certificate-choice хүсэлт
|
|
612
|
+
* initialCallbackUrl авдаггүй тул ЗӨВХӨН QR холбоос (`deviceLinkInput(s, "QR", lang)`). CERT эрх.
|
|
613
|
+
*/
|
|
614
|
+
async certificateChoiceDeviceLink(opts) {
|
|
615
|
+
return this.deviceLink(routePath(RP_ROUTES.certChoiceDeviceLink), this.certChoiceBody(opts), {
|
|
616
|
+
sessionType: "cert",
|
|
617
|
+
relyingPartyName: this.creds.rpName
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
// ── Гэрчилгээ урьдчилж мэдэгдэх гарын үсэг (client-side PAdES / CMS) ──
|
|
621
|
+
signerBody(signer, digestB64, interactions, opts) {
|
|
622
|
+
if (!signer?.documentNumber?.trim()) {
|
|
623
|
+
throw new Error("eid: signer.documentNumber \u0445\u043E\u043E\u0441\u043E\u043D \u2014 validateCertificateChoice-\u0438\u0439\u043D \u04AF\u0440 \u0434\u04AF\u043D\u0433 \u04E9\u0433");
|
|
624
|
+
}
|
|
625
|
+
const out = this.body(digestB64, interactions, opts, "sha256WithRSAEncryption");
|
|
626
|
+
const want = hashLen(out.sign.hashAlgorithm);
|
|
627
|
+
const d = /^[A-Za-z0-9+/]*={0,2}$/.test(digestB64) ? Buffer.from(digestB64, "base64") : null;
|
|
628
|
+
if (!d || d.length !== want || d.toString("base64") !== digestB64) {
|
|
629
|
+
throw new Error(`eid: digest \u043D\u044C \u0441\u0442\u0430\u043D\u0434\u0430\u0440\u0442 base64, ${out.sign.hashAlgorithm}-\u0438\u0439\u043D \u0443\u0440\u0442 (${want} \u0431\u0430\u0439\u0442) \u0431\u0430\u0439\u0445 \u0451\u0441\u0442\u043E\u0439`);
|
|
630
|
+
}
|
|
631
|
+
return out;
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Certificate-choice-оор сонгосон ЯГ ТЭР төхөөрөмж (`signer.documentNumber`) рүү push sign. Олон утастай иргэнд
|
|
635
|
+
* etsi-ээр эхлүүлбэл өөр утас (өөр гэрчилгээ) зурж, гэрчилгээг урьдчилж оруулсан CMS (PAdES signingCertificateV2)
|
|
636
|
+
* хүчингүй болдог — тиймээс digest-ийг ҮРГЭЛЖ documentNumber-аар зуруулна.
|
|
637
|
+
*
|
|
638
|
+
* `signer.certChoiceSessionId` байвал `/signature/notification/linked/{documentNumber}` (`linkedSessionID`), эс
|
|
639
|
+
* бөгөөс `/signature/notification/document/{documentNumber}`. Default `sha256WithRSAEncryption` + SHA-256 (digest =
|
|
640
|
+
* 32 байт DTBS); digest-ийн урт буруу бол хүсэлт явахгүй. Хариуг:
|
|
641
|
+
* `validator.validateSign(r, s.sign.digest, { hashAlgorithm: s.sign.hashAlgorithm, expectedAlgorithm:
|
|
642
|
+
* s.sign.signatureAlgorithm, expectedCertificate: signer.certificate, expectedFlowType: "Notification" })`.
|
|
643
|
+
*/
|
|
644
|
+
async digestForSigner(signer, digestB64, interactions, opts) {
|
|
645
|
+
const { body, sign } = this.signerBody(signer, digestB64, interactions, opts);
|
|
646
|
+
const linked = signer.certChoiceSessionId?.trim();
|
|
647
|
+
if (linked) {
|
|
648
|
+
body.linkedSessionID = linked;
|
|
649
|
+
return this.notification(routePath(RP_ROUTES.signNotificationLinked, signer.documentNumber), body, sign);
|
|
650
|
+
}
|
|
651
|
+
return this.notification(routePath(RP_ROUTES.signNotificationDocument, signer.documentNumber), body, sign);
|
|
652
|
+
}
|
|
653
|
+
/** {@link digestForSigner}-ийн QR / Web2App / App2App хувилбар: `/signature/device-link/document/{documentNumber}`. */
|
|
654
|
+
async deviceLinkForSigner(signer, digestB64, interactions, opts) {
|
|
655
|
+
const { body, sign } = this.signerBody(signer, digestB64, interactions, opts);
|
|
656
|
+
return this.deviceLink(routePath(RP_ROUTES.signDeviceLinkDocument, signer.documentNumber), body, { sessionType: "sign", sign });
|
|
657
|
+
}
|
|
529
658
|
};
|
|
530
659
|
|
|
531
660
|
// src/session.ts
|
|
@@ -1286,13 +1415,19 @@ var ResponseValidator = class {
|
|
|
1286
1415
|
* @param opts — `hashAlgorithm` мөр (хуучин хэлбэр; default SHA-256) эсвэл {@link ValidateSignOptions}.
|
|
1287
1416
|
*/
|
|
1288
1417
|
validateSign(result, digestB64, opts = {}) {
|
|
1289
|
-
const
|
|
1418
|
+
const o = typeof opts === "string" ? { hashAlgorithm: opts } : opts;
|
|
1419
|
+
const { hashAlgorithm = "SHA-256", expectedFlowType } = o;
|
|
1290
1420
|
const leaf = this.precheck(result, "RAW_DIGEST_SIGNATURE");
|
|
1291
1421
|
if (expectedFlowType && result.flowType !== expectedFlowType) {
|
|
1292
1422
|
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)`);
|
|
1293
1423
|
}
|
|
1424
|
+
if (o.expectedCertificate && !leaf.raw.equals(o.expectedCertificate.raw)) {
|
|
1425
|
+
throw new ValidationError(
|
|
1426
|
+
`\u0413\u044D\u0440\u0447\u0438\u043B\u0433\u044D\u044D certificate-choice-\u043E\u043E\u0440 \u0441\u043E\u043D\u0433\u043E\u0441\u043E\u043D\u0442\u043E\u0439 \u0442\u0430\u0430\u0440\u0430\u0445\u0433\u04AF\u0439 (serial ${leaf.serialNumber} \u2260 ${o.expectedCertificate.serialNumber})`
|
|
1427
|
+
);
|
|
1428
|
+
}
|
|
1294
1429
|
const digest = Buffer.from(digestB64, "base64");
|
|
1295
|
-
const { alg, hash } = this.algorithmOf(result, leaf, null, hashAlgorithm);
|
|
1430
|
+
const { alg, hash } = this.algorithmOf(result, leaf, o.expectedAlgorithm ?? null, hashAlgorithm);
|
|
1296
1431
|
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");
|
|
1297
1432
|
if (!verifyPrehashedSignature(digest, result.signatureValueB64, leaf.publicKey, alg, hash)) {
|
|
1298
1433
|
throw new ValidationError("\u0413\u0430\u0440\u044B\u043D \u04AF\u0441\u044D\u0433 \u04E9\u0433\u0441\u04E9\u043D digest-\u0442\u044D\u0439 \u0442\u0430\u0430\u0440\u0441\u0430\u043D\u0433\u04AF\u0439");
|
|
@@ -1306,6 +1441,24 @@ var ResponseValidator = class {
|
|
|
1306
1441
|
parsedSubject: parseSubject(leaf)
|
|
1307
1442
|
};
|
|
1308
1443
|
}
|
|
1444
|
+
/**
|
|
1445
|
+
* Certificate-choice session-ий хариуг шалгана: COMPLETE/OK (эс бөгөөс `SessionFailedError`), гэрчилгээ ба
|
|
1446
|
+
* documentNumber байгаа, хугацаа, certLevel, зориулалт (гарын үсгийн — contentCommitment, `checkKeyUsage` үед), гинж,
|
|
1447
|
+
* issuer. Энэ хариунд гарын үсэг БАЙХГҮЙ — иргэнийг таниагүй (нэвтрэлт биш); зөвхөн дараагийн sign session-ий
|
|
1448
|
+
* гэрчилгээг урьдчилж мэдэхэд. `sessionId` — certificate-choice session.
|
|
1449
|
+
*/
|
|
1450
|
+
validateCertificateChoice(result, sessionId = null) {
|
|
1451
|
+
this.sessionDone(result);
|
|
1452
|
+
const leaf = this.leafOf(result, "RAW_DIGEST_SIGNATURE");
|
|
1453
|
+
return {
|
|
1454
|
+
documentNumber: result.documentNumber,
|
|
1455
|
+
certificate: leaf,
|
|
1456
|
+
certificateDerB64: leaf.raw.toString("base64"),
|
|
1457
|
+
certificateLevel: String(result.certificateLevel ?? ""),
|
|
1458
|
+
parsedSubject: parseSubject(leaf),
|
|
1459
|
+
certChoiceSessionId: sessionId
|
|
1460
|
+
};
|
|
1461
|
+
}
|
|
1309
1462
|
/**
|
|
1310
1463
|
* Хариуны signatureAlgorithm + hashAlgorithm-ийг RP-ийн хүссэнтэй ба иргэний түлхүүрийн төрөлтэй тулгана
|
|
1311
1464
|
* (downgrade / alg confusion хаалт).
|
|
@@ -1355,13 +1508,22 @@ var ResponseValidator = class {
|
|
|
1355
1508
|
// ── дотоод ──
|
|
1356
1509
|
/** state/endResult, cert байгаа эсэх, chain, validity, certLevel — нийтлэг шалгалт. */
|
|
1357
1510
|
precheck(result, protocol) {
|
|
1358
|
-
|
|
1359
|
-
if (result.endResult !== "OK") throw new SessionFailedError(result.endResult ?? "UNKNOWN");
|
|
1511
|
+
this.sessionDone(result);
|
|
1360
1512
|
if (result.signatureProtocol && result.signatureProtocol !== protocol) {
|
|
1361
1513
|
throw new ValidationError(`signatureProtocol ${result.signatureProtocol} \u2260 ${protocol}`);
|
|
1362
1514
|
}
|
|
1363
1515
|
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");
|
|
1364
1516
|
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");
|
|
1517
|
+
return this.leafOf(result, protocol);
|
|
1518
|
+
}
|
|
1519
|
+
/** COMPLETE + OK; амжилтгүй endResult → SessionFailedError. */
|
|
1520
|
+
sessionDone(result) {
|
|
1521
|
+
if (result.state !== "COMPLETE") throw new ValidationError(`Session \u0434\u0443\u0443\u0441\u0430\u0430\u0433\u04AF\u0439 (state=${result.state})`);
|
|
1522
|
+
if (result.endResult !== "OK") throw new SessionFailedError(result.endResult ?? "UNKNOWN");
|
|
1523
|
+
}
|
|
1524
|
+
/** Хариуны гэрчилгээ: documentNumber, задлалт, хугацаа, certLevel, зориулалт (protocol), гинж, issuer. */
|
|
1525
|
+
leafOf(result, protocol) {
|
|
1526
|
+
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");
|
|
1365
1527
|
if (!result.documentNumber) throw new ValidationError("\u0425\u0430\u0440\u0438\u0443\u043D\u0434 documentNumber \u0430\u043B\u0433\u0430");
|
|
1366
1528
|
const leaf = new import_node_crypto5.X509Certificate(Buffer.from(result.certificateDerB64, "base64"));
|
|
1367
1529
|
this.checkValidity(leaf);
|
|
@@ -1778,7 +1940,7 @@ var EidClient = class {
|
|
|
1778
1940
|
});
|
|
1779
1941
|
const level = cfg.defaultCertificateLevel ?? "QUALIFIED";
|
|
1780
1942
|
this.auth = new AuthApi(http, cfg.credentials, { certificateLevel: level });
|
|
1781
|
-
this.sign = new SignApi(http, cfg.credentials, { certificateLevel: level });
|
|
1943
|
+
this.sign = new SignApi(http, cfg.credentials, { certificateLevel: level, sessionTtlMs: cfg.signSessionTtlMs });
|
|
1782
1944
|
this.pdf = new PdfApi(http, cfg.credentials, {
|
|
1783
1945
|
certificateLevel: level,
|
|
1784
1946
|
timeoutMs: cfg.pdfTimeoutMs ?? DEFAULT_PDF_TIMEOUT_MS,
|
|
@@ -1896,6 +2058,37 @@ function buildDeviceLink(p) {
|
|
|
1896
2058
|
const authCode = (0, import_node_crypto6.createHmac)("sha256", Buffer.from(p.sessionSecret, "base64")).update(payload, "utf8").digest("base64url");
|
|
1897
2059
|
return `${unprotected}&authCode=${authCode}`;
|
|
1898
2060
|
}
|
|
2061
|
+
function deviceLinkInput(s, deviceLinkType, lang) {
|
|
2062
|
+
const base = {
|
|
2063
|
+
deviceLinkBase: s.deviceLinkBase ?? "",
|
|
2064
|
+
deviceLinkType,
|
|
2065
|
+
sessionToken: s.sessionToken ?? "",
|
|
2066
|
+
sessionSecret: s.sessionSecret ?? "",
|
|
2067
|
+
lang,
|
|
2068
|
+
receivedAt: s.receivedAt
|
|
2069
|
+
};
|
|
2070
|
+
if (s.sessionType === "sign" && s.sign) {
|
|
2071
|
+
return {
|
|
2072
|
+
...base,
|
|
2073
|
+
sessionType: "sign",
|
|
2074
|
+
digest: s.sign.digest,
|
|
2075
|
+
relyingPartyName: s.sign.relyingPartyName,
|
|
2076
|
+
interactions: s.sign.interactions,
|
|
2077
|
+
initialCallbackUrl: s.sign.initialCallbackUrl
|
|
2078
|
+
};
|
|
2079
|
+
}
|
|
2080
|
+
if (s.sessionType === "cert") {
|
|
2081
|
+
return { ...base, sessionType: "cert", relyingPartyName: s.relyingPartyName ?? "", interactions: "" };
|
|
2082
|
+
}
|
|
2083
|
+
return {
|
|
2084
|
+
...base,
|
|
2085
|
+
sessionType: "auth",
|
|
2086
|
+
rpChallenge: s.acsp?.rpChallenge ?? s.rpChallenge,
|
|
2087
|
+
relyingPartyName: s.acsp?.relyingPartyName ?? "",
|
|
2088
|
+
interactions: s.acsp?.interactions ?? "",
|
|
2089
|
+
initialCallbackUrl: s.acsp?.initialCallbackUrl ?? ""
|
|
2090
|
+
};
|
|
2091
|
+
}
|
|
1899
2092
|
function qrDeviceLinkTicker(input, onLink, opts) {
|
|
1900
2093
|
const clock = opts?.clock ?? Date.now;
|
|
1901
2094
|
const tick = () => onLink(buildDeviceLink({ ...input, deviceLinkType: "QR", now: clock() }));
|
|
@@ -1932,6 +2125,16 @@ function memoryLoginStore() {
|
|
|
1932
2125
|
};
|
|
1933
2126
|
}
|
|
1934
2127
|
var CALLBACK_PATH = "/auth/eid/callback";
|
|
2128
|
+
function forwardedClientIp(headers, trustProxy) {
|
|
2129
|
+
if (trustProxy === false || trustProxy === 0) return void 0;
|
|
2130
|
+
const xff = header(headers, "x-forwarded-for").split(",").map((x) => x.trim()).filter(Boolean);
|
|
2131
|
+
if (xff.length) {
|
|
2132
|
+
if (trustProxy === true) return xff[0];
|
|
2133
|
+
const n = Math.floor(trustProxy);
|
|
2134
|
+
return n >= 1 && n <= xff.length ? xff[xff.length - n] : void 0;
|
|
2135
|
+
}
|
|
2136
|
+
return header(headers, "x-real-ip").trim() || void 0;
|
|
2137
|
+
}
|
|
1935
2138
|
var ID_RE = /^(\p{L}{2}[0-9]{8}|[0-9]{8,14}|PNOMN-[0-9]{8,14})$/u;
|
|
1936
2139
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
1937
2140
|
var B64URL_RE = /^[A-Za-z0-9_-]{43}$/;
|
|
@@ -1983,11 +2186,12 @@ function safeEqualHex(a, b) {
|
|
|
1983
2186
|
const y = Buffer.from(b, "hex");
|
|
1984
2187
|
return x.length === y.length && x.length > 0 && (0, import_node_crypto7.timingSafeEqual)(x, y);
|
|
1985
2188
|
}
|
|
1986
|
-
function toIdentity(r, v, flowType) {
|
|
2189
|
+
function toIdentity(sessionId, r, v, flowType) {
|
|
1987
2190
|
const s = v.parsedSubject;
|
|
1988
2191
|
const etsi = s.serialNumber ?? "";
|
|
1989
2192
|
const m = /^PNOMN-([0-9]+)$/.exec(etsi);
|
|
1990
2193
|
return {
|
|
2194
|
+
sessionId,
|
|
1991
2195
|
etsi,
|
|
1992
2196
|
civilId: m ? m[1] : null,
|
|
1993
2197
|
givenName: s.givenName ?? null,
|
|
@@ -2155,7 +2359,8 @@ function createEidLogin(opts) {
|
|
|
2155
2359
|
const idCode = typeof body.idCode === "string" ? body.idCode.replace(/\s+/g, "").toUpperCase() : "";
|
|
2156
2360
|
if (mode === "push" && !ID_RE.test(idCode)) return json(400, { error: "invalid_id" });
|
|
2157
2361
|
const limited = () => json(429, { error: "rate_limited" }, { "Retry-After": "60" });
|
|
2158
|
-
|
|
2362
|
+
const ip = req.ip || (opts.trustProxy ? forwardedClientIp(req.headers, opts.trustProxy) : void 0);
|
|
2363
|
+
if (ip && !await rateLimit(`start:${ip}`)) return limited();
|
|
2159
2364
|
if (mode === "push" && !await rateLimit(`push:${idCode}`)) return limited();
|
|
2160
2365
|
let r;
|
|
2161
2366
|
try {
|
|
@@ -2177,7 +2382,7 @@ function createEidLogin(opts) {
|
|
|
2177
2382
|
async function finish(req, tx, r, flow, verifier) {
|
|
2178
2383
|
const v = client.validator.validateAuth(r, tx.acsp, { expectedFlowType: flow, userChallengeVerifier: verifier });
|
|
2179
2384
|
if (!await store.delete(tx.sessionId)) return void 0;
|
|
2180
|
-
const out = await opts.onSuccess(toIdentity(r, v, flow), { request: req, mode: tx.mode }) ?? {};
|
|
2385
|
+
const out = await opts.onSuccess(toIdentity(tx.sessionId, r, v, flow), { request: req, mode: tx.mode }) ?? {};
|
|
2181
2386
|
return out;
|
|
2182
2387
|
}
|
|
2183
2388
|
async function status(req) {
|
|
@@ -2260,11 +2465,20 @@ function createEidLogin(opts) {
|
|
|
2260
2465
|
}
|
|
2261
2466
|
return json(404, { error: "not_found" });
|
|
2262
2467
|
}
|
|
2468
|
+
let warnedNoIp = false;
|
|
2263
2469
|
async function handle(req) {
|
|
2264
2470
|
const headers = {};
|
|
2265
2471
|
req.headers.forEach((v, k) => headers[k] = v);
|
|
2266
2472
|
const body = req.method === "POST" ? await req.text() : void 0;
|
|
2267
|
-
const
|
|
2473
|
+
const ip = opts.getClientIp?.(req) || void 0;
|
|
2474
|
+
if (!ip && !opts.getClientIp && !opts.trustProxy && !warnedNoIp) {
|
|
2475
|
+
warnedNoIp = true;
|
|
2476
|
+
onError(new Error("eid-login: handle(Request)-\u0434 \u043A\u043B\u0438\u0435\u043D\u0442 IP \u0430\u043B\u0433\u0430 \u2014 `start:<ip>` \u0445\u044F\u0437\u0433\u0430\u0430\u0440 \u0430\u0436\u0438\u043B\u043B\u0430\u0445\u0433\u04AF\u0439; getClientIp \u044D\u0441\u0432\u044D\u043B trustProxy \u04E9\u0433"), {
|
|
2477
|
+
request: { method: req.method, url: req.url, headers },
|
|
2478
|
+
mode: "qr"
|
|
2479
|
+
});
|
|
2480
|
+
}
|
|
2481
|
+
const r = await handleRequest({ method: req.method, url: req.url, headers, body, ip });
|
|
2268
2482
|
const h = new Headers();
|
|
2269
2483
|
for (const [k, v] of Object.entries(r.headers)) for (const x of [v].flat()) h.append(k, x);
|
|
2270
2484
|
return new Response(r.status === 302 || r.status === 303 || r.body === "" ? null : r.body, { status: r.status, headers: h });
|
|
@@ -2280,6 +2494,7 @@ function createEidLogin(opts) {
|
|
|
2280
2494
|
DEFAULT_BASE_URL,
|
|
2281
2495
|
DEFAULT_PDF_MAX_BYTES,
|
|
2282
2496
|
DEFAULT_PDF_TIMEOUT_MS,
|
|
2497
|
+
DEFAULT_SIGN_SESSION_TTL_MS,
|
|
2283
2498
|
DeviceLinkError,
|
|
2284
2499
|
EidClient,
|
|
2285
2500
|
EidError,
|
|
@@ -2303,6 +2518,8 @@ function createEidLogin(opts) {
|
|
|
2303
2518
|
buildDeviceLink,
|
|
2304
2519
|
createEidLogin,
|
|
2305
2520
|
deviceLink,
|
|
2521
|
+
deviceLinkInput,
|
|
2522
|
+
forwardedClientIp,
|
|
2306
2523
|
interactionsToBase64,
|
|
2307
2524
|
isMobileUserAgent,
|
|
2308
2525
|
memoryLoginStore,
|
|
@@ -2314,6 +2531,7 @@ function createEidLogin(opts) {
|
|
|
2314
2531
|
qrDeviceLinkTicker,
|
|
2315
2532
|
randomChallenge,
|
|
2316
2533
|
resolveBaseUrl,
|
|
2534
|
+
sessionExpiry,
|
|
2317
2535
|
sha256Base64,
|
|
2318
2536
|
tokenAuthDigest,
|
|
2319
2537
|
tokenLimitOf,
|