Authentication & authorization¶
Every platform in the ecosystem uses one authentication model. This page explains that model and gives practical guidance for a relying party (RP) joining for the first time.
The model¶
sequenceDiagram
participant U as User
participant RP as RP app
participant SSO as Gerege SSO
participant EID as eID Mongolia
participant P as Phone
U->>RP: Clicks sign in
RP->>SSO: Authorization request (code + PKCE)
SSO->>EID: Start eID authentication
EID->>P: QR / deep link / push
P-->>EID: PIN1 approval
EID-->>SSO: Identified citizen
SSO-->>RP: Authorization code
RP->>SSO: code + code_verifier → tokens
SSO-->>RP: access + refresh + id_token
RP->>SSO: /userinfo
SSO-->>RP: User claims
The core principle: the RP never reaches eID directly. Everything goes through SSO.
Sign-in methods¶
| Method | Kind | Notes |
|---|---|---|
| eID | Primary | QR code · mobile deep link · push by national ID number |
| Secondary | Must be verified with eID the first time |
Not available: passwords, email/OTP sign-in, SMS OTP sign-in.
This is deliberate. If there is no password, there is no password to leak, reuse or phish.
Where a platform authenticates — AUTH_MODE¶
A platform in this ecosystem can play one of two roles:
- Identity service — it authenticates users itself. The sign-in card
(eID QR / national ID · Google) appears on its own landing page and
/login. - Relying party — it delegates sign-in to an upstream SSO. Pressing "Sign in" redirects to the SSO, the user authenticates there and comes back.
These two are not a code difference — they are configuration. The
platform's backend setting AUTH_MODE decides:
| Value | Sign-in surface |
|---|---|
provider |
The sign-in card is rendered on this platform |
client |
Redirect to the upstream SSO (SSO_ISSUER) |
When unset it is derived from whether SSO_CLIENT_ID is configured.
The frontend reads its mode from the public GET /api/v1/site/auth endpoint —
no authentication required, no secrets in the payload:
Being an issuer is a SEPARATE question
AUTH_MODE answers "where do this platform's users sign in".
Whether the platform is itself an issuer for other apps is decided
separately by OAUTH_ISSUER. Both can be active at once — a chained
setup where a platform issues tokens to others while sending its own users
to an upstream IdP.
The practical result: an SSO service and a platform that consumes it run the same code. The identical Docker image boots into either role depending on its environment. See Shared code for the wider picture.
PIN1 vs PIN2¶
| PIN1 | PIN2 | |
|---|---|---|
| Certificate | Authentication | Signing |
| Purpose | Sign in | Produce a signature |
| Legal effect | None | Yes — non-repudiation |
Signing in ≠ signing
Authenticating with PIN1 does not mean the user consented to anything. Actions with legal effect (contracts, granting rights, financial obligations) must be signed separately with PIN2.
OIDC specifics¶
| Item | Value |
|---|---|
| Flow | Authorization code + PKCE (S256) |
| Access token | Opaque |
id_token |
JWT, RS256 |
| Refresh token | Rotating, with reuse detection |
| Machine-to-machine | client_credentials |
| Discovery | /.well-known/openid-configuration |
| UserInfo | /userinfo |
Refresh token rotation¶
Every use of a refresh token issues a new one and invalidates the old. If an old token is presented again — a sign of theft — the entire chain is revoked.
So an RP must persist the new token after every refresh. Keeping the old one will tear down all sessions on the next refresh.
Sessions and logout¶
- A session is a JWT access + refresh pair.
- Logout revokes both the refresh and the access token (access deny-list).
- After logout the user returns to the domain they started on.
Becoming an RP¶
1. Register the application¶
Create a client in the Gerege SSO console. You receive a
client_id and client_secret.
Have ready before registering:
- Redirect URIs (for every environment — dev / staging / prod)
- Post-logout redirect URI
- The scopes you need
- Application name and logo (shown on the user's consent screen)
2. Read discovery¶
Do not hard-code endpoints — read them from here.
3. Authorization request¶
Implement authorization code + PKCE (S256). Always use state and nonce.
4. Exchange the code¶
Code + code_verifier → access_token, refresh_token, id_token.
5. Validate the id_token¶
Mandatory checks:
- [ ] RS256 signature, using a key from JWKS
- [ ]
issmatches the issuer from discovery - [ ]
audis yourclient_id - [ ]
exphas not passed - [ ]
noncematches the one you sent
6. Fetch user info¶
From the /userinfo endpoint.
Common mistakes¶
Redirect URIs must match exactly
Character for character: a trailing /, http vs https, the port and the
path all matter. This is the single most common integration failure.
Checklist when changing a domain
Changing a domain or brand requires updating all three of the following. Miss one and authentication fails silently:
- [ ] The redirect URI list in SSO
- [ ] The TLS certificate SAN
- [ ] The issuer / endpoint addresses in the RP's configuration
Do not skip PKCE
Use PKCE even for confidential clients. The cost is negligible; the protection is real.
Authorization model¶
Once authenticated, authorization begins. The ecosystem's standard hierarchy:
See Platform conventions for the detail.
At the organisation level: membership is protected by Postgres RLS — a user sees only the data of organisations they belong to. This is a database-level constraint, not an application code check.
Granting manager requires the person to approve with PIN2 — see
Platform conventions.