SGFinDex API (2.0.11-release)

Download OpenAPI specification:Download

SGFinDex leverages on the OAuth 2.0 authorization framework that enables third-party applications to obtain user consent and retrieve data on behalf of user.

Note: This specification is subject to changes based on evolution of the APIs.

Release Notes

  • 2.0.11 release (27 Jun 2024)

    • Removed following v1 APIs:
      • Authorise
      • Token
      • Get User Data
      • Revoke Token
      • Get User Identity
    • Removed Request Signing under Security
  • 2.0.10 release (22 Nov 2023)

    • Added a new optional parameter to specify key ID for SGFinDex's V2 fpdata
  • 2.0.9 release (10 Nov 2023)

    • Updated link to reference Myinfo v4 Person API response schema
  • 2.0.8 release (06 Nov 2023)

    • Updated the decoded schema of access token returned from /consumer/v2/token API
    • Updated the error code "Invalid Consent or Public Cert" that is associated with 400 response of /consumer/v1/fpdata to "Invalid Consent"
  • 2.0.7 release (03 Oct 2023)

    • Added reference to Payload Signing and Encryption to guide on decryption and verification of payload
    • Added reference to JWKS url at Payload Signing and Encryption
  • 2.0.6 release (05 Sep 2023)

    • Added jwk.alg attribute and specified each attribute in the jwk of DPoP JWT
    • Changed Key Requirements under JWKS Requirements for Service Consumer and Provider
      • RSA encryption key alg changed from RSA-OAEP-256 to RSA-OAEP
    • Removed purpose in code example under Authorise
  • 2.0.5 release (11 Aug 2023)

    • Elaborated Key Requirements under JWKS Requirements for Service Consumer and Provider
    • Elaborated description of Client Assertion for version 2's Service Provider's revoke and useridentity
    • Added link to Client Assertion section at version 2 APIs
  • 2.0.4 release (20 Jul 2023)

    • Removed cnf.jkt from Get User Identity v2 and Revoke Token v2
    • Added RSA keys examples under JWKS Requirements for Service Consumer and Provider
    • Added code challenge as salt for hashing for Verifying User under Security section
  • 2.0.3 release (28 Jun 2023)

    • Added kid into base string signing
  • 2.0.2 release (21 Jun 2023)

    • Added Service Consumer and Provider JWKS requirements under Security section
    • Added Verifying User under Security section
    • Added Payload Signing and Encryption under Security section
    • Added hashed_uinfin in access_token returned by Token endpoint
    • Changed Get User Data identifier path parameter from uinfin to uuid
    • Corrected client assertion type values for Token v2, Get User Identity v2 and Revoke Token v2
  • 2.0.1 release (4 May 2023)

    • Amended previous release date from 23 Feb 2023 to 11 Apr 2023
    • Amended Authorise v2 input parameters
    • Added reference to Token validation in Token v2 and Data v2
    • Removed reference to Client Secret from Token v2 and Data v2
    • Removed reference to State in Token v2
    • Removed reference to Client ID from Data v2
    • Changed integrationDate in Data v2 to connectionExpiryDate as per v1
    • Added invalid bearer token in Authorization, client assertion type and code verifier under response 401 possible scenarios
    • Added invalid consent and transaction ID under response 400 possible scenarios
    • Reinstated identifier path for fpdata v2 endpoint
    • Added JWKS endpoint for Token Validation
  • 2.0.0 release (11 Apr 2023)

    • Created authorise endpoint v2
    • Created token endpoint v2
    • Created fpdata endpoint v2
    • Created useridentity endpoint v2
    • Created revoke endpoint v2
  • 1.0.9 release (29 Mar 2023)

    • Request signatures to be added with kid in format for requesters to inform kid to use against JWKS
    • Added link to reference token validation from response of token call
    • Added link to reference token validation from response of data call
  • 1.0.8 release (23 Mar 2023)

    • Support for JWKS does not require issued certificate format
    • Updated Get User Data for activity based refresh consent
      • Changed integrationDate to connectionExpiryDate in response payload
  • 1.0.7 release (24 Mar 2023)

    • AXA renamed to HSBC LIFE in data payload
    • Support for JWKS does not require certificate format
    • Updated Get User Data for activity based refresh consent
      • Added consent parameter in request body
      • Added integrationDate in response payload
  • 1.0.6 release (20 Sept 2022)

    • Updated authorise scope description to include financialdata.banking
  • 1.0.5 release (21 Jul 2022)

    • Updated sample JWKS EC and RSA key payloads and descriptions
  • 1.0.4 release (22 Jun 2022)

    • Updated sample payload and description with additional service providers
  • 1.0.3 release (16 Feb 2022)

    • Added Retrieving JSON Web Key Set (JWKS) endpoint
  • 1.0.2-release (7 Feb 2022)

    • Added the error response in 'error' field into the 'payload' field. Note for those who are still using the error response in the 'payload' field, it will be removed in the next major release.
  • 1.0.1-release (12 Nov 2021)

    • Added enumeration of values of SP under response schema of Get User Data
    • Added sample payload of the response
    • Added a field 'error' under response schema of Get User Data
    • Updated description of a field 'payload' under response schema of Get User Data
    • Added response_type parameter in specs under Authorise to reflect current system code which defaults response_type to 'code' when not provided. This is not a breaking change - clients that do not provide this value currently will not be affected.
  • 1.0.0-release (9 July 2021)

    • Mandate client_id in Login endpoint
  • 0.0.15 (2 March 2021)

    • Removed Authenticate endpoints
  • 0.0.14 (19 November 2020)

    • Updated the list of recommended TLS cipher suites
    • Added optional query parameter, appLaunchURL in Authorise and Login endpoint
  • 0.0.13 (3 July 2020)

    • Removed X-Forwarded-For header in Login endpoint
    • Added optional attribute in MyInfo's response schema of Get User Data endpoint
  • 0.0.12 (14 May 2020)

    • Updated Get User Data response sample
  • 0.0.11 (11 May 2020)

    • Added X-Forwarded-For header in Login and Get User Data endpoints
  • 0.0.10 (29 April 2020)

    • Updated response in Get User Data
      • Updated myinfo.hdbownership to an array
  • 0.0.9 (23 April 2020)

    • Updated example scope for Authorise endpoint
    • Updated Token endpoint redirect_uri value has to be same as Authorise redirect_uri
    • Added an example of Authorization header with Bearer token
  • 0.0.8 (25 February 2020)

    • Updated response sample for Get User Data endpoint
  • 0.0.7 (7 February 2020)

    • Changed consumer/v1/bc_authenticate endpoint to consumer/setup/v1/bc-authenticate
    • Added consumer/consent/v1/bc-authenticate endpoint
  • 0.0.6 (24 January 2020)

    • Updated Get User Data to POST method
    • Updated all Authorization header parameters to string
    • Updated and extracted Authenticate endpoint to a new section
      • Updated endpoint URL to 'bc_authenticate'
      • Updated response from 'nonce_id' to 'bc_session'
      • Updated request body to 'application/x-www-form-urlencoded'
    • Added base string example under Request Signing
    • Updated possible error scenarios for all 400 Bad Request
    • Updated Get User Data endpoint
      • Updated txnNo and client_id from query parameters to request body schema
      • Added client_secret parameter
    • Updated Token endpoint
      • Added grant_type parameter
    • Renamed API groups
    • Update response schema for Get User Data endpoint
    • Update Get User Identity and Revoke Token endpoint
      • Remove client_id and client_secret parameters
    • Update Login endpoint
      • Added conditional mandatory client_id and redirect_uri parameters
  • 0.0.5 (15 November 2019)

    • Updated the authorization header parameter description in the following APIs:
      • Authenticate (both Setup profile and Data retrieval)
      • Token
      • Revoke Token
      • Get user info
    • Get User Data endpoint:
      • Updated the identifier parameter description
      • Set txnNo as an mandatory field and it will be provided to service provider as Interaction_Id
      • Added public_cert parameter that will be provided to service provider as 'encryptionCertificate'
  • 0.0.4 (14 October 2019)

    • Updated overview section to include service provider flows
    • Removed sandbox environment
    • Removed support for TLS 1.1
    • Updated Get Token code snippet to include 'State' parameter
    • Updated service provider 'get user information' to 'get user identity', and updated the URL endpoint
  • 0.0.3 (23 September 2019)

    • Added service provider API endpoints
    • Updated consuming app API and HTTP endpoints
  • 0.0.2 (4 September 2019)

    • Updated uinfin to Identifier
  • 0.0.1 (26 August 2019)

    • Initial Draft

