Skip to content

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
Google 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:

{ "mode": "client", "sso_issuer": "https://sso.gerege.mn", "provider": false }

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

GET https://sso.gerege.mn/.well-known/openid-configuration

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_verifieraccess_token, refresh_token, id_token.

5. Validate the id_token

Mandatory checks:

  • [ ] RS256 signature, using a key from JWKS
  • [ ] iss matches the issuer from discovery
  • [ ] aud is your client_id
  • [ ] exp has not passed
  • [ ] nonce matches 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:

superadmin (1) → admin (2) → manager (3) → user (4)

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.