@gerege-systems/eid-mongolia-sdk 0.13.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 +176 -99
- 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 +1005 -0
- package/dist/button.d.cts +93 -0
- package/dist/button.d.ts +93 -0
- package/dist/button.js +975 -0
- package/dist/index.cjs +591 -305
- package/dist/index.d.cts +254 -146
- package/dist/index.d.ts +254 -146
- package/dist/index.js +586 -301
- package/package.json +12 -4
package/README.md
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
RP-API v3, Smart-ID нийцтэй. Бүрэн баримт: <https://developer.eidmongolia.mn/>.
|
|
6
6
|
|
|
7
7
|
> ⚠️ SDK **backend**-д ажиллана: бүх дуудлага API secret (`rp_sk_…`)-ээр баталгаажна, secret-ийг браузер/гар утсанд
|
|
8
|
-
> хэзээ ч задлахгүй. Браузерт зөвхөн таны backend-ийн өгсөн холбоос/QR/товч харагдана — «eID-ээр нэвтрэх» товч
|
|
9
|
-
>
|
|
10
|
-
>
|
|
8
|
+
> хэзээ ч задлахгүй. Браузерт зөвхөн таны backend-ийн өгсөн холбоос/QR/товч харагдана — «eID-ээр нэвтрэх» товч
|
|
9
|
+
> (`/button`) ч таны backend-ийг л дуудна (§1a). `@gerege-systems/eid-mongolia-sdk/browser`-ийн legacy device-link
|
|
10
|
+
> туслахууд 2026-10-07-оос ажиллахгүй (§4).
|
|
11
11
|
|
|
12
12
|
## Суулгах
|
|
13
13
|
|
|
@@ -22,125 +22,171 @@ Node.js ≥ 22 (global `fetch`, `node:crypto`).
|
|
|
22
22
|
RP-г eID Mongolia-гийн оператор бүртгэнэ — хүсэлтээ <eIDsupport@gerege.com> эсвэл 7777-3773-аар гаргана
|
|
23
23
|
(нийтийн `POST /v3/rp-applications` 2026-10-14-нөөс засвартай: `503 rp_applications_maintenance`). Оператор
|
|
24
24
|
баталсны дараа **relyingPartyUUID** ба **API secret** (`rp_sk_…`) олгогдоно; secret **зөвхөн нэг удаа** харагдана.
|
|
25
|
-
Шаардлагатай мэдээлэл (байгууллага, зорилго `AUTH`/`SIGN`, `callbackHosts
|
|
25
|
+
Шаардлагатай мэдээлэл (байгууллага, зорилго `AUTH`/`SIGN`, `callbackHosts`…):
|
|
26
26
|
<https://developer.eidmongolia.mn/#registration>.
|
|
27
27
|
|
|
28
|
-
## 1a. «eID-ээр нэвтрэх»
|
|
28
|
+
## 1a. «eID-ээр нэвтрэх» — нэг товч
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
(
|
|
33
|
-
хуудасгүйгээр шууд eID апп нээгдэнэ. Гэрээ: <https://developer.eidmongolia.mn/#oidc>.
|
|
30
|
+
Smart-ID-тэй RP-үүдийн хэв маяг: хөтөчид **нэг товч**, бүх eID дуудлага таны **backend**-д (API secret-ээр). SDK хоёр
|
|
31
|
+
хэсэг өгнө — backend-ийн бэлэн урсгал `createEidLogin` ба хөтчийн товч `@gerege-systems/eid-mongolia-sdk/button`.
|
|
32
|
+
Доорх нь §2/§4-ийн RP-API (device link v3, push, `ResponseValidator`)-ийг л ашиглана; тусдаа протокол биш.
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
| Хаана | Юу болно |
|
|
35
|
+
|---|---|
|
|
36
|
+
| Desktop хөтөч | Цонх: QR (секунд тутам шинэчлэгдэнэ) + «регистрийн дугаараар утас руу мэдэгдэл» (push), үлдсэн хугацаа, шууд төлөв |
|
|
37
|
+
| Утасны хөтөч | Товч → `start?mode=web2app` → сервер 302 → eID апп (Web2App). Апп баталгаажуулаад `/auth/eid/callback` руу буцаана |
|
|
38
|
+
| Backend | session эхлүүлэх, QR угсрах (`sessionSecret` серверт), poll, `validateAuth` (гинж + ACSP_V2 + `flowType` + Web2App-д `userChallengeVerifier`) → таны `onSuccess` |
|
|
39
|
+
|
|
40
|
+
**Endpoint-ууд** (`/auth/eid` дор — eID сервер Web2App буцах замыг үргэлж `/auth/eid/callback` болгож нормчилдог):
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
| | Хүсэлт | Хариу |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| `POST /auth/eid/start` | `{"mode":"qr"}` эсвэл `{"mode":"push","idCode":"УБ12345678"}` (JSON заавал) | `{mode, vc, qr?, expiresIn}` + `Set-Cookie: eid_login` |
|
|
45
|
+
| `GET /auth/eid/start?mode=web2app` | утасны хөтөч (товчны навигаци) | `302` → eID апп |
|
|
46
|
+
| `GET /auth/eid/status` | cookie | `PENDING` (+шинэ `qr`, `expiresIn`) · `WAIT_CALLBACK` · `OK` (`redirect`, `data`) · `FAILED` (`error`) · `EXPIRED` |
|
|
47
|
+
| `GET /auth/eid/callback` | апп буцаана: `?sessionId&userChallengeVerifier` | `303` → `onSuccess`-ийн `redirect` / `successRedirect`, алдаа бол `failureRedirect?eid_error=<код>` |
|
|
44
48
|
|
|
45
|
-
**
|
|
46
|
-
`login.eidmongolia.mn/oauth2/token` дээр `401 invalid_client` авна. Discovery-ийн `mtls_endpoint_aliases.token_endpoint`
|
|
47
|
-
(RFC 8705 §5, `https://rp.eidmongolia.mn/oauth2/token`) руу RP-API-д хэрэглэдэг **ижил client cert**-ээр явуулна:
|
|
48
|
-
`useMtlsEndpointAliases: true` + `dispatcher` (RP-API client-ийнхтэй ижил undici `Agent`). Discovery-д alias байхгүй
|
|
49
|
-
бол `token_endpoint` руу буцна; alias https биш бол `discovery_failed`. mTLS-гүй RP флаг тавихгүй.
|
|
49
|
+
**Backend** (нэг instance-ийг модулийн түвшинд):
|
|
50
50
|
|
|
51
51
|
```ts
|
|
52
|
-
import {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
import { EidClient, createEidLogin } from "@gerege-systems/eid-mongolia-sdk";
|
|
53
|
+
|
|
54
|
+
const eid = new EidClient({
|
|
55
|
+
credentials: { rpUUID: process.env.EID_RP_UUID!, rpName: "Интернэт банк", apiSecret: process.env.EID_SECRET! },
|
|
56
|
+
trust: { trustAnchorsPem: [process.env.EID_NATIONAL_ROOT_CA_PEM!] },
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
export const eidLogin = createEidLogin({
|
|
60
|
+
client: eid,
|
|
61
|
+
callbackUrl: "https://bank.example.mn/auth/eid/callback", // host нь RP-ийн callbackHosts-д; утасны урсгалд заавал
|
|
62
|
+
async onSuccess(id) {
|
|
63
|
+
// id.etsi (PNOMN-…) — хэрэглэгчийг ҮҮГЭЭР таньна; id.givenName/surname (латин), id.person (кирилл, байвал)
|
|
64
|
+
// id.sessionId, id.documentNumber, id.certificateDerB64 — дараагийн алхамд (гарын үсэг, гэрчилгээ холбох)
|
|
65
|
+
const user = await users.findOrCreateByEtsi(id.etsi);
|
|
66
|
+
const sid = await sessions.create(user.id); // ШИНЭ session (session fixation)
|
|
67
|
+
return {
|
|
68
|
+
redirect: "/dashboard",
|
|
69
|
+
headers: { "Set-Cookie": `app_session=${sid}; Path=/; HttpOnly; Secure; SameSite=Lax` },
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
// store: redisStore, // олон instance / serverless бол (анхдагч — процессын санах ой)
|
|
73
|
+
// rateLimit: (key) => redisLimiter.allow(key), // "start:<ip>", "push:<idCode>"; анхдагч push 3/мин нэг дугаарт
|
|
74
|
+
trustProxy: 1, // handle(Request)-д клиент IP: nginx/LB нэг hop бол 1 (доор «Клиент IP»)
|
|
59
75
|
});
|
|
60
76
|
```
|
|
61
77
|
|
|
78
|
+
Next.js App Router — `app/auth/eid/[action]/route.ts`:
|
|
79
|
+
|
|
62
80
|
```ts
|
|
63
|
-
import {
|
|
81
|
+
import { eidLogin } from "@/lib/eid";
|
|
82
|
+
export const dynamic = "force-dynamic";
|
|
83
|
+
export const GET = (req: Request) => eidLogin.handle(req);
|
|
84
|
+
export const POST = (req: Request) => eidLogin.handle(req);
|
|
85
|
+
```
|
|
64
86
|
|
|
65
|
-
|
|
66
|
-
clientId: process.env.EID_RP_UUID!,
|
|
67
|
-
clientSecret: process.env.EID_SECRET!, // rp_sk_… — зөвхөн backend
|
|
68
|
-
redirectUri: "https://bank.example.mn/auth/eid/callback",
|
|
69
|
-
// issuer анхдагч https://login.eidmongolia.mn, scope анхдагч "openid"
|
|
70
|
-
});
|
|
87
|
+
**Клиент IP** (`start:<ip>` хязгаар). Web-standard `Request`-д IP байдаггүй — `handle(req)` ашиглаж байвал аль нэгийг өг:
|
|
71
88
|
|
|
72
|
-
|
|
73
|
-
|
|
89
|
+
- `trustProxy: n` — итгэмжит reverse proxy-ийн ард: `X-Forwarded-For`-ийн **баруунаас n** дахь утга (n = таны proxy-ийн тоо),
|
|
90
|
+
XFF байхгүй бол `X-Real-IP`. `true` = XFF-ийн зүүн утга (proxy нь клиентийн XFF-ийг дарж бичдэг үед л). Proxy-гүй
|
|
91
|
+
үед бүү асаа — header-ийг клиент хуурамчаар илгээнэ.
|
|
92
|
+
- `getClientIp: (req) => …` — платформын IP (Bun `server.requestIP(req)?.address`, Deno `info.remoteAddr.hostname`,
|
|
93
|
+
Vercel `req.headers.get("x-real-ip")`). `trustProxy`-оос давуу.
|
|
74
94
|
|
|
75
|
-
|
|
76
|
-
app.
|
|
77
|
-
|
|
78
|
-
res.cookie(TX, JSON.stringify({ state: a.state, nonce: a.nonce, codeVerifier: a.codeVerifier }), cookieOpts);
|
|
79
|
-
res.set("Cache-Control", "no-store").redirect(302, a.url);
|
|
80
|
-
});
|
|
95
|
+
Аль нь ч үгүй бол IP-ээр хязгаарлахгүй (эхний хүсэлтэд `onError`-оор нэг удаа анхааруулна). `handleRequest`-д `req.ip`
|
|
96
|
+
(Express `req.ip`, `app.set("trust proxy", …)`-тэй) шууд хэрэглэгдэнэ. Анхдагч `rateLimit` зөвхөн `push:`-ийг хязгаарладаг —
|
|
97
|
+
`start:<ip>`-ийг өөрийн `rateLimit`-ээр (Redis г.м.) хязгаарлана. Go: `eidlogin.Config.ClientIP` (`eidlogin.ForwardedClientIP(n)`) — ижил дүрэм.
|
|
81
98
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const { claims } = await oidc.handleCallback(req.originalUrl, tx); // state → token → id_token (ES256)
|
|
89
|
-
const user = await users.findOrCreateBySub(claims.sub); // pairwise sub-аар таньна (нэрээр биш)
|
|
90
|
-
await startSession(res, user); // таны session-ийг шинээр үүсгэ (session fixation)
|
|
91
|
-
res.redirect(303, "/");
|
|
92
|
-
} catch (e) {
|
|
93
|
-
const code = e instanceof EidOidcError ? e.code : "server_error"; // access_denied → «дахин оролдоно уу»
|
|
94
|
-
res.redirect(303, `/login?error=${code}`);
|
|
95
|
-
}
|
|
99
|
+
Express:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
app.use("/auth/eid", express.json(), async (req, res) => {
|
|
103
|
+
const r = await eidLogin.handleRequest({ method: req.method, url: req.originalUrl, headers: req.headers, body: req.body, ip: req.ip });
|
|
104
|
+
res.status(r.status).set(r.headers).send(r.body);
|
|
96
105
|
});
|
|
97
106
|
```
|
|
98
107
|
|
|
99
|
-
|
|
100
|
-
callback → `oidc.handleCallback(request.nextUrl.searchParams, tx)` (зөвхөн query уншина — прокси ард host хамаагүй).
|
|
101
|
-
|
|
102
|
-
**Товч** — энгийн холбоос (`<a href>` эсвэл form GET). `fetch`/XHR/JS redirect биш: iOS Universal Link зөвхөн
|
|
103
|
-
хэрэглэгчийн tap-аас эхэлсэн redirect гинжид eID аппыг нээнэ.
|
|
108
|
+
**Хөтөч** — Web Component (React/Vue/энгийн HTML аль нь ч):
|
|
104
109
|
|
|
105
110
|
```html
|
|
106
|
-
<
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
font: 500 1rem/1 system-ui, sans-serif; text-decoration: none; }
|
|
114
|
-
.eid-login:hover { background: #f5f7fa; }
|
|
115
|
-
.eid-login:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
|
|
116
|
-
</style>
|
|
111
|
+
<script type="module">import "@gerege-systems/eid-mongolia-sdk/button";</script>
|
|
112
|
+
<eid-login-button start-url="/auth/eid/start" status-url="/auth/eid/status" lang="mn"></eid-login-button>
|
|
113
|
+
<script type="module">
|
|
114
|
+
document.querySelector("eid-login-button").addEventListener("eid-login-success", (e) => {
|
|
115
|
+
// e.detail = { state: "OK", redirect, data } — preventDefault() хийвэл redirect хийхгүй
|
|
116
|
+
});
|
|
117
|
+
</script>
|
|
117
118
|
```
|
|
118
119
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
120
|
+
эсвэл `mountEidLoginButton(el, { startUrl, statusUrl, lang: "en", theme: "auto", successUrl, onSuccess, onError })`.
|
|
121
|
+
Атрибут: `lang` (`mn` | `en`), `mode` (`auto` анхдагч — утас → Web2App, бусад → QR цонх; `qr`; `app`),
|
|
122
|
+
`theme` (`auto` анхдагч | `light` | `dark`), `success-url`.
|
|
123
|
+
Үйл явдал: `eid-login-success`, `eid-login-error` (`detail.error`: `USER_REFUSED…`, `VALIDATION`, `EXPIRED` …).
|
|
124
|
+
Гадны хамааралгүй (QR encoder дотроо), цонх нь focus trap, Esc, `aria-live` төлөвтэй.
|
|
125
|
+
|
|
126
|
+
**Дизайн, theme.** Дизайн нь eID нэвтрэх хуудсынх (лого, өнгө). Фонт нь хуудаснаас өвлөгдөнө (товч, цонх, input бүгд).
|
|
127
|
+
`theme="auto"` үед дараалал: хуудасны `html[data-theme="dark|light"]` → `html.dark` / `html.light` → OS
|
|
128
|
+
(`prefers-color-scheme`); хуудас theme солиход (MutationObserver) шууд дагана. `theme="light|dark"` нь хуудсыг давна.
|
|
129
|
+
Өнгө, радиусыг CSS хувьсагчаар (элемент дээр — shadow-ийн `:host`-оос давуу) дарна:
|
|
130
|
+
|
|
131
|
+
```css
|
|
132
|
+
eid-login-button {
|
|
133
|
+
--eid-accent: #0b5cff; --eid-accent-fg: #fff; /* үндсэн товч, QR-ийн алхмын тоо (--eid-accent-subtle, --eid-accent-subtle-fg) */
|
|
134
|
+
--eid-bg: #fff; --eid-bg-subtle: …; --eid-bg-muted: …; /* цонх, hover */
|
|
135
|
+
--eid-fg: …; --eid-fg-muted: …; --eid-fg-subtle: …; /* текст */
|
|
136
|
+
--eid-border: …; --eid-border-strong: …; --eid-ring: …; /* хүрээ, focus */
|
|
137
|
+
--eid-warning-bg: …; --eid-warning-border: …; --eid-warning-fg: …; --eid-danger-fg: …;
|
|
138
|
+
--eid-radius: 6px; --eid-radius-lg: 8px;
|
|
139
|
+
}
|
|
140
|
+
html[data-theme="dark"] eid-login-button { --eid-accent: …; } /* dark-д өөр өнгө бол */
|
|
141
|
+
eid-login-button::part(button) { width: 100%; } /* part: button, backdrop, dialog */
|
|
142
|
+
```
|
|
124
143
|
|
|
125
|
-
|
|
126
|
-
(
|
|
127
|
-
|
|
128
|
-
|
|
144
|
+
**eID аппын доод хувилбар.** `flowType` бэхлэлт (device link v3) ба Web2App-ийн `userChallengeVerifier` нь
|
|
145
|
+
**iOS 2.2.3 (build 59)**, **Android 2.2.3 (versionCode 60)**-аас эхэлсэн. eID сервер 2026-10-07-оос legacy холбоосыг
|
|
146
|
+
бүрэн хассан (v3-гүй session-д утас `DEVICE_LINK_REQUIRED`) бөгөөд доод хувилбарыг `/mobile/app-config`-оор албадан
|
|
147
|
+
шинэчлүүлдэг: Android 2026-10-07-оос ≥ 70 (төхөөрөмжийн баталгаатай), 2026-10-14-ний байдлаар iOS ≥ 2.2.7,
|
|
148
|
+
Android ≥ 83 (`curl https://ca.eidmongolia.mn/mobile/app-config`). Иймд гарын үсэг зурж чадах бүх апп эдгээрийг дэмжинэ —
|
|
149
|
+
RP талд нэмэлт шалгалт хэрэггүй. Эх сурвалж: eID CA-ийн `docs/DEVICE_LINK_V3.md` §5, §7; changelog
|
|
150
|
+
`2026-10-02-ios-min-version-2-2-3`, `2026-10-07-android-min-70`.
|
|
129
151
|
|
|
130
152
|
**Аюулгүй байдал.**
|
|
131
153
|
|
|
132
|
-
- `
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
- `
|
|
136
|
-
|
|
137
|
-
`
|
|
138
|
-
|
|
139
|
-
- `
|
|
140
|
-
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
154
|
+
- `sessionToken`, `sessionSecret`, `rpChallenge` серверт (store) үлдэнэ; хөтөчид зөвхөн HttpOnly, Secure, SameSite=Lax cookie
|
|
155
|
+
(`<sessionId>.<binding>`, store-д binding-ийн SHA-256). Status ба callback зөвхөн нэвтрэлтийг **эхлүүлсэн хөтөчид** хариулна —
|
|
156
|
+
өөр хөтчид нээгдсэн callback URL (verifier-тэй ч) хүлээн авагдахгүй (login CSRF).
|
|
157
|
+
- `start` POST нь JSON заавал (simple request биш → cross-origin preflight), `Sec-Fetch-Site: cross-site` → 403.
|
|
158
|
+
- `OK` нь зөвхөн `validateAuth` амжилттай үед: QR → `flowType=QR`, push → `Notification`, утас → `Web2App` + callback-ийн
|
|
159
|
+
`userChallengeVerifier`. Web2App session-ийг poll дуусгахгүй (`WAIT_CALLBACK`). `onSuccess` нэг session-д нэг л удаа
|
|
160
|
+
(store-ийн `delete` атомар байх ёстой — Redis `DEL`).
|
|
161
|
+
- `onSuccess`-ийн `data` хөтөчид очно — нууц бүү оруул. Иргэний бүртгэлтэй эсэхийг push алдаагаар задлахгүй (`eid_unavailable`).
|
|
162
|
+
- Олон instance / serverless (Vercel г.м.) дээр анхдагч санах ойн store ажиллахгүй — `EidLoginStore` (`get`/`set`/`delete`)-ийг Redis-ээр.
|
|
163
|
+
- `onSuccess` дуудагдахаас ӨМНӨ tx store-оос атомар устгагдана (нэг удаагийн баталгаа) — `onSuccess` дотор store-оос бүү
|
|
164
|
+
унш; хэрэгтэй бүхэн `identity`-д (`sessionId`, `documentNumber`, `certificateDerB64`, …).
|
|
165
|
+
|
|
166
|
+
**Redis store** (SDK нэмэлт хамааралгүй — жишээ нь `ioredis`):
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import Redis from "ioredis";
|
|
170
|
+
import type { EidLoginStore, EidLoginTx } from "@gerege-systems/eid-mongolia-sdk";
|
|
171
|
+
|
|
172
|
+
const redis = new Redis(process.env.REDIS_URL!);
|
|
173
|
+
const key = (sid: string) => `eid:login:${sid}`;
|
|
174
|
+
|
|
175
|
+
export const redisStore: EidLoginStore = {
|
|
176
|
+
async get(sid) {
|
|
177
|
+
const v = await redis.get(key(sid));
|
|
178
|
+
return v ? (JSON.parse(v) as EidLoginTx) : undefined;
|
|
179
|
+
},
|
|
180
|
+
async set(sid, tx, ttlMs) {
|
|
181
|
+
await redis.set(key(sid), JSON.stringify(tx), "PX", ttlMs);
|
|
182
|
+
},
|
|
183
|
+
async delete(sid) {
|
|
184
|
+
return (await redis.del(key(sid))) === 1; // DEL атомар — зэрэг хоёр status-ийн зөвхөн нэг нь onSuccess дуудна
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Tx-д `sessionSecret` (QR угсрах материал) байдаг — Redis-ийг сүлжээнээс хаалттай, TLS/AUTH-тай ашигла.
|
|
144
190
|
|
|
145
191
|
## 2. Нэвтрэлт (push)
|
|
146
192
|
|
|
@@ -222,6 +268,40 @@ eid.validator.validateSign(result, digest384, { hashAlgorithm: "SHA-384", expect
|
|
|
222
268
|
|
|
223
269
|
RAW_DIGEST_SIGNATURE-д `flowType` гарын үсэгт ордоггүй (зөвхөн digest зурагдана) — энэ нь RP-API хариуны тулгалт.
|
|
224
270
|
|
|
271
|
+
**QR / Web2App / App2App** — `eid.sign.deviceLinkByEtsi` / `deviceLinkByDocument` (`callbackUrl`-тай бол Web2App/App2App);
|
|
272
|
+
холбоосыг backend-д `buildDeviceLink(deviceLinkInput(s, "QR", "mon"))` (auth/cert session-д ч мөн). `s.sign`
|
|
273
|
+
(`SignContext`: digest, hash, алгоритм, interactions, callback)-ийг session-тэй хамт серверт хадгал.
|
|
274
|
+
|
|
275
|
+
**`expiresAt`** (ms epoch) — sign / certificate-choice session-ий *тооцоолсон* дуусах мөч: CA session start хариунд
|
|
276
|
+
хугацаа буцаадаггүй (зөвхөн `sessionID`), CA-ийн анхдагч 600с (`DEFAULT_SIGN_SESSION_TTL_MS`; CA админ 60–1800с болгож
|
|
277
|
+
болно → `signSessionTtlMs`). Хүсэлтээс өмнөх цагаас тооцдог тул CA-ийнхаас хойно хэзээ ч биш; эцсийн үнэн нь
|
|
278
|
+
`GET /session` (`TIMEOUT`).
|
|
279
|
+
|
|
280
|
+
### 3.1. Гэрчилгээг урьдчилж мэдэх (client-side PAdES / CMS)
|
|
281
|
+
|
|
282
|
+
Гэрчилгээ DTBS-д (`signingCertificateV2`) ордог тул эхлээд certificate-choice, дараа нь digest-ийг **яг тэр төхөөрөмж
|
|
283
|
+
рүү (`documentNumber`)** зуруулна. Олон утастай иргэнд etsi-ээр эхлүүлбэл өөр утас зурж, гарын үсэг гэрчилгээтэйгээ
|
|
284
|
+
таарахгүй.
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
const cc = await eid.sign.certificateChoiceByEtsi("PNOMN-12345678"); // эсвэл certificateChoiceDeviceLink() (зөвхөн QR)
|
|
288
|
+
const signer = eid.validator.validateCertificateChoice(await eid.session.waitForResult(cc.sessionId), cc.sessionId);
|
|
289
|
+
const dtbs = buildDtbs(signer.certificate); // 32 байт SHA-256, стандарт base64
|
|
290
|
+
const s = await eid.sign.digestForSigner(signer, dtbs, ix, { fileName: "Гэрээ.pdf" }); // linked/{documentNumber}
|
|
291
|
+
// QR / Web2App: eid.sign.deviceLinkForSigner(signer, dtbs, ix, { callbackUrl })
|
|
292
|
+
const r = await eid.session.waitForResult(s.sessionId);
|
|
293
|
+
const sig = eid.validator.validateSign(r, s.sign!.digest, {
|
|
294
|
+
hashAlgorithm: s.sign!.hashAlgorithm,
|
|
295
|
+
expectedFlowType: "Notification",
|
|
296
|
+
expectedAlgorithm: s.sign!.signatureAlgorithm, // sha256WithRSAEncryption — PSS хариуг татгалзана
|
|
297
|
+
expectedCertificate: signer.certificate, // өөр гэрчилгээгээр зурсан бол татгалзана
|
|
298
|
+
});
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
`digestForSigner` / `deviceLinkForSigner`-ийн анхдагч: `sha256WithRSAEncryption` + `SHA-256` (CA SplitKey RSA нь хүссэн
|
|
302
|
+
алгоритмаар, PKCS#1 v1.5; хуучин threshold-ECDSA credential нь үргэлж `ecdsa-with-SHA256` — `expectedAlgorithm` үүнийг
|
|
303
|
+
зөвхөн EC P-256 гэрчилгээтэй үед зөвшөөрнө). Digest-ийн урт hash-тай таарахгүй бол хүсэлт явахгүй.
|
|
304
|
+
|
|
225
305
|
## 3a. Байгууллагын нэрийн өмнөөс гарын үсэг (onBehalfOf)
|
|
226
306
|
|
|
227
307
|
Иргэн **өөрийн PIN2 гэрчилгээгээр** зурна; тухайн мөчид байгууллагыг төлөөлөх эрхтэйг eID **өөрийн бүртгэлээр**
|
|
@@ -445,7 +525,6 @@ OK ирж болзошгүй. Иймд `ResponseValidator`:
|
|
|
445
525
|
| `ApiError` | бусад HTTP алдаа (`.status`, `.body`, серверийн `.code`, `.retryAfterMs`, `.headers`, `.retryable` — ж: 404 `ORG_NOT_FOUND`) |
|
|
446
526
|
| `ResponseTooLargeError` | хариу SDK-ийн хязгаараас их (JSON 4 MiB, PDF `pdfMaxBytes`) — `.op`, `.limit` |
|
|
447
527
|
| `NetworkError` | timeout/сүлжээ (`.op` — таних тэмдэггүй зам, `.timeout`) |
|
|
448
|
-
| `EidOidcError` | «eID-ээр нэвтрэх» (OIDC) — `.code`: `access_denied`, `login_required`, `state_mismatch`, `invalid_grant`, `invalid_id_token`, `server_error` … |
|
|
449
528
|
| `DeviceLinkError` | device-link холбоос угсрах боломжгүй (`deviceLinkBase` https биш, token/secret/lang хэлбэр, төрөлд хэрэгтэй талбар дутуу) |
|
|
450
529
|
|
|
451
530
|
## mTLS (eIDAS qualified орчин)
|
|
@@ -469,9 +548,7 @@ npm test # build + node --test
|
|
|
469
548
|
### Гэрээний шалгалт
|
|
470
549
|
|
|
471
550
|
SDK-ийн дууддаг RP-API зам бүр (`src/contract.ts`) live [`/.well-known/eid`](https://rp.eidmongolia.mn/.well-known/eid)-ийн
|
|
472
|
-
`endpoints`-д байх
|
|
473
|
-
талбарууд) [discovery](https://login.eidmongolia.mn/.well-known/openid-configuration)-тэй таарах ёстой. Discovery-д
|
|
474
|
-
байгаа ч SDK хамраагүй замууд WARN (fail биш). Сүлжээ шаарддаг тул `npm test`-д ордоггүй:
|
|
551
|
+
`endpoints`-д байх ёстой. Discovery-д байгаа ч SDK хамраагүй замууд WARN (fail биш). Сүлжээ шаарддаг тул `npm test`-д ордоггүй:
|
|
475
552
|
|
|
476
553
|
```bash
|
|
477
554
|
npm run test:contract
|
|
@@ -72,7 +72,38 @@ interface NotificationSession {
|
|
|
72
72
|
rpChallenge: string;
|
|
73
73
|
/** Auth session-д validator-т өгөх контекст (sign session-д undefined). */
|
|
74
74
|
acsp?: AcspContext;
|
|
75
|
+
/** Sign session-ий хүсэлтэд илгээсэн утгууд ({@link SignContext}); auth / certificate-choice-д undefined. */
|
|
76
|
+
sign?: SignContext;
|
|
77
|
+
/** Sign / certificate-choice session-ий ТООЦООЛСОН дуусах мөч (ms epoch) — {@link sessionExpiry}; auth-д undefined. */
|
|
78
|
+
expiresAt?: number;
|
|
75
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Sign session-ий хүсэлтэд илгээсэн утгууд ЯГ ХЭВЭЭР: `validateSign` (digest, hashAlgorithm, signatureAlgorithm) ба
|
|
82
|
+
* device link (`buildDeviceLink`: digest, relyingPartyName, interactions, initialCallbackUrl)-д. RP session-тэй хамт
|
|
83
|
+
* серверт хадгална.
|
|
84
|
+
*/
|
|
85
|
+
interface SignContext {
|
|
86
|
+
/** Илгээсэн digest (стандарт base64, ЯГ ХЭВЭЭР — device link-ийн authCode-д ордог). */
|
|
87
|
+
digest: string;
|
|
88
|
+
hashAlgorithm: HashAlgorithm;
|
|
89
|
+
signatureAlgorithm: SignatureAlgorithm;
|
|
90
|
+
relyingPartyName: string;
|
|
91
|
+
/** Илгээсэн interactions-ийн base64 МӨР. */
|
|
92
|
+
interactions: string;
|
|
93
|
+
initialCallbackUrl: string;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* CA-ийн SIGNATURE ба CERTIFICATE_CHOICE session-ий анхдагч хүчинтэй хугацаа (мс; `EID_SIGN_SESSION_TTL_SECONDS` = 600).
|
|
97
|
+
* CA-ийн админ «Системийн тохиргоо»-нд 60–1800 секундэд өөрчилж болно; session start хариунд CA хугацааг буцаадаггүй
|
|
98
|
+
* (зөвхөн `sessionID`) тул SDK үүнийг `ClientConfig.signSessionTtlMs`-ээр тооцоолно.
|
|
99
|
+
*/
|
|
100
|
+
declare const DEFAULT_SIGN_SESSION_TTL_MS = 600000;
|
|
101
|
+
/**
|
|
102
|
+
* Session start-ын хариунаас тооцоолох дуусах мөч (ms epoch): хүсэлт илгээхээс ӨМНӨХ цаг + ttl. CA ExpiresAt-ийг өөрийн
|
|
103
|
+
* хүлээн авсан мөчөөс тооцдог тул энэ утга CA-ийнхаас ХЭЗЭЭ Ч хойно биш (консерватив). RP UI-ийн тоолуур / «дахин
|
|
104
|
+
* эхлүүлэх» шийдвэрт л — эцсийн үнэн нь `GET /session` (`TIMEOUT`).
|
|
105
|
+
*/
|
|
106
|
+
declare function sessionExpiry(sentAtMs: number, ttlMs?: number): number;
|
|
76
107
|
/**
|
|
77
108
|
* Device-link (QR/Web2App/App2App) session. Иргэн QR уншиж эсвэл товч дарж нэгдэнэ.
|
|
78
109
|
* `sessionToken`, `sessionSecret`-ийг ЗӨВХӨН backend-д хадгална — браузер руу бүү гарга.
|
|
@@ -99,6 +130,14 @@ interface DeviceLinkSession {
|
|
|
99
130
|
* ЗӨВХӨН backend-д хадгална.
|
|
100
131
|
*/
|
|
101
132
|
receivedAt: number;
|
|
133
|
+
/** auth | sign | cert ({@link deviceLinkInput}-д). Хуучин хадгалсан session-д undefined = auth. */
|
|
134
|
+
sessionType?: "auth" | "sign" | "cert";
|
|
135
|
+
/** Sign session-д ({@link deviceLinkInput}, `validateSign`); бусад үед undefined. */
|
|
136
|
+
sign?: SignContext;
|
|
137
|
+
/** Certificate-choice session-ий хүсэлтэд илгээсэн нэр (cert холбоосын authCode-д). */
|
|
138
|
+
relyingPartyName?: string;
|
|
139
|
+
/** Sign / certificate-choice session-ий ТООЦООЛСОН дуусах мөч (ms epoch, {@link sessionExpiry}); auth-д undefined. */
|
|
140
|
+
expiresAt?: number;
|
|
102
141
|
}
|
|
103
142
|
/** Session poll-ийн нэгтгэсэн үр дүн (validator-т орох түүхий материал). */
|
|
104
143
|
interface SessionResult {
|
|
@@ -444,4 +483,4 @@ declare class ValidationError extends EidError {
|
|
|
444
483
|
declare class DeviceLinkError extends EidError {
|
|
445
484
|
}
|
|
446
485
|
|
|
447
|
-
export { type AcspContext as A,
|
|
486
|
+
export { type AcspContext as A, type SignatureProtocol as B, type CertificateLevel as C, type DeviceLinkSession as D, EidError as E, type FlowType as F, type TokenCertStatus as G, type HashAlgorithm as H, type Interaction as I, deviceLink as J, isMobileUserAgent as K, openOrShowQR as L, parseRetryAfter as M, type NotificationSession as N, ON_BEHALF_ERROR_CODES as O, type PersonBlock as P, sessionExpiry as Q, type RpCredentials as R, type SignatureAlgorithm as S, type TokenCertSessionBlock as T, ValidationError as V, type SessionResult as a, type PdfSessionBlock as b, type RepresentationsResponse as c, ApiError as d, AuthenticationError as e, DEFAULT_SIGN_SESSION_TTL_MS as f, DeviceLinkError as g, type DeviceLinkInput as h, type EndResult as i, ForbiddenError as j, type HttpErrorInit as k, type NavigatorLike as l, NetworkError as m, type OnBehalfErrorCode as n, type OpenOrShowQROptions as o, type OrgResult as p, type PdfDocumentStatus as q, type PdfSigner as r, RETRYABLE_ERROR_CODES as s, type Representation as t, ResponseTooLargeError as u, type RetryableErrorCode as v, SessionFailedError as w, type SessionState as x, type SignContext as y, type SignatureAlgorithmParameters as z };
|
|
@@ -72,7 +72,38 @@ interface NotificationSession {
|
|
|
72
72
|
rpChallenge: string;
|
|
73
73
|
/** Auth session-д validator-т өгөх контекст (sign session-д undefined). */
|
|
74
74
|
acsp?: AcspContext;
|
|
75
|
+
/** Sign session-ий хүсэлтэд илгээсэн утгууд ({@link SignContext}); auth / certificate-choice-д undefined. */
|
|
76
|
+
sign?: SignContext;
|
|
77
|
+
/** Sign / certificate-choice session-ий ТООЦООЛСОН дуусах мөч (ms epoch) — {@link sessionExpiry}; auth-д undefined. */
|
|
78
|
+
expiresAt?: number;
|
|
75
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Sign session-ий хүсэлтэд илгээсэн утгууд ЯГ ХЭВЭЭР: `validateSign` (digest, hashAlgorithm, signatureAlgorithm) ба
|
|
82
|
+
* device link (`buildDeviceLink`: digest, relyingPartyName, interactions, initialCallbackUrl)-д. RP session-тэй хамт
|
|
83
|
+
* серверт хадгална.
|
|
84
|
+
*/
|
|
85
|
+
interface SignContext {
|
|
86
|
+
/** Илгээсэн digest (стандарт base64, ЯГ ХЭВЭЭР — device link-ийн authCode-д ордог). */
|
|
87
|
+
digest: string;
|
|
88
|
+
hashAlgorithm: HashAlgorithm;
|
|
89
|
+
signatureAlgorithm: SignatureAlgorithm;
|
|
90
|
+
relyingPartyName: string;
|
|
91
|
+
/** Илгээсэн interactions-ийн base64 МӨР. */
|
|
92
|
+
interactions: string;
|
|
93
|
+
initialCallbackUrl: string;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* CA-ийн SIGNATURE ба CERTIFICATE_CHOICE session-ий анхдагч хүчинтэй хугацаа (мс; `EID_SIGN_SESSION_TTL_SECONDS` = 600).
|
|
97
|
+
* CA-ийн админ «Системийн тохиргоо»-нд 60–1800 секундэд өөрчилж болно; session start хариунд CA хугацааг буцаадаггүй
|
|
98
|
+
* (зөвхөн `sessionID`) тул SDK үүнийг `ClientConfig.signSessionTtlMs`-ээр тооцоолно.
|
|
99
|
+
*/
|
|
100
|
+
declare const DEFAULT_SIGN_SESSION_TTL_MS = 600000;
|
|
101
|
+
/**
|
|
102
|
+
* Session start-ын хариунаас тооцоолох дуусах мөч (ms epoch): хүсэлт илгээхээс ӨМНӨХ цаг + ttl. CA ExpiresAt-ийг өөрийн
|
|
103
|
+
* хүлээн авсан мөчөөс тооцдог тул энэ утга CA-ийнхаас ХЭЗЭЭ Ч хойно биш (консерватив). RP UI-ийн тоолуур / «дахин
|
|
104
|
+
* эхлүүлэх» шийдвэрт л — эцсийн үнэн нь `GET /session` (`TIMEOUT`).
|
|
105
|
+
*/
|
|
106
|
+
declare function sessionExpiry(sentAtMs: number, ttlMs?: number): number;
|
|
76
107
|
/**
|
|
77
108
|
* Device-link (QR/Web2App/App2App) session. Иргэн QR уншиж эсвэл товч дарж нэгдэнэ.
|
|
78
109
|
* `sessionToken`, `sessionSecret`-ийг ЗӨВХӨН backend-д хадгална — браузер руу бүү гарга.
|
|
@@ -99,6 +130,14 @@ interface DeviceLinkSession {
|
|
|
99
130
|
* ЗӨВХӨН backend-д хадгална.
|
|
100
131
|
*/
|
|
101
132
|
receivedAt: number;
|
|
133
|
+
/** auth | sign | cert ({@link deviceLinkInput}-д). Хуучин хадгалсан session-д undefined = auth. */
|
|
134
|
+
sessionType?: "auth" | "sign" | "cert";
|
|
135
|
+
/** Sign session-д ({@link deviceLinkInput}, `validateSign`); бусад үед undefined. */
|
|
136
|
+
sign?: SignContext;
|
|
137
|
+
/** Certificate-choice session-ий хүсэлтэд илгээсэн нэр (cert холбоосын authCode-д). */
|
|
138
|
+
relyingPartyName?: string;
|
|
139
|
+
/** Sign / certificate-choice session-ий ТООЦООЛСОН дуусах мөч (ms epoch, {@link sessionExpiry}); auth-д undefined. */
|
|
140
|
+
expiresAt?: number;
|
|
102
141
|
}
|
|
103
142
|
/** Session poll-ийн нэгтгэсэн үр дүн (validator-т орох түүхий материал). */
|
|
104
143
|
interface SessionResult {
|
|
@@ -444,4 +483,4 @@ declare class ValidationError extends EidError {
|
|
|
444
483
|
declare class DeviceLinkError extends EidError {
|
|
445
484
|
}
|
|
446
485
|
|
|
447
|
-
export { type AcspContext as A,
|
|
486
|
+
export { type AcspContext as A, type SignatureProtocol as B, type CertificateLevel as C, type DeviceLinkSession as D, EidError as E, type FlowType as F, type TokenCertStatus as G, type HashAlgorithm as H, type Interaction as I, deviceLink as J, isMobileUserAgent as K, openOrShowQR as L, parseRetryAfter as M, type NotificationSession as N, ON_BEHALF_ERROR_CODES as O, type PersonBlock as P, sessionExpiry as Q, type RpCredentials as R, type SignatureAlgorithm as S, type TokenCertSessionBlock as T, ValidationError as V, type SessionResult as a, type PdfSessionBlock as b, type RepresentationsResponse as c, ApiError as d, AuthenticationError as e, DEFAULT_SIGN_SESSION_TTL_MS as f, DeviceLinkError as g, type DeviceLinkInput as h, type EndResult as i, ForbiddenError as j, type HttpErrorInit as k, type NavigatorLike as l, NetworkError as m, type OnBehalfErrorCode as n, type OpenOrShowQROptions as o, type OrgResult as p, type PdfDocumentStatus as q, type PdfSigner as r, RETRYABLE_ERROR_CODES as s, type Representation as t, ResponseTooLargeError as u, type RetryableErrorCode as v, SessionFailedError as w, type SessionState as x, type SignContext as y, type SignatureAlgorithmParameters as z };
|
package/dist/browser.d.cts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { g as DeviceLinkError, h as DeviceLinkInput, D as DeviceLinkSession, E as EidError, l as NavigatorLike, o as OpenOrShowQROptions, J as deviceLink, K as isMobileUserAgent, L as openOrShowQR } from './browser-CuMH5IVG.cjs';
|
package/dist/browser.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { g as DeviceLinkError, h as DeviceLinkInput, D as DeviceLinkSession, E as EidError, l as NavigatorLike, o as OpenOrShowQROptions, J as deviceLink, K as isMobileUserAgent, L as openOrShowQR } from './browser-CuMH5IVG.js';
|