Releases and Compatibility

The RESTful API adopts Semantic Versioning 2.0.0 for releases, and every new release of the API increments the version numbers in the following format:

{MAJOR}.{MINOR}.{PATCH}
  1. {MAJOR} number introduces incompatible API changes with previous {MAJOR} number also resets {MINOR} to 0,
  2. {MINOR} number introduces new functionalities or information that are backward compatible also resets {PATCH} to 0, and
  3. {PATCH} number introduces bug fixes and remains backward compatible.

Pre-release or draft versions, when provided, are denoted by appended hypen - with a series of separate identifiers {LABEL}-{VERSION} following the {PATCH} number. Such releases are unstable and may not provide the intended compatibility with the specification in draft status.

Serving as notice, the RESTful API in version 2.X.X are incompatible with version 1.X.X releases.

Despite backward compatibility in {MINOR} or {PATCH} releases, API consumers are best to evaluate and determine their implementation does not disrupt use-case requirements.

Overview

This API Specification guides you through SGFinDex integration.

Consuming Application

The integration from consuming application consists of two main flows: An optional setup profile flow, and the data retrieval flow.

1. Setup profile flow allow consuming application to send User to SGFinDex to set up their profile. This flow is optional and only applicable if the consuming application had integrated to the Singpass OIDC server, and had obtained the User Singpass Id Token.

Setup Profile



2. Data retrieval flow involves 1) obtaining the user permission for consuming application to get user data, and 2) calling the SGFinDex API to retrieve user data.

Retrieval Flow

Service Provider

There are two APIs created for Service provider to consume.

1. Get logged in user identity for cross-checking. Once the user redirects to Service Provider /authorise endpoint, Service Provider can do a backchannel call to FPDS APIM to get the user identity.

Retrieval Flow



2. Delete an existing profile. A service provider can delete a profile by calling a revoke endpoint with a valid refresh token. Click here for more information about how to integrates to revoke API endpoint.

Environments

The RESTful APIs are provided in both testing and live environments, and are accessible over the Internet via HTTPS.

Consumers are to ensure firewall clearance on their edge network nodes for connecting to the APIs.

The convention used by API endpoints' URLs is in the following format:

https://{ENV_DOMAIN_NAME}/{CONTEXT}/{VERSION}/{RESOURCE}
  • {ENV_DOMAIN_NAME} indicates Service Authorisation's API domain names - respectively:

    • To be provider on next release
  • /{CONTEXT}, indicates the context configured for the API = /consumer

  • /{VERSION} indicates the endpoint's release {MAJOR} version number path - for this release = /v1

  • /{RESOURCE} indicates the API resource path name. Any additional query string parameters are appended as needed.

Available Environments

1. Staging Environment

The staging enviroment is used for testing your application with the full security measures required in production.

Note:

  • Domain Name: https://stg.fpdsapim.myinfo.gov.sg/
  • PKI digital signature is required for Token API.
  • Authorization Bearer access token should be verified by your resource API.
  • Client assertion is required for Token API.
  • Authorization DPoP access token will be verified by our resource API.

2. Production Environment

The production enviroment is the actual live environment with full security measures and live data.

Note:

  • Domain Name: https://fpdsapim.myinfo.gov.sg/
  • PKI digital signature is required for Token API.
  • Authorization Bearer access token should be verified by your resource API.
  • Client assertion is required for Token API.
  • Authorization DPoP access token will be verified by our resource API.

Scheduled Downtimes

The following are the scheduled downtimes for the various environments:

Production Environment

  • Monthly 8 hours scheduled downtime.
  • Downtime will be announced prior to date.
  • Communication by email

Staging Environment

  • None

Security

HTTPS Interface

