Skip to content

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 нэвтрэх"}]
  }'
Response:
{ "sessionID": "…", "sessionToken": "…", "sessionSecret": "…",
  "deviceLinkBase": "https://eidmongolia.mn/dl", "vc": "2025" }

⚠️ The host of initialCallbackUrl must 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 the vc (4-digit code) on the gerege.mn screen so the user can match it against the one on their phone.

Use the sessionID from the response to open the app from the browser:

geregesmartid://approve?sessionId=<sessionID>
The app opens and the user confirms with PIN1 (authentication key). Then the app returns to your 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" } }
Display 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
Verify: 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)

  • initialCallbackUrl must be https:// + 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.