Citizen PKI dashboard — RP-facing endpoints (Go-only extension)¶
Date: 2026-07-04. Request: template.gerege.mn/docs/EID_ENDPOINT_REQUESTS.md §B.
A citizen views their own PKI through an RP's dashboard (e.g., template.gerege.mn "My system"):
certificates + counts, RP-scoped activity history/counts, associated devices, dashboard totals.
Privacy — only specifically authorized RPs¶
These endpoints return a citizen's PII, so they are NOT OPEN to all RPs. The calling RP must have
the PKI_READ permission — granted individually to each RP by an admin (admin console → Relying
party (RP) → edit RP → "PKI_READ" checkbox). An unauthorized RP → 403.
first-party(the operator's own web/admin demo) and dev ("") — always allowed.- Activity is RP-scoped: an RP sees only the sessions it created (other RPs' logins/signatures are not exposed).
Authentication: as in v3, Authorization: Bearer <rp_sk_…>.
Endpoints (all require the PKI_READ permission)¶
GET /v3/certificates/etsi/{personEtsi}¶
A citizen's full set of certificates + status counts.
{
"personEtsi": "PNOMN-...",
"counts": { "valid": 2, "revoked": 1, "expired": 0, "suspended": 1, "total": 4 },
"certificates": [
{ "documentNumber":"…", "type":"AUTH|SIGN|SEAL", "serialNumber":"…",
"certificateLevel":"ADVANCED|QUALIFIED|QSCD",
"status":"VALID|REVOKED|EXPIRED|SUSPENDED",
"notBefore":"RFC3339", "notAfter":"RFC3339", "issuerDn":"…" }
]
}
GET /v3/devices/etsi/{personEtsi}¶
A citizen's associated devices (active + inactive).
{
"personEtsi":"PNOMN-...", "activeCount":1, "total":2,
"devices":[ { "documentNumber":"…", "platform":"APNS|FCM",
"enrolledAt":"RFC3339", "active":true, "deactivatedAt":null } ]
}
GET /v3/rp/activity/etsi/{personEtsi} — RP-scoped¶
History + counts of only the sessions created by the calling RP.
Query: ?flow=AUTHENTICATION|SIGNATURE&limit=20&offset=0
{
"personEtsi":"PNOMN-...",
"counts": { "authentication": 42, "signature": 7 },
"sessions": [ { "sessionId":"…", "flow":"AUTHENTICATION", "outcome":"OK",
"docText":"…", "timestamp":"RFC3339" } ],
"total": 49, "limit": 20, "offset": 0
}
audit_events (SESSION_AUTH/SESSION_SIGN, persisted across restart). RP-scoping is
filtered by the audit detail's crp (creator RP UUID).
GET /v3/person/summary/etsi/{personEtsi} — dashboard totals¶
The dashboard's combined totals (in one call).
{
"personEtsi":"PNOMN-...", "givenName":"…", "surname":"…",
"certificates": { "valid":2, "revoked":1, "expired":0, "suspended":1, "total":4 },
"activity": { "authentication":42, "signature":7 },
"devicesActive":1, "devicesTotal":2,
"representationCount":1
}
Well-known¶
GET /.well-known/eid → endpoints.person.{certificates,devices,activity,summary} (marked PKI_READ).
Implementation¶
- Service:
service.PersonPKIService(server/internal/service/memory/person.go). - Handlers:
server/internal/httpapi/handlers_person.go(rpAuth +requirePKIReadgate). - Permission:
PKI_READin the RP.Permissions CSV (checkbox in the admin console's RP form). - Nonexistent citizen → 404; unauthorized RP → 403; no Bearer → 401 (RequireRPAuth).