SGFinDex API gateway supports accessing of APIs via the following interfaces:

  • HTTP version 1.1 connection over TLS (Transport Layer Security) version 1.2 standards, and ciphersuites:

    • using AES (Advanced Encryption Standard) and SHA (Secure Hash Algorithm),
    • on either GCM (Galois/Counter Mode) or CBC (Cipher Block Chaining) mode.
  • Below is the list of recommended cipher suites that you may use:

    • TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
    • TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
    • TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
    • TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256

IMPORTANT: ensure your server supports TLS 1.2 and supports a cipher suite in the list above.

Accessing the RESTful APIs using prior versions of TLS and/or unsupported ciphersuites will result in connectivity errors. SGFinDex API gateway does not support 2-way TLS client nor mutual authentication.

API HTTP interface features:

  1. JSON (JavaScript Object Notation) is the supported data media format and indicated in Content-Type header application/json, also
  2. Content-Length header is omitted by having Transfer-Encoding header chunked emitted for streaming data

OAuth2.0

SGFinDex use OAuth2.0 authorisation code flow to perform authentication & authorisation.

The sequence diagram below illustrates the steps involved in integrating your application with our APIs:

OAuth

The flow consists of 2 APIs:

  1. Authorise

    • This will trigger the Singpass login and consent page. Once successful, your application will receive the authorisation code via your callback url.
  2. Token

    • Call this server-to-server API with a valid authorisation code to get the access token.
  3. Protected Resources (User Data)

    • Call this server-to-server API with a valid access token to get the user data.

There is an optional API (authenticate endpoint) consuming app can call if the end user had already logged in to Singpass.

Application Authentication

Access to all server-to-server APIs will be authenticated by SGFinDex API gateway. Prior to calling the APIs, respective consumers are required to have:

  • approval of access, onboarding process for the required API resources will be provisioned, and
  • authentication credentials are then supplied and exchanged.

Authentication methods provided by SGFinDex API gateway on internet:

  • OAuth 2.0 using ES256 Client Assertion and DPoP (see "Client Assertion" and "DPoP" sections below)

Proof Key for Code Exchange (PKCE)

