sg-gov-singpass · git:20261027.6f21efa · 2026-10-27 · sha256 a0fa852c10f3f692
sg-gov-singpass git:20261027.6f21efaA
Immutable. This exact content never changes and is served at /api/v1/blob/a0fa852c10f3f692.
---
name: sg-gov-singpass
description: >-
Integrate Singpass (citizen/resident login and Myinfo (v5) government-verified
person data) into a Singapore government digital service using the current
FAPI 2.0 Singpass authentication API. Use this whenever a service authenticates
individual members of the public or pre-fills their personal data: "log in with
Singpass", Singpass Login, Myinfo, NRIC/UUID identity, PAR, DPoP, userinfo,
the Singpass Developer Portal (SDP), consent pages, Myinfo scopes (uinfin,
name, CPF, NOA, HDB), test personas, or debugging invalid_client /
invalid_dpop_proof / upstream_dependency_error. Covers the 5-step flow, the
UUID-first identity model, Myinfo data compliance, onboarding, and app-review
UX rules. For Myinfo v3/v4 or pre-FAPI integrations and their migration
deadlines use sg-gov-singpass-legacy; for business users acting for companies
use sg-gov-corppass.
---
# Singpass: citizen login and Myinfo (v5) on FAPI 2.0
Singpass is Singapore's national digital identity: an OpenID Connect provider that
supports the authorization-code flow only, and relying parties must be confidential
clients. Two products share one API and one onboarding portal:
- **Singpass Login** — authentication only. The flow ends at the ID token.
- **Singpass Myinfo (v5)** — Login plus consented retrieval of government-verified
person data from the `/userinfo` endpoint. Every Singpass user automatically has a
Myinfo profile.
The current API is **fully OIDC + FAPI 2.0 compliant** (new apps are FAPI 2.0 by
default since Feb 2026; **all Singpass APIs must be FAPI 2.0-compliant by
31 Dec 2026**). Use a **certified OIDC relying-party library** (openid.net certified
list) rather than hand-rolling. Singpass says so explicitly, and the library handles
PAR, PKCE, DPoP, and claim validation correctly. If you are maintaining a
Myinfo v3/v4 integration or a pre-FAPI Login flow, read **sg-gov-singpass-legacy**; it
covers the different endpoints, crypto, and hard deadlines.
Most important identity rule: **the ID token `sub` is a UUID, never the NRIC.** NRIC/FIN arrives only if you request the justification-gated `user.identity`
scope (`sub_attributes.identity_number`) or the `uinfin` Myinfo scope. Key your user
records on the UUID; the PDPC NRIC advisory guidelines apply once you store NRIC
instead.
## Source and currency
Embedded from https://docs.developer.singpass.gov.sg as of **2026-07-23**. The docs
iterate; verify compliance-critical details against the live pages. Append `.md` to any docs page URL
for markdown; index at https://docs.developer.singpass.gov.sg/docs/llms.txt. Read
the reference files for exact wire formats:
- `references/fapi2-integration.md` — endpoints, all five steps with every
parameter, ID-token/userinfo claims, JWKS/client-assertion/DPoP/PKCE specs,
error tables, debugging map.
- `references/myinfo-data-and-compliance.md` — the Myinfo data catalog, envelope
format, display rules, key principles, SDP onboarding, testing, pricing,
scheduled downtimes, UX/brand requirements, CIBA.
## The five steps
Backend-for-frontend; all secrets and signing server-side.
1. **PAR.** `POST {issuer}/par` (issuer `https://id.singpass.gov.sg/fapi`, staging
`stg-id`) with fresh `state` + `nonce` (backend-generated v4 UUIDs, persisted
against the session), PKCE `code_challenge` (S256), a client assertion
(ES256-family JWT: `iss`/`sub` = client_id, `aud` = the FAPI issuer, unique
`jti`, **`exp` − `iat` ≤ 2 minutes**), and a `DPoP` header signed with an
**ephemeral EC key generated for this session**. Login apps must also send
`authentication_context_type` (anti-fraud enum, e.g.
`APP_AUTHENTICATION_DEFAULT`); it is mandatory for Login apps and not for
Myinfo apps. Response: `request_uri`, valid **60 seconds**.
2. **Redirect** to `{issuer}/auth?client_id=...&request_uri=...` with nothing else
in the URL. On callback validate `state` (reject mismatch — CSRF) and `iss`; handle
error callbacks (`server_error`, `temporarily_unavailable`) and never render
`error_description` verbatim (content spoofing).
3. **Token exchange within 60 seconds of code issuance**: fresh client assertion,
same DPoP key as PAR, `code_verifier`. Returns `token_type: DPoP`, an access
token (**30 minutes**; Login apps ignore it) and an **always-encrypted** ID
token.
4. **Decrypt and validate the ID token**: JWE (ECDH-ES+A256KW / A256CBC-HS512,
decrypt with your `enc` key selected by JWE `kid`) wrapping a JWS (ES256, verify
against `https://id.singpass.gov.sg/.well-known/keys` by `kid`). Then check
`iss`, `aud`, `exp`, and `nonce`. Login integrations stop here.
5. **Userinfo (Myinfo apps only).** `GET {issuer}/userinfo` with
`Authorization: DPoP <access_token>` (not `Bearer`) and a DPoP proof carrying
`ath` (base64url SHA-256 of the access token). The response is the same
JWE-wrapped-JWS scheme; person data sits under `person_info` in the classic
Myinfo envelope (`value`/`code`+`desc` with `classification`, `source`,
`lastupdated`).
## Identity: what comes back
| You requested | You get |
|---|---|
| `openid` only | `sub` (UUID), `sub_type: "user"`, `amr`, `acr`, `nonce` |
| + `user.identity` | `sub_attributes`: `account_type` (`standard`/`foreign`), `identity_number` (NRIC/FIN/foreign ID), `identity_coi` |
| + `name` / `email` / `mobileno` | those `sub_attributes` (email/mobileno only if registered; `mobileno` always absent for foreign-account users) |
| Myinfo scopes (`uinfin`, `dob`, `cpfbalances.oa`, ...) | attributes in `person_info` at userinfo |
Read `acr` for assurance level (`urn:singpass:authentication:loa:2` = 2FA default;
`loa:3` adds face verification, whitelisted use cases only and **chargeable since
1 Jan 2026**; `loa:1` appears only in staging). Don't build logic on `amr` values;
the list is non-exhaustive and changes.
**Singpass Foreign Account (SFA) users** can log in (`account_type: "foreign"`) but
**have no Myinfo**: userinfo returns `invalid_request` for them. Design a manual
path for them.
## Compliance and UX rules that fail app review
The production app review enforces the Key Principles. Build these in from the start:
- **A non-Singpass alternative is mandatory**: Singpass cannot be the only
authentication or form-filling option.
- **Display all retrieved Myinfo data** on the form before submission; no silent
backend-only retrieval. Government-verified fields (`source: "1"`) are
**non-editable**; user-provided fields (`email`, `mobileno`, and the
MSF-sourced marital fields) **must stay editable**.
- **Purge unsubmitted data**; purge save-as-draft data periodically; **never reuse
session/cookie-cached Myinfo data**; every retrieval is a fresh fetch.
- **Request only what you need**: one purpose per app, every scope justified, each
request reviewed. The consent page cannot offer per-field selection.
- Display rules for specific data: all 15 months of CPF contribution history shown
even if you need 3; NOA (Detailed) must show year, type (+ "Clearance" when tax
clearance = Y), assessable income and breakdown; Principal Name shown
non-editable even when you also capture split name fields.
- **New Login/Myinfo apps support QR login only** (national scam prevention; no
password/SMS-OTP flows) and **WebView embedded browsers are unsupported**; use
in-app browser patterns (see the official iOS/Android demo repos).
- Singpass button: white or red fill only, "Log in" as two words, aria-label so
screen readers say "Sing pass". App logo: square PNG, transparent, 256–512 px,
≤200 KB, no padding.
## Onboarding (Singpass Developer Portal)
SDP is at https://developer.singpass.gov.sg. Access is **authorised via Corppass** by
your entity's Corppass Admin. Singapore-registered entities only. Staging first:
create the app, register redirect URLs (https; no IP-address URLs; custom schemes
unsupported; mobile uses app-claimed HTTPS universal links), configure JWKS
(endpoint or object), pick scopes, create **staging test accounts (max 5 per
entity)**. Myinfo test personas (password `Userinfo@2024`) are for Myinfo testing
only, not Login. Production needs the Singpass Services Agreement, billing
contacts, and a **User Journey document**; approval takes **up to two weeks**;
config changes re-enter review and propagate in 5–10 minutes after approval.
Pricing: government agencies get the WOG-intranet pricing plan (not public). For
others, Login is pay-per-transaction; Myinfo bills **Standard vs Plus** tiers: any
single Plus (financial) scope in a transaction bills the whole transaction at Plus.
Request a `cpfbalances.*` scope only when the service needs it, because it bills the whole transaction at Plus.
Validate your crypto with the official tool:
https://developer.singpass.gov.sg/troubleshooting-tool (JWKS, client assertions,
DPoP, PKCE). Demo app: https://github.com/singpass/demo-app.
## Operational hard rules
- **60-second limits**: `request_uri` and authorization code both expire in 60 s;
client assertion and DPoP `exp` ≤ 2 min after `iat`; access token 30 min.
- **Your JWKS endpoint is on the user-facing critical path**: ≥1 `sig` + ≥1 `enc`
EC key (P-256/384/521), public HTTPS, **3-second response budget**, never expose
`d`, never reuse retired `kid`s. Failures surface as `invalid_client` /
`upstream_dependency_error` during user logins. Rotate at least yearly with
the 1-hour-cache choreography.
- Retry policy is normative: at most 3 retries with exponential backoff on
`server_error` / `upstream_dependency_error` / `temporarily_unavailable`, then
guide users to the alternative path.
- Never pin Singpass TLS certs; never hardcode Singpass keys or `kid`s; select by
`kid` from a ≥1-hour-cached JWKS, re-fetch once on validation failure.
- Plan for **scheduled upstream downtimes** (CPFB/IRAS/MOM maintenance windows;
see reference): Myinfo financial scopes return 502 `upstream_dependency_error`
during them; map to a friendly message and the manual path.
- **CIBA** (backchannel push-notification step-up auth) exists for high-risk
transactions: government agencies only, case-by-case whitelisting, poll mode,
and it runs on the pre-FAPI endpoint family. See the reference before designing
for it.
## Audit checklist
| Check | Failure smell |
|---|---|
| Certified RP library; PAR + PKCE + DPoP present | hand-rolled `/auth` URL with scopes in the browser |
| `sub` treated as UUID; NRIC only via `user.identity`/`uinfin` | NRIC parsed out of `sub`, NRIC as DB key |
| Client assertion ≤2 min, `aud` = FAPI issuer, fresh `jti` | hour-long assertions, `aud` = bare host |
| Ephemeral DPoP key per session, `ath` at userinfo | static DPoP key, `Bearer` on userinfo |
| `state`/`nonce`/`iss` validated; errors handled on callback | callback trusts `code` alone |
| JWKS: `sig` + `enc`, 3 s, yearly rotation plan | sig-only JWKS, no rotation, `d` exposed |
| Myinfo data displayed, correct editability, purged, re-fetched | cached profiles, editable NRIC field |
| Manual/non-Singpass path exists incl. SFA users | Singpass-only service |
| QR-only login assumed; no WebViews | password-login UX, embedded WebView |
| Plus-tier scopes consciously chosen | `cpfbalances.*` requested "just in case" |
## What this skill does not cover
- **Business users acting for companies** (UEN, entity roles, Myinfo Business) —
the **sg-gov-corppass** skill. If the user represents an organisation, it's Corppass.
- **Myinfo v3/v4 and pre-FAPI integrations** — the **sg-gov-singpass-legacy** skill:
different endpoints (`api.myinfo.gov.sg/com/v3|v4`), different crypto
(client_secret + PKI_SIGN, X.509), the comma-separated legacy `sub`, and the
migration deadlines.
- **Sign with Singpass** (Secure Electronic Signatures) and **Verify** — separate
products with separate docs (docs.sign.singpass.gov.sg); this skill only routes
you there.
- **PDPA, NRIC retention, data classification policy** — **sg-gov-data-protection**.
- **The catalog controls mandating Singpass** — AC-7 and related controls live in
**sg-gov-access-control**. This skill implements the login and TX-6 pre-fill;
**sg-gov-service-shell** owns the surrounding DSS page shell. Session hardening,
secrets, and error handling around the flow are **sg-gov-secure-coding-as**.
- **The transaction around the login** — the **sg-gov-transactions-payments** skill owns
the rest of the DSS TX family (multi-step flow, save draft, payment/refund UX,
receipts, status tracking) that consumes this skill's TX-6 Myinfo pre-fill.