gerege.mn — eID Gerege Integration (auth · sign · push)¶
A guide to connecting gerege.mn as an RP to eID Gerege to obtain authentication and electronic
signatures via the App2App (same-device) and Push (cross-device) flows. The wire protocol is Smart-ID
compatible (ACSP_V2).
General RP guide: RP_INTEGRATION.md. This file contains the actual gerege.mn configuration + copy-paste examples.
0. gerege.mn RP configuration (registered)¶
| Field | Value |
|---|---|
| relyingPartyUUID | 43a7bda9-0d68-4324-9ec1-3fc6dd8e4edf |
| relyingPartyName | gerege.mn |
| API secret (Bearer) | rp_sk_… — obtained from the operator, stored in the $GEREGE_RP_SECRET env (server keeps only the SHA-256 hash) |
| RP-API base | https://rp-api.eidmongolia.mn/v3 |
| Callback host allowlist | gerege.mn, www.gerege.mn (the App2App return URL must be one of these) |
| App deep-link scheme | geregesmartid://approve?sessionId=… |
| Discovery | GET https://rp-api.eidmongolia.mn/.well-known/eid |
On every call:
- Header: Authorization: Bearer $GEREGE_RP_SECRET
- Body: relyingPartyUUID, relyingPartyName, certificateLevel (QUALIFIED), signatureProtocol (ACSP_V2)
1. Authentication — App2App (same-device, browser + app on one phone)¶
When the user visits gerege.mn from their phone: create a session → open the app via deep-link → the user confirms with their PIN → the app returns to the callback → poll the session result.
1.1 Start a session (with initialCallbackUrl)¶
curl -sS -X POST https://rp-api.eidmongolia.mn/v3/authentication/device-link/anonymous \
-H "Authorization: Bearer $GEREGE_RP_SECRET" -H "Content-Type: application/json" \
-d '{
"relyingPartyUUID": "43a7bda9-0d68-4324-9ec1-3fc6dd8e4edf",
"relyingPartyName": "gerege.mn",
"certificateLevel": "QUALIFIED",
"signatureProtocol": "ACSP_V2",
"initialCallbackUrl": "https://gerege.mn/auth/eid/callback?state=<csrf>",
"interactions": [{"type":"displayTextAndPIN","displayText60":"gerege.mn нэвтрэх"}]
}'
{ "sessionID": "…", "sessionToken": "…", "sessionSecret": "…",
"deviceLinkBase": "https://eidmongolia.mn/dl", "vc": "2025" }
⚠️ The host of
initialCallbackUrlmust be on the allowlist (gerege.mn/www.gerege.mn) — otherwise the server silently drops it (open-redirect/phishing protection). The server keeps only the host + query and force-normalizes the path to/auth/eid/callback, so place your RP return page at this path. You can display thevc(4-digit code) on the gerege.mn screen so the user can match it against the one on their phone.
1.2 Open the app (deep-link)¶
Use the sessionID from the response to open the app from the browser:
initialCallbackUrl.
1.3 Poll the result → [§4]¶
2. Authentication — Push (cross-device: user is on a different device/computer)¶
If you know the user by registration number/civil ID/document, send a push to their phone.
# ETSI (PNOMN-<civilId>) or directly by registration/civil ID for push (the server detects the type)
curl -sS -X POST https://rp-api.eidmongolia.mn/v3/authentication/notification/etsi/PNOMN-<civilId> \
-H "Authorization: Bearer $GEREGE_RP_SECRET" -H "Content-Type: application/json" \
-d '{
"relyingPartyUUID": "43a7bda9-0d68-4324-9ec1-3fc6dd8e4edf",
"relyingPartyName": "gerege.mn",
"certificateLevel": "QUALIFIED",
"signatureProtocol": "ACSP_V2",
"interactions": [{"type":"displayTextAndPIN","displayText60":"gerege.mn нэвтрэх"}]
}'
# → { "sessionID": "…", "vc": { "type":"alphaNumeric4", "value":"2422" } }
vc.value on the gerege.mn screen — the user matches it against the code in the push they
received on their phone and confirms. You can also send a push by document/{documentNumber} (device UUID).
3. Electronic signature (Signature) — PIN2, non-repudiation¶
The document's SHA-256 digest (base64) is signed on the phone with PIN2 (signing key). The phone computes the VC from the signed digest, so the code the user sees = the content being signed (WYSIWYS).
3.1 Via Push (cross-device)¶
DIGEST=$(sha256sum document.pdf | cut -d' ' -f1 | xxd -r -p | base64)
curl -sS -X POST https://rp-api.eidmongolia.mn/v3/signature/notification/document/<documentNumber> \
-H "Authorization: Bearer $GEREGE_RP_SECRET" -H "Content-Type: application/json" \
-d "{
\"relyingPartyUUID\": \"43a7bda9-0d68-4324-9ec1-3fc6dd8e4edf\",
\"relyingPartyName\": \"gerege.mn\",
\"certificateLevel\": \"QUALIFIED\", \"signatureProtocol\": \"ACSP_V2\",
\"digest\": \"$DIGEST\", \"hashType\": \"SHA256\",
\"interactions\": [{\"type\":\"displayTextAndPIN\",\"displayText60\":\"gerege.mn гарын үсэг\"}]
}"
# Via ETSI: /signature/notification/etsi/PNOMN-<civilId>
3.2 Via App2App (same-device)¶
POST /v3/signature/device-link/document/{documentNumber} — body with digest, hashType,
initialCallbackUrl (gerege.mn), then open the app via geregesmartid://approve?sessionId=….
3.3 After completion — PAdES PDF (optional)¶
As soon as the poll returns endResult=OK, stamp the original PDF and download a PDF embedding a
validation page + RFC 3161 timestamp (PAdES-T):
curl -sS -X POST "https://rp-api.eidmongolia.mn/v3/signature/stamp/<sessionID>?fileName=document.pdf" \
-H "Authorization: Bearer $GEREGE_RP_SECRET" --data-binary @document.pdf -o signed.pdf
https://eidmongolia.mn/verify/<sessionID> (public; also in the QR).
4. Poll the session result (long-poll)¶
curl -sS "https://rp-api.eidmongolia.mn/v3/session/<sessionID>?timeoutMs=120000" \
-H "Authorization: Bearer $GEREGE_RP_SECRET"
state=RUNNING — in progress (poll again)
- state=COMPLETE + result.endResult=OK →
- signature.value — detached ECDSA signature (signature.signatureAlgorithm=ecdsa-with-SHA256)
- cert.value — citizen's X.509 (auth or sign), cert.certificateLevel
- state=COMPLETE + other result.endResult (USER_REFUSED, TIMEOUT, WRONG_VC…) → refused
Auth validation (gerege.mn side): chain the returned cert to the eID Gerege root CA (OCSP:
endpoints.ocsp from the discovery doc) and verify that rpChallenge (if sent) is included in the
signature.
5. Security requirements (gerege.mn side)¶
initialCallbackUrlmust behttps://+ an allowlist host (gerege.mn/www.gerege.mn). Other hosts are silently dropped.- Include the
state(CSRF) parameter in the callback URL and verify it on return. - Keep the API secret on the backend only (never expose it in the browser). RP-API calls are server-to-server.
- Showing the VC code to the user to match against the phone prevents fisher-in-the-middle attacks.
- Poll session TTL is 10 min (
SMARTID_SESSION_TTL_SECONDS).
6. Brief flow diagram (App2App authentication)¶
gerege.mn (browser) eID Gerege RP-API eID Gerege app (phone)
│ POST device-link/anonymous │ │
│ (+ initialCallbackUrl) ───▶ │ create session (check │
│ ◀── sessionID, vc │ callback allowlist) │
│ geregesmartid://approve?sessionId=… ───────────────────────▶ │ app opens
│ │ │ PIN1 → threshold auth
│ │ ◀──── commit/prove/finish ─── │
│ ◀───────── callback: https://gerege.mn/auth/eid/callback?state=… ── │ (returns)
│ GET session/{id} (poll) ───▶ │ │
│ ◀── COMPLETE, endResult=OK, cert ─│ │
Example working client: web/src/lib/rpclient.ts (TypeScript),
or the sdk/typescript/ SDK.