PKCE is an extension to the Authorization Code flow to prevent CSRF and authorization code injection attacks (Refer to https://datatracker.ietf.org/doc/html/rfc7636). The mechanism relies on 2 parameters namely code_challenge (base64(sha256(code_verifier))) and code_verifier (cryptograhic random string generated and kept secret on server side) sent in the authorize and token call respectively to enable the Authorisation Server to perform correlation. Below is an example of how code_verifier and code_challenge are produced on server side. The code_challenge can then be attached to the authorize call.

Sample Code in NodeJS

  function base64URLEncode(str) {
    return str.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
  }
  function sha256(buffer) {
    return crypto.createHash('sha256').update(buffer).digest();
  }
  function generateCodeChallenge(codeVerifier) {
    try {
      let codeChallenge = base64URLEncode(sha256(this.codeVerifier));
      return codeChallenge;
    } catch (error) {
      // error handling
      throw (error);
    }
  }
  var codeVerifier = base64URLEncode(crypto.randomBytes(32)); 
  var codeChallenge = generateCodeChallenge(codeVerifier);

Authentication methods provided by Myinfo on internet:

  • OAuth 2.1 using Client Assertion
  • Client Assertion should be signed using a key that is published on partner's JWKS endpoint, which is submitted during client onboarding.

Client Assertion

The Partner's application is required to generate client assertions to be attached to server-to-server calls to prove authenticity (Refer to https://datatracker.ietf.org/doc/html/rfc7521). The partner's private key signs the assertion metadata, and SGFinDex will use the partner's onboarded JWKS endpoint to obtain the public key for verification. Below is a sample of the JWT header and payload of the client assertion:

  {
    "typ": "JWT",
    "alg": "ES256",
    "kid": "x0zDLIC9yNRIXu3gW8nTQDOMNe7sKMAjQnZj3AWTW2U",
  } . {
    "sub": "STG2-APIM-SELF-TEST",
    "jti": "jNDZuyLw66gkTjmCNMawzrTJNlhS8wdjpU0DHTzo",
    "aud": "https://api.myinfo.gov.sg/com/v4/token",
    "iss": "STG2-APIM-SELF-TEST",
    "iat": 1662365106,
    "exp": 1662365406,
    "cnf":{
      "jkt": "G_q8Qv9-xv_9xJo-esolTnvxVSobMER7O0LKGPBlTqY"
    }
  }

Description of JWT header attributes:

  • {typ} Type - value "JWT"
  • {alg} Algorithm - value "ES256"
  • {kid} Key ID - The unique identifier for the key.

Description of JWT payload attributes:

  • {sub} Subject - client_id issued by Myinfo upon onboarding
  • {jti} JWT ID - random unique identifier
  • {aud} Audience - URL that partner's application is calling
  • {iss} Issuer - client_id issued by Myinfo upon onboarding
  • {iat} Issued At (Payload) - current timestamp
  • {exp} Expiry (Payload) - expiry timestamp
  • {cnf.jkt} JWK Thumbprint - base64url encoding of the JWK SHA-256 Thumbprint of the partners's ephemeral public signing key used to sign the DPoP Proof JWT

Sample post body with DPoP Proof and client assertion

Below is an example of a post body of a token API call:

  // Response body of token call in a transaction

  POST /token HTTP/1.1
  Host: stg.fpdsapim.myinfo.gov.sg
  Content-Type: application/x-www-form-urlencoded
  DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImp3ayI6eyJrdHkiOiJFQyIsImtpZCI6IkdfcThRdjkteHZfOXhKby1lc29sVG52eFZTb2JNRVI3TzBMS0dQQmxUcVkiLCJjcnYiOiJQLTI1NiIsIngiOiJPMTRHMXVlUjBTeHRSNGRsVW9rZ1FzMmM3NXFLV1VXSVRLWDYwaXYwTHkwIiwieSI6IlMzQjhSUC1OaGc2dTBMTE43Y0ZfX2lzUmctQVNHcFp3WTN4V2JrQVFRWXMiLCJ1c2UiOiJzaWciLCJhbGciOiJFUzI1NiJ9LCJhbGciOiJFUzI1NiJ9.eyJodHUiOiJodHRwczovL3NpdC5hcGkubXlpbmZvLmdvdi5zZy9jb20vdjQvdG9rZW4iLCJodG0iOiJQT1NUIiwianRpIjoib3MyWk14cEZVMnpISm9aTWhXbkFaeWRwcWYyM0JsZ3dDTkFFcmptRyIsImlhdCI6MTY2MjM2NTEwNiwiZXhwIjoxNjYyMzY1MjI2fQ.X69HdF8C_9IRAeDGG9kr7ViV4SGyH7G4ovm_pTlvHwQaLJvr4f4Lf445h-2aPt06kPagnfuO4-HgctRalxThqw

  grant_type=authorization_code&
  code=5uHGo2QpNAG99gO6rjqtzNuzrdnzJwatjpeuejvB&
  redirect_uri=http%3A%2F%2Fmyapp.com%2Fcallback&
  client_id=STG2-APIM-SELF-TEST&
  code_verifier=ZK2mVhN9gBY9aGytuTIYmU7yHMueb7jZxMrE4WzjRRU&
  client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&
  client_assertion=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImFRUHlaNzJOTTA0M0U0S0Vpb2FIV3ppeHQwb3dWOTlnQzlrUkszODhXb1EifQ.eyJzdWIiOiJTVEcyLU1ZSU5GTy1TRUxGLVRFU1QiLCJqdGkiOiJqTkRadXlMdzY2Z2tUam1DTk1hd3pyVEpObGhTOHdkanBVMERIVHpvIiwiYXVkIjoiaHR0cHM6Ly9zaXQuYXBpLm15aW5mby5nb3Yuc2cvY29tL3Y0L3Rva2VuIiwiaXNzIjoiU1RHMi1NWUlORk8tU0VMRi1URVNUIiwiaWF0IjoxNjYyMzY1MTA2LCJleHAiOjE2NjIzNjU0MDYsImNuZiI6eyJqa3QiOiJHX3E4UXY5LXh2Xzl4Sm8tZXNvbFRudnhWU29iTUVSN08wTEtHUEJsVHFZIn19.OuXR4qOmY8Lilqf6QcgC7PW1hRAgWoG41gHdSC4N-6UiipH1kgdXynazq0rF5JxqOi0Bxah4Jh41KbgEoJ3geQ

Sample Code in NodeJS that generates the DPoP Proof and client assertion

  async function generateClientAssertion(url, clientId, privateSigningKey, jwkThumbprint) {
    try {
      let now = Math.floor((Date.now() / 1000));

      let payload = {
        "sub": clientId,
        "jti": generateRandomString(40),
        "aud": url,
        "iss": clientId,
        "iat": now,
        "exp": now + 300,
        "cnf" : {
          "jkt": jwkThumbprint
        }
      };

      let jwsKey = await jose.JWK.asKey(privateSigningKey, "pem");
      let jwtToken = await jose.JWS.createSign({ "format": 'compact', "fields": { "typ": 'JWT' } }, jwsKey).update(JSON.stringify(payload)).final();
      // "typ" attribute in header generated with option specified. kid and alg attributes are generated automatically.
      logger.info("jwtToken", jwtToken);
      return jwtToken;
    } catch (error) {
      logger.error("generateClientAssertion error", error);
      throw constant.ERROR_GENERATE_CLIENT_ASSERTION;
    }
  };

  async function generateJwkThumbprint(ephemeralPublicKey) {
    let jwkKey = await jose.JWK.asKey(ephemeralPublicKey, 'pem');
    let jwkThumbprintBuffer = await jwkKey.thumbprint('SHA-256');
    let jwkThumbprint = jose.util.base64url.encode(jwkThumbprintBuffer, 'utf8');

    return jwkThumbprint;
  }

  async function generateEphemeralKey() {
    let options = {
      "namedCurve": "P-256",
      "publicKeyEncoding": {
        "type": "spki",
        "format": "pem"
      },
      "privateKeyEncoding": {
        "type": "sec1",
        "format": "pem"
      }
    };

    ephemeralKeyPair = crypto.generateKeyPairSync("ec", options);

    let ephemeralPublicKey = (await jose.JWK.asKey(ephemeralKeyPair.publicKey, "pem")).toJSON();
    ephemeralPublicKey.use = "sig";
    ephemeralPublicKey.alg = "ES256";

    return ephemeralPublicKey;
  }

Token Validation

Access Tokens are in JWT format. This JWT complies to the standard 'JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants' (https://tools.ietf.org/html/rfc7523).

You will need to verify the token with our public key from following JWKS:

Sample Code in NodeJS

  // Sample Code for Verifying & Decoding JWS or JWT
  function verifyJWS(jws, publicCert) {
    // verify token
    // ignore notbefore check because it gives errors sometimes if the call is too fast.
    try {
      var jwspayload = jwt.verify(jws, fs.readFileSync(publicCert, 'utf8'), {
        algorithms: ['RS256'],
        ignoreNotBefore: true
      });
      return jwspayload;
    }
    catch(error) {
      throw("Error with verifying and decoding JWS");
    }
  }

Demonstration of Proof of Possesion (DPoP)

Partner's application is required to generate DPoP Proof JWT to be attached to server-to-server resource calls to prove the legit possession of the access token. (Refer to https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop). The token is signed by partner's ephemeral private signing key, which the partner's public signing key had been used in the token call and "embedded" into the access token provided. Below is a sample of the JWT header and payload of a DPoP token:

  {
    "typ": "dpop+jwt",
    "alg": "ES256",
    "jwk": {
      "kty": "EC",
      "kid": "CRx5jixF8ZLRpxpqguxCwiq0g6b-ACHfQQJT7uiAkio",
      "alg": "ES256",
      "crv": "P-256",
      "x": "mxVK8wvCaQ8iUJ4AyZr1oK1_ceL_27kgTPISNEcChm4",
      "y": "0P-81zpWvcy6YAPSiV_K4h94wdEdk-RwrhbTL0fkeyc"
    }
  }
  . {
    "jti": "dfgsrtsDFBBgbsB230afktmdFGdgegemmet",
    "htu": "https://api.myinfo.gov.sg/com/v4/person/08939d6c-11c0-4bc9-b2cd-f5fd14369521",
    "htm": "GET",
    "iat": 1645757787,
    "exp": 1645757907,
    "ath": "RyPeyISsEqdR1lsZBv80o0A8eF1Kvxdm_uf1hnLRf9M"
  }
  • {typ} Type - Type (Header), value "dpop+jwt"

  • {alg} Algorithm (Header) - digital signature algorithm identifier as per RFC7518 (Refer to https://datatracker.ietf.org/doc/html/rfc7518). MUST NOT be none or an identifier for a symmetric algorithm (MAC).

  • {jwk} JSON Web Key (Header) - public key chosen by the client. MUST NOT contain the private key.

    • {kty} - The family of cryptographic algorithms used with the key

    • {kid} - The unique identifier for the key

    • {alg} - The specific cryptographic algorithm used with the key

    • {crv} - The crv member identifies the cryptographic curve used with the key

    • {x} - The x member contains the x coordinate for the elliptic curve point

    • {y} - The y member contains the y coordinate for the elliptic curve point

  • {jti} JWT ID (Payload) - unique identifier

  • {htu} HTTP URL (Payload) - HTTP URI used for the request, without query (?) and fragment parts (#)

  • {htm} HTTP Method (Payload) - HTTP method for the request to which the JWT is attached

  • {iat} Issued At (Payload) - current timestamp

  • {exp} Expiry (Payload) - expiry timestamp

  • {ath} Access token hash (Payload) - The base64url encoded SHA-256 hash of the ASCII encoding of the associated access token's value (Required only for /fpdata call after DPoP-bound access token is issued)

Sample Code in NodeJS that generates the DPoP token

  // function to generate the base64url encoded SHA-256 hash of the ASCII encoding of the accesstoken
  function generateAth(accessToken) {
    let sha256AccessToken =  crypto.createHash('sha256').update(accessToken).digest();
    let base64URLEncodedHash = sha256AccessToken.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
    return base64URLEncodedHash;
  };
  
  // generates DPoP Proof JWT headers for calling **fpdata** API
  async function generateDpopProof  (url, method, sessionPopKeyPair, ath) {
    try {
      let now = Math.floor((Date.now() / 1000));
      let payload = {
        "htu": url,
        "htm": method,
        "jti": generateRandomString(40),
        "iat": now,
        "exp": now + 120,
      };

      // required only for /fpdata resource call
      if(ath)payload.ath = ath; 

      let privateKey = await jose.JWK.asKey(sessionPopKeyPair.privateKey, "pem");
      let jwk = (await jose.JWK.asKey(sessionPopKeyPair.publicKey, "pem")).toJSON(true);;
      jwk.alg = "ES256";
      let jwtToken = await jose.JWS.createSign({ "format": 'compact', "fields": { "typ": 'dpop+jwt', "jwk": jwk } }, { "key": privateKey, "reference": false }).update(JSON.stringify(payload)).final();
      return jwtToken;
    } catch (error) {
      logger.error("generateDpop error", error);
      throw constant.ERROR_GENERATE_DPOP;
    }
  };

JWKS Requirements for Service Consumer and Provider

When onboarding to SGFinDex via the Developers and Partner Portal, every Service Consumer and Provider is required to provide a JWKS (JSON Web Key Set) endpoint.

The JWKS endpoint which hosts the JWK (JSON Web Key) must meet the following requirements.

  • Endpoint is served behind HTTPS on port 443 using a TLS server certificate issued by a standard publicly verifiable CA issuer (no private CAs), with a complete certificate chain presented by the server.
  • No other custom HTTP header requirements outside standard HTTP headers.
  • Able to respond in 3 seconds.

A JWKS endpoint can host multiple JWKs, using a key ID kid to distinguish between each JWK.

The JWK will be used in the following scenarios:

  • Signature JWK is used to verify the client assertion presented when consuming APIs.
  • Encryption JWK is used to encrypt the response of data APIs.

JWK for Signature

The signature JWK will be used to verify the client assertion JWT presented in request, thereby authenticating the client.

The signature JWK should have the following attributes:

  • Must contain a key use use field of value sig, refer to rfc7517#section-4.2.

  • Must contain a key ID kid field, refer to rfc7517#section-4.5.

  • Must contain key type kty of either EC or RSA.

    • For key type kty of value EC, with algorithm alg of value ES256 and curve crv of value P-256 (For version 2 APIs).

    • For key type kty of value RSA, with algorithm alg of value RS256 (For version 1 APIs).

Example:

// EC Signature Key
{
  "kty": "EC",
  "use": "sig",
  "kid": "sig-2021-01-15T12:09:06Z",
  "alg": "ES256",
  "crv": "P-256",
  "x": "Tjm2thouQXSUJSrKDyMfVGe6ZQRWqCr0UgeSbNKiNi8",
  "y": "8BuGGu519a5xczbArHq1_iVJjGGBSlV5m_FGBJmiFtE"
}

// RSA Signature Key
{
  "kty": "RSA",
  "kid": "sig-2023-06-15T14:31:42Z",
  "use": "sig",
  "alg": "RS256",
  "e": "AQAB",
  "n": "z7HAG7BFZu-VkvcMceXFH4Jt0y2ZZTgD10Y_GuD8iXi_c6SeAreF6KEXpq3mC_bpspf75hCW--mibqiXxJhxOAAOfQ0WAU-W2tWWlv--tDKsLlKUFF5ebWyZHjWPqPEjRIyc8nkqvlGqAjLp7oefcpOgvbZwMcSc7hrh7NX2nSBDJzyBCVRx-CLKe5q3_9bZzbfudK2RHo9o9p6pC6QCE6url2fEpsPC4M3j4T283ksJFpqOxsbmwp5ns_tmHBkN099NHfgrcda5GLrv8DYKW1vcPQ1RlDThlP3EiWefXx76AiTMby1CkArwafD20zurqdngcSjHuJ80_hxou1WCfw"
}

Key rotation for signature key

To rotate the signature keys with zero downtime, the client must:

  • Host the replacement signature key in the JWKS endpoint onboarded.
  • Ensure the replacement signature key has a different key ID kid to the original key.
  • Ensure the replacement signature key matches the other cryptographic key requirements.

JWK for Encryption

The encryption JWK will be used to encrypt data requested.

The encryption JWK must have the following attributes:

  • Must contain a key use use field of value enc, refer to rfc7517#section-4.2.

  • Must contain a key ID kid field, refer to rfc7517#section-4.5.

  • Must contain key type kty of either EC or RSA.

    • For key type kty of value EC, with algorithm alg of value ECDH-ES+A256KW and curve crv of value P-256 (For version 2 APIs).

    • For key type kty of value RSA, with algorithm alg of value RSA-OAEP (For version 1 APIs).

  • If there are multiple keys that meets requirements, the first EC or RSA key will be used.

  • If encryptionKid is provided as parameter during data pull, encryptionKid will be used to filter the keys in the JWKS. Criteria of keys listed above applies.

Example:

// EC Encryption Key
{
  "kty": "EC",
  "use": "enc",
  "kid": "enc-2021-01-15T12:09:06Z",
  "crv": "P-256",
  "x": "xom6kD54yfXRPvMFVYFlVjUKzmNhz7wf0DP_2h9kXtY",
  "y": "lrh8C9c8-SBJTm1FcfqLkj2AnHtaxpnB1qsN6PiFFJE",
  "alg": "ECDH-ES+A256KW"
}

// RSA Encryption Key
{
  "kty": "RSA",
  "kid": "enc-2023-06-15T14:31:42Z",
  "use": "enc",
  "alg": "RSA-OAEP",
  "e": "AQAB",
  "n": "-fbCIK-zGdvVjoOJ78M-7l-Avu80MsSPvOCn1IKUACRS31G-H2Z64vvjz6o-h5dEldwhpFCRPmdTl0TEvj4AIcw6CcWLUmL7QttLSW_kaZ3FVV_UaUzjGSP5G9MrEfC67zwl7b1td3N93szrPA47YYXjgmLq9t2MswFyIYQ6pkBhO4_9joTmrz9LcPnOQrqZx2_X9GUIsQAA52Su3ZdxCuCLhUdSe9_AwMdxuhtBCfTdwJEArwcj-3jDxAlZL_Vr1OImBtsgQ2dnCMDE_weaA2NQp2dIQ5zj7a_zaZ8l3G5L2TGKXeCEo8rDuGjvuYQ4fLxjyiJqLydzosCaeB7N7w"
}

Key rotation for encryption key

To rotate the encryption key with zero downtime, the client must:

  • Host the replacement encryption key in the JWKS endpoint onboarded.
  • Identify private key to decrypt JWE using key ID kid in JWE.
  • Ensure the replacement encryption key matches the other cryptographic key requirements.

Verifying User

The access token exchanged contains a new attribute called hashed_uinfin. This attribute can be used by a Service Consumer to verify that the user who consented to the release of the data is the same user who is logged in to the Service Consumer.

The Service Consumer will need to hash the NRIC or FIN of the user appended with the code_challenge issued in the authorise call using the SHA-256 algorithm on their end, to compare the value against the value of hashed_uinfin.

Sample Code in NodeJS

const uinfin = 'S1234567D';
const codeChallenge = 'rgm9otPOPMkH0V5maJRV8c-yO4WMAnJWM5rU66OLsuU';

const hashed_uinfin = crypto.createHash('sha256').update(uinfin + codeChallenge).digest('hex');

Payload Signing and Encryption

Before user data is returned to a Service Consumer, the response payload for the Get User Data API is first signed, then encrypted by SGFinDex:

In order to read the response payload, a Service Consumer has to perform the following steps in this order:

  1. Decrypt the response payload with the Service Consumer's private encryption key. This private encryption key should correspond to the Service Consumer's encryption public key in the Service Consumer's JWKS endpoint that has been onboarded onto SGFinDex.
  2. Validate the decrypted payload signature with SGFinDex's public signing key returned from SGFinDex's JWKS endpoint.

After completing the steps above, the Service Consumer will be able to extract the payload in JSON format.

Step 1: Decryption

  • The response payload is encrypted using a Service Consumer public encryption key provided in their onboarded JWKS endpoint. Therefore, decryption of the payload is to be done using the Service Consumer's corresponding private encryption key.
  • Current encryption algorithms used:
    • ECDH-ES+A256KW (for content key wrapping)
    • A256GCM (for content encryption)

Sample Code in NodeJS

  // Sample Code for decrypting JWE
  async function decryptJWEWithKey(compactJWE, encryptionPrivateKey) {
    try {
      let keystore = jose.JWK.createKeyStore();
      let jweParts = compactJWE.split("."); // header.encryptedKey.iv.ciphertext.tag
      if (jweParts.length != 5) {
        throw constant.ERROR_INVALID_DATA_OR_SIGNATURE;
      }

      //Session encryption private key should correspond to the session encryption public key passed in to client assertion
      let key = await keystore.add(encryptionPrivateKey, "pem");

      let data = {
        "type": "compact",
        "protected": jweParts[0],
        "encrypted_key": jweParts[1],
        "iv": jweParts[2],
        "ciphertext": jweParts[3],
        "tag": jweParts[4],
        "header": JSON.parse(jose.util.base64url.decode(jweParts[0]).toString())
      };

      let result = await jose.JWE.createDecrypt(key).decrypt(data);

      return result.payload.toString();
    } catch (error) {
      throw constant.ERROR_DECRYPT_JWE;
    }
  };

Step 2: Verification of Signature

  • The decrypted payload is signed according to the JWS (JSON Web Signature) format, similar to the access token.
    • Signature algorithm used is ES256.
    • Additional attributes iat (epoch time when the signature is generated) and 'txnid' (unique transaction ID for reconciliation purpose) are included in the JWS header.
    • Verify the decrypted payload signature with SGFinDex's public signing key returned from SGFinDex's JWKS endpoint Retrieving-JSON-Web-Key-Set-(JWKS).

Sample Code in NodeJS

// Sample Code for Verifying & Decoding JWS or JWT
function verifyJWS = async (compactJWS, JWKSUrl) => {
  var jwks = await getJwks(JWKSUrl);

  try {
    let keyStore = await jose.JWK.asKeyStore(jwks);

    let result = await jose.JWS.createVerify(keyStore).verify(compactJWS);
    let payload = JSON.parse(Buffer.from(result.payload).toString());

    return payload;
  } catch (error) {
    throw "Error with verifying JWS";
  }
}

Error Handling

The RESTful APIs used HTTP specification standard status codes to indicate the success or failure of each request. For HTTP 400 Bad Request errors, the response content will be in the following JSON format:

{
    "code": "integer (int32)",
    "message": "string"
}

Refer to the individual API definitions for the error codes you might encounter for each API.

Support

For business and technical queries, please contact support@sgdex.gov.sg.

Profile Management

Login

This API triggers Singpass login, upon successful login, redirect user to SGFinDex to manage their connected financial institutions.

query Parameters
client_id
required
string

Unique ID for your application. This is a mandatory parameter for identifying calling-application.

redirect_uri
string

URL to redirect the user back to the client application when user logs out from central infra. User will be redirected to https://www.singpass.gov.sg if this field is not provided or invalid.

appLaunchURL
string

Intent URL or universal link of Mobile Application for Singpass Mobile to launch after authentication process.

Responses

Retrieval of Data

Authorise

This API triggers Singpass login and consent to retrieve user's data from SGFinDex. Once the user has authenticated and consented, an authorisation code (authcode) will be returned for verification via the callback URL defined. The authcode can then be used to retrieve an access token via the Token API.

Note:

  • This API is public and should be implemented as a link or button on your online webpage.
query Parameters
scope
required
string

Example: cpfbalances noa hdbownership.outstandingloanbalance hdbownership.monthlyloaninstalment financialdata financialdata.banking
Space separated list of scopes requested. Scope needs include at least financialdata or financialdata.banking.

redirect_uri
required
string

Your callback URL to return to with the authorisation code.

client_id
required
string

Unique ID for your application.

response_type
required
string
Default: "code"

Response type for authorisation code flow - must be "code". If not provided, the system will default the value to code.

code_challenge
required
string

The Base64-URL encoded value of the S256 hash of the ASCII encoding of code_verifier.

code_challenge_method
required
string

The PKCE code challenge method that is being used. The value 'S256' is required.

appLaunchURL
string

Intent URL or universal link of Mobile Application for Singpass Mobile to launch after authentication process.

Responses

Request samples

function callAuthoriseApi() {
  var authoriseUrl = authApiUrl + "?client_id=" + clientId +
    "&scope=" + attributes +
    "&response_type" + responseType +
    "&code_challenge=" + codeChallenge +
    "&code_challenge_method=" + "S256" +
    "&redirect_uri=" + redirectUrl;

  window.location = authoriseUrl;
}

Token

This API generates an access token when presented with a valid authcode obtained from the Authorise API. This token can then be used to request for the user's data that were consented.

header Parameters
DPoP
required
string

Add DPoP proof JWT containing the following parameters:

  • typ: Type (Header) - value "dpop+jwt"
  • alg: Algorithm (Header) - digital signature algorithm identifier as per RFC7518. MUST NOT be none or an identifier for a symmetric algorithm (MAC).
  • jwk: JSON Web Key (Header) - public key chosen by the client. MUST NOT contain the private key.
  • jti: JWT ID (Payload) - unique identifier
  • htu: HTTP URL (Payload) - HTTP URI used for the request to which the JWT is attached.
  • htm: HTTP Method (Payload) - HTTP method for the request to which the JWT is attached.
  • iat: Issued AT (Payload) - current timestamp of JWT
  • exp: Expiry (Payload) - Expiry timestamp
Request Body schema: application/x-www-form-urlencoded
code
required
string

The authcode given by the Authorise API endpoint response.

client_id
required
string

Unique ID for your application.

redirect_uri
required
string

Application's callback URL. This value has to be similar to the redirect_uri parameter provided in Authorise call.

grant_type
required
string

Example: authorization_code
The grant type that the application is accessing.

client_assertion
required
string

The assertion being used to authenticate the client, please refer to Client Assertion for details. This JWT is signed with consumer private key and contains the following parameters:

  • sub: Subject - client_id issued by Myinfo upon onboarding
  • jti: JWT ID - random unique number
  • aud: Audience - URL that partner application is calling
  • iss: Issuer - client_id issued by Myinfo upon onboarding
  • iat: Issued At - current timestamp
  • exp: Expiry - expiry timestamp, maximum 300 seconds (5 minutes)
  • cnf.jkt: JWK thumbprint - base64url encoding of the JWK SHA-256 Thumbprint of the partners's ephemeral public signing key used to sign the JWT
client_assertion_type
required
string

The format of the assertion as defined by the authorization server. The value will be an absolute URI. Example: urn:ietf:params:oauth:client-assertion-type:jwt-bearer

code_verifier
required
string

High-entropy cryptographic random STRING using the unreserved characters [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~" from Section 2.3 of [RFC3986], with a minimum length of 43 characters and a maximum length of 128 characters.

Responses

Request samples

//get access token using authcode
async function getAccessToken(authCode, privateSigningKey, codeVerifier, sessionPopKeyPair) {
try {

  let tokenUrl = process.env.MYINFO_API_TOKEN_URL;
  let redirectUrl = process.env.PARTNER_REDIRECT_URL;
  let clientId = process.env.MYINFO_APP_CLIENT_ID;

  let cacheCtl = "no-cache";
  let contentType = "application/x-www-form-urlencoded";
  let method = constant.HTTP_METHOD.POST;
  let clientAssertionType = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
  let jktThumbprint = await generateJwkThumbprint(sessionPopKeyPair.publicKey)
  let clientAssertion = await generateClientAssertion(tokenUrl, clientId, privateSigningKey, jktThumbprint);
  // assemble params for Token API
  let strParams = `grant_type=authorization_code` +
      "&code=" + authCode +
      "&redirect_uri=" + redirectUrl +
      "&client_id=" + clientId +
      "&code_verifier=" + codeVerifier +
      "&client_assertion_type=" + clientAssertionType +
      "&client_assertion=" + clientAssertion

  let params = querystring.parse(strParams);
  let dPoP = await generateDpop(tokenUrl, null,constant.HTTP_METHOD.POST, sessionPopKeyPair);

  // assemble headers for Token API
  let strHeaders = `Content-Type=${contentType}&Cache-Control=${cacheCtl}&DPoP=${dPoP}`;
  let headers = querystring.parse(strHeaders);

  // invoke Token API
  let parsedTokenUrl = urlParser.parse(url);
  let tokenDomain = parsedTokenUrl.hostname;
  let tokenRequestPath = parsedTokenUrl.path;

  return getHttpsResponse(hostname, path, headers, method, params);
} catch (error) {
    throw error;
};

Response samples

Content type
application/json
{
  • "token_type": "DPoP",
  • "access_token": {
    }
}

Get User Data

This API returns user's data from SGFinDex when presented with a valid access token obtained from the Token API.

Note: Null value indicates that an attribute is unavailable.

path Parameters
identifier
required
string

A unique identifier of a user. You can get this identifier from the access token (JWT) sub parameter after calling Token endpoint.

header Parameters
DPoP
required
string

Add DPoP proof JWT containing the following parameters:

  • typ: Type (Header) - value "dpop+jwt"
  • alg: Algorithm (Header) - digital signature algorithm identifier as per RFC7518. MUST NOT be none or an identifier for a symmetric algorithm (MAC).
  • jwk: JSON Web Key (Header) - public key chosen by the client. MUST NOT contain the private key.
  • jti: JWT ID (Payload) - unique identifier
  • htu: HTTP URL (Payload) - HTTP URI used for the request to which the JWT is attached.
  • htm: HTTP Method (Payload) - HTTP method for the request to which the JWT is attached.
  • iat: Issued AT (Payload) - current timestamp of JWT
  • exp: Expiry (Payload) - Expiry timestamp
  • ath: Access token hash (Payload) - The base64url encoded SHA-256 hash of the ASCII encoding of the associated access token's value
Authorization
required
string

Access token JWT that was returned from token call

X-Forwarded-For
string

Client IP address

Request Body schema: application/x-www-form-urlencoded
consent
required
string

JWT that contains the Consent ID for activity based refresh consent and the encryption key ID for service providers fetch the encryption cert from JWKS.

txnNo
required
string

Transaction ID from requesting digital services for cross referencing. This parameter will be used by SGFinDex as the 'Interaction-Id' when federating data from service providers.

encryptionKid
string

Consuming applications can specify Key ID in their registered JWKS to encrypt response payload with.

Responses

Request samples

// function to prepare request for PERSON API
async function createPersonRequest(sub, validToken) {
  var url = _personApiUrl + "/" + sub + "/";
  var cacheCtl = "no-cache";
  var method = "GET";
  var request = null;
  // assemble params for Person API
  var strParams = "consent=" + consent;
  var params = querystring.parse(strParams);

  // assemble headers for Person API
  var strHeaders = "Cache-Control=" + cacheCtl;
  var headers = querystring.parse(strHeaders);

  let ath = base64URLEncode(sha256(accessToken))
  let dpopToken = await generateDpop(urlLink, ath, method, sessionPopKeyPair);

  // Add Authorisation headers for connecting to API Gateway
  _.set(headers, "Authorization", "DPoP " + accessToken);
  _.set(headers, "DPoP", dpopToken);

  // invoke person API
  var request = restClient.get(url);

  // Set headers
  if (!_.isUndefined(headers) && !_.isEmpty(headers))
    request.set(headers);

  // Set Params
  if (!_.isUndefined(params) && !_.isEmpty(params))
    request.query(params);

  return request;
}

Response samples

Content type
application/json
{
  • "fpdata": [
    ],
  • "myinfo": {
    },
  • "connectionExpiryDate": "2023-12-31"
}

Token Management

Revoke Token

Permanently revoke a refresh token. This operation cannot be undone. Attempts to revoke an invalid refresh token will result in an error response.

Request Body schema: application/x-www-form-urlencoded
refresh_token
required
string

The refresh token for revocation

client_assertion
required
string

This API is for Service Provider to revoke tokens. This field is used to authenticate the provider, please refer to Client Assertion for details. This JWT is signed with the private key of the Service Provider and contains the following parameters:

  • sub: Subject - client_id issued by Myinfo upon onboarding
  • jti: JWT ID - random unique number
  • aud: Audience - URL that partner application is calling
  • iss: Issuer - client_id issued by Myinfo upon onboarding
  • iat: Issued At - current timestamp
  • exp: Expiry - expiry timestamp, maximum 300 seconds (5 minutes)
client_assertion_type
required
string

The format of the assertion as defined by the authorization server. The value will be an absolute URI. Example: urn:ietf:params:oauth:client-assertion-type:jwt-bearer

Responses

Response samples

Content type
application/json
{
  • "refresh_token": "string",
  • "deleted": true
}

User Identification

Get User Identity

This API allows the service provider to get the SGFinDex logged-in user identity by providing the state.

Request Body schema: application/x-www-form-urlencoded
state
required
string

The state provided to the service provider during the authorisation request.

client_assertion
required
string

This API is for Service Provider to verify user. This field is used to authenticate the provider, please refer to Client Assertion for details. This JWT is signed with the private key of the Service Provider and contains the following parameters:

  • sub: Subject - client_id issued by Myinfo upon onboarding
  • jti: JWT ID - random unique number
  • aud: Audience - URL that partner application is calling
  • iss: Issuer - client_id issued by Myinfo upon onboarding
  • iat: Issued At - current timestamp
  • exp: Expiry - expiry timestamp, maximum 300 seconds (5 minutes)
client_assertion_type
required
string

The format of the assertion as defined by the authorization server. The value will be an absolute URI. Example: urn:ietf:params:oauth:client-assertion-type:jwt-bearer

Responses

Response samples

Content type
application/json
{
  • "identity": "string"
}

Retrieving JSON Web Key Set (JWKS)

Get Public Key

This API will return the updated SGFinDex's public key information by default. With passing kid (the unique identifier for the key) in the path parameter, this API will return the specified public key information.

path Parameters
kid
string

The unique identifier for the key.

NOTE: If this parameter is not provided, the result will show an array of keys that are available.

Responses

Response samples

Content type
application/json
Example
{
  • "keys": [
    ]
}