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.
2.0.11 release (27 Jun 2024)
2.0.10 release (22 Nov 2023)
2.0.9 release (10 Nov 2023)
2.0.8 release (06 Nov 2023)
2.0.7 release (03 Oct 2023)
2.0.6 release (05 Sep 2023)
2.0.5 release (11 Aug 2023)
2.0.4 release (20 Jul 2023)
2.0.3 release (28 Jun 2023)
2.0.2 release (21 Jun 2023)
2.0.1 release (4 May 2023)
2.0.0 release (11 Apr 2023)
1.0.9 release (29 Mar 2023)
1.0.8 release (23 Mar 2023)
1.0.7 release (24 Mar 2023)
1.0.6 release (20 Sept 2022)
1.0.5 release (21 Jul 2022)
1.0.4 release (22 Jun 2022)
1.0.3 release (16 Feb 2022)
1.0.2-release (7 Feb 2022)
1.0.1-release (12 Nov 2021)
1.0.0-release (9 July 2021)
0.0.15 (2 March 2021)
0.0.14 (19 November 2020)
0.0.13 (3 July 2020)
0.0.12 (14 May 2020)
0.0.11 (11 May 2020)
0.0.10 (29 April 2020)
0.0.9 (23 April 2020)
0.0.8 (25 February 2020)
0.0.7 (7 February 2020)
0.0.6 (24 January 2020)
0.0.5 (15 November 2019)
0.0.4 (14 October 2019)
0.0.3 (23 September 2019)
0.0.2 (4 September 2019)
0.0.1 (26 August 2019)
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}
{MAJOR}
number introduces incompatible API changes with previous {MAJOR}
number also resets {MINOR}
to 0
,{MINOR}
number introduces new functionalities or information that are backward compatible also resets {PATCH}
to 0
, and{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.
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.
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.
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.
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.
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:
/{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.
The staging enviroment is used for testing your application with the full security measures required in production.
The production enviroment is the actual live environment with full security measures and live data.
The following are the scheduled downtimes for the various environments:
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:
Below is the list of recommended cipher suites that you may use:
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:
Content-Type
header application/json
, alsoContent-Length
header is omitted by having Transfer-Encoding
header chunked
emitted for streaming dataSGFinDex 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:
The flow consists of 2 APIs:
Authorise
Token
Protected Resources (User Data)
There is an optional API (authenticate endpoint) consuming app can call if the end user had already logged in to Singpass.
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:
Authentication methods provided by SGFinDex API gateway on internet:
ES256
Client Assertion and DPoP (see "Client Assertion" and "DPoP" sections below)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.
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:
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"
}
}
{typ}
Type - value "JWT"{alg}
Algorithm - value "ES256"{kid}
Key ID - The unique identifier for the key.{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 JWTBelow 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
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;
}
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:
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");
}
}
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)
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;
}
};
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.
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:
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:
kid
to the original key.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:
kid
in JWE.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
.
NodeJS
const uinfin = 'S1234567D';
const codeChallenge = 'rgm9otPOPMkH0V5maJRV8c-yO4WMAnJWM5rU66OLsuU';
const hashed_uinfin = crypto.createHash('sha256').update(uinfin + codeChallenge).digest('hex');
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:
After completing the steps above, the Service Consumer will be able to extract the payload in JSON format.
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;
}
};
ES256
.iat
(epoch time when the signature is generated) and 'txnid' (unique transaction ID for reconciliation purpose) are included in the JWS header.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";
}
}
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.
For business and technical queries, please contact support@sgdex.gov.sg.
This API triggers Singpass login, upon successful login, redirect user to SGFinDex to manage their connected financial institutions.
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. |
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.
DPoP required | string Add DPoP proof JWT containing the following parameters:
|
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 |
grant_type required | string Example: |
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:
|
client_assertion_type required | string The format of the assertion as defined by the authorization server. The value will be an absolute URI.
Example: |
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. |
//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; };
{- "token_type": "DPoP",
- "access_token": {
- "sub": "string",
- "hashed_uinfin": "string",
- "scope": "string",
- "nbf": 0,
- "iss": "string",
- "expires_in": 0,
- "iat": 0,
- "exp": 0,
- "realm": "string",
- "aud": "string",
- "jti": "string",
- "jku": "string",
- "cnf": {
- "jkt": "string"
}, - "client": {
- "client_id": "string",
- "client_name": "string",
- "entity_uen": "string",
- "entity_name": "string"
}
}
}
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.
identifier required | string A unique identifier of a user. You can get this identifier from the access token (JWT) |
DPoP required | string Add DPoP proof JWT containing the following parameters:
|
Authorization required | string Access token JWT that was returned from token call |
X-Forwarded-For | string Client IP address |
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. |
// 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; }
{- "fpdata": [
- {
- "sp": "MAYBANK",
- "payload": "eyJlbmMiOiJBMjU2Q0JDLUhTNTEy... JWE from provider",
- "response_code": 200
}, - {
- "sp": "OCBC",
- "payload": "eyJlbmMiOiJBMjU2Q0JDLUhTNTEy... JWE from provider",
- "response_code": 200
}, - {
- "sp": "SCB",
- "payload": "eyJlbmMiOiJBMjU2Q0JDLUhTNTEy... JWE from provider",
- "response_code": 200
}, - {
- "sp": "DBS",
- "payload": "eyJlbmMiOiJBMjU2Q0JDLUhTNTEy... JWE from provider",
- "response_code": 200
}, - {
- "sp": "SGX CDP",
- "payload": "eyJlbmMiOiJBMjU2Q0JDLUhTNTEy... JWE from provider",
- "response_code": 200
}, - {
- "sp": "HSBC",
- "payload": "eyJlbmMiOiJBMjU2Q0JDLUhTNTEy... JWE from provider",
- "response_code": 200
}, - {
- "sp": "CITIBANK",
- "payload": "eyJlbmMiOiJBMjU2Q0JDLUhTNTEy... JWE from provider",
- "response_code": 200
}, - {
- "sp": "UOB",
- "payload": "eyJlbmMiOiJBMjU2Q0JDLUhTNTEy... JWE from provider",
- "response_code": 200
}, - {
- "sp": "AIA",
- "payload": "eyJlbmMiOiJBMjU2Q0JDLUhTNTEy... JWE from provider",
- "response_code": 200
}, - {
- "sp": "HSBC LIFE",
- "payload": "eyJlbmMiOiJBMjU2Q0JDLUhTNTEy... JWE from provider",
- "response_code": 200
}, - {
- "sp": "SINGLIFE",
- "payload": "eyJlbmMiOiJBMjU2Q0JDLUhTNTEy... JWE from provider",
- "response_code": 200
}, - {
- "sp": "GREAT EASTERN",
- "payload": "eyJlbmMiOiJBMjU2Q0JDLUhTNTEy... JWE from provider",
- "response_code": 200
}, - {
- "sp": "MANULIFE",
- "payload": "eyJlbmMiOiJBMjU2Q0JDLUhTNTEy... JWE from provider",
- "response_code": 200
}, - {
- "sp": "INCOME",
- "payload": "eyJlbmMiOiJBMjU2Q0JDLUhTNTEy... JWE from provider",
- "response_code": 200
}, - {
- "sp": "PRUDENTIAL",
- "payload": "eyJlbmMiOiJBMjU2Q0JDLUhTNTEy... JWE from provider",
- "response_code": 200
}
], - "myinfo": {
- "response_code": 500
}, - "connectionExpiryDate": "2023-12-31"
}
Permanently revoke a refresh token. This operation cannot be undone. Attempts to revoke an invalid refresh token will result in an error response.
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:
|
client_assertion_type required | string The format of the assertion as defined by the authorization server. The value will be an absolute URI.
Example: |
{- "refresh_token": "string",
- "deleted": true
}
This API allows the service provider to get the SGFinDex logged-in user identity by providing the state.
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:
|
client_assertion_type required | string The format of the assertion as defined by the authorization server. The value will be an absolute URI.
Example: |
{- "identity": "string"
}
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.
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. |
{- "keys": [
- {
- "kty": "string",
- "n": "string",
- "e": "string",
- "alg": "string",
- "kid": "string",
- "use": "string",
- "crv": "string",
- "x": "string",
- "y": "string",
- "x5t": "string"
}
]
}