Singpass Developer Docs
Legacy Myinfo v3/v4
Legacy Myinfo v3/v4
  • Legacy Myinfo v3/v4
  • Data Catalog
  • Key Principles
  • Technical Specifications
    • Myinfo v4
      • Difference between v3 and v4
      • Technical Guidelines
      • Technical Concepts
        • OAuth 2.1 Concepts
        • Proof of Key Code Exchange (PKCE)
        • JSON Web Token (JWT)
        • Client Assertions
        • JSON Web Key Store (JWKS)
        • Demonstration of Proof-of-Possession (DPoP)
      • API Specifications
      • Tutorials
        • Tutorial 1: Myinfo Person sample Data
        • Tutorial 2: End-to-end Integration with Myinfo v4 APIs
      • Resources
        • Myinfo Connectors
        • Error Codes
      • FAQ
    • Myinfo v3
      • Technical Guidelines
      • API Specifications
      • Latest X.509 Public Key Certificate
      • Tutorials
        • Tutorial 1: Basic Person API
        • Tutorial 2: Using OAuth2
        • Tutorial 3: Implementing PKI Digital Signature
      • Resources
        • Myinfo Connectors
        • Error Codes
      • FAQ
Powered by GitBook
On this page
  • 1. Download & Setup Sample Client Application
  • 1.1 Install Node.js and NPM
  • 1.2 Download the sample client application
  • 1.3 Run NPM install
  • 1.4 Start the Application
  • 1.5 Access the Application on Your Browser
  • 2. Authorize API
  • 2.1 Code Example for Authorize API
  • 2.2 Login with MockPass Select the test ID and login using MockPass:
  • 2.3 Consent Page
  • 2.4 Retrieve Authorization Code
  • 3. Token API
  • 3.1 Request
  • 3.2 Response
  • 4. Person API
  • 4.1 Request
  • 4.2 Response
  • Summary

Was this helpful?

  1. Technical Specifications
  2. Myinfo v4
  3. Tutorials

Tutorial 2: End-to-end Integration with Myinfo v4 APIs

PreviousTutorial 1: Myinfo Person sample DataNextResources

Last updated 1 month ago

Was this helpful?

This tutorial will call the APIs from your server-based (or running from localhost) application. Myinfo uses for our Authentication and Authorisation flow. For a brief overview of the end to end flow, please refer to our


1. Download & Setup Sample Client Application

1.1 Install Node.js and NPM

In order for the demo application to run, you will need to Install Node.js and NPM. Follow the instructions given by the links below depending on your OS.

1.2 Download the sample client application

Please download the sample client application from our .

1.3 Run NPM install

Run the following command in the folder where you unzipped the application:

npm install

1.4 Start the Application

Execute the following command to start the application:

npm start

1.5 Access the Application on Your Browser

You should be able to access the sample application via the following URL:

http://localhost:3001

2. Authorize API

Click on the "Retrieve Myinfo" button on the sample application.

This calls the /authorize API below (with some additional parameters):

PARAMETER
DESCRIPTION

client_id

unique ID for your application. For our sample application, this is STG2-MYINFO-SELF-TEST

scope

space separated list of attributes requested. Possible attributes are listed in the Person object definition in the API specifications.

purpose_id

purpose_id should have a corresponding pre-registered purpose(s) description as part of the setup. Purpose description refers to what the user will see in the consent page

code_challenge

code_challenge_method

Hashing method used in producing code_challenge. Only supports 'S256'

redirect_uri

response_type

Response type for authorisation code flow - must be "code".

2.1 Code Example for Authorize API

The index.html is the frontend(client side) code.

function callAuthorizeApi() {
//Call backend server to generate code challenge 
$.ajax({
    url: "/generateCodeChallenge",
    data: {},
    type: "POST",
    success: function (code_challenge) {
        //Redirect to authorize url after generating code challenge
        var authorizeUrl = authApiUrl + "?client_id=" + clientId +
        "&scope=" + scope +
        "&purpose_id=" + purpose_id +
        "&code_challenge=" + code_challenge +
        "&code_challenge_method=" + method + 
       "&response_type=code" + 
        "&redirect_uri=" + redirectUrl;
     
        window.location = authorizeUrl;
        },
        error: function (result) {
            alert("ERROR:" + JSON.stringify(result.responseJSON.error));
        }
    });
}

This code example above triggers the /authorize API.

2.2 Login with MockPass Select the test ID and login using MockPass:

NRIC: 60050408A

The /authorize API endpoint triggers the MockPass login and shows the consent page after successful login.

Mockpass is a mock Singpass/Corppass login page for testing in development consistently

2.3 Consent Page

Once you have successfully logged into MockPass, a consent page similar to the one below will be shown:

2.4 Retrieve Authorization Code

If you have followed the steps above correctly, our system should return you an authorization code by invoking your callback URL.

You should now see something like this in your browser URL dialog:

http://localhost:3001/callback?code=e2369168-52da-421a-b70f-03f64e779c4b

Notice that the query string parameter value of code above is the authorization code(authcode). We will use this authorization code(authcode) in the next /token API call.

In the example above, the authorization code(authcode) is e2369168-52da-421a-b70f-03f64e779c4b.

The /token API and /person API calls should be invoked automatically by the sample app once you have finished the consent page.

We will look at the code for these 2 APIs in the following sections.


3. Token API

Now that you have the authcode, you can invoke the /token API to get the access_token.

The Token API must be called from a server-side component. It cannot be called from the browser or mobile native client application due to security reasons (there is no secure place to store your private key).

3.1 Request

Request Parameters:

  • Method: POST

  • Content-Type: application/x-www-form-urlencoded

  • Agent: HTTP client

  • Response Type: json

i) Headers:

PARAMETER
DESCRIPTION

DPoP

DPoP Proof (JWT) containing the client's ephemeral public signing key that can be used to prove legit possession of the access_token issued. (Reference: DPoP flow)

Sample header of the Token API request:

POST https://test.api.myinfo.gov.sg/com/v4/token HTTP 1.1
Accept: application/json
Content-Type: application/x-www-form-urlencoded
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImp3ayI6eyJrdHkiOiJFQyIsImtpZCI6Ik0yT0pWVGRmaVV3OUMxSFFrTzF2VGlqTFhHT3lwY0Z1VDV3ZDQtNVdkekUiLCJjcnYiOiJQLTI1NiIsIngiOiJuOTJ0THQyUk5vQmdCM2piT2txNjF3bF8zM1VTUzA4dFMtR1I3ZGUwTWYwIiwieSI6IjNyaUcxamNFWThCTFpkUjhmc2VBa1hBLVJvSW92X3g5Q19iRzNKWFZVcTQiLCJ1c2UiOiJzaWciLCJhbGciOiJFUzI1NiJ9LCJhbGciOiJFUzI1NiJ9.eyJodHUiOiJodHRwczovL3NpdC5hcGkubXlpbmZvLmdvdi5zZy9jb20vdjQvdG9rZW4iLCJodG0iOiJQT1NUIiwianRpIjoiYldEWTZhVENuQUdLV3RDVWpZTFZJVm1vRW1CNFhhcmVwc0JEODVHQyIsImlhdCI6MTY2MjcxNDk5OCwiZXhwIjoxNjYyNzE1MTE4fQ.yOsoD-q7DPlgI_cnRVmjXcBraSxD5riCVmc8kaEoxGstTipFdTZIvnMs6EYs8oSFUYv4OVWcYm5MKLrJNLdtew
PARAMETER
DESCRIPTION

code

grant_type

Default: "authorization_code" Grant type for getting token (default "authorization_code")

client_id

Unique ID for your application.

redirect_uri

Application's callback URL.

client_assertion

client_assertion_type

Default: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" The format of the assertion, which is defined to be 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'

code_verifier

Sample body of the /token API request in application/x-www-form-urlencoded:

Copy to clipboard

body : 'code=KhtDB67e2DrPgB3wpLjUsDfppHsBB42A9Crsx2b0
&grant_type=authorization_code
&redirect_uri=http%3A%2F%2Flocalhost%3A3001%2Fcallback
&client_id=STG2-MYINFO-SELF-TEST
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImFRUHlaNzJOTTA0M0U0S0Vpb2FIV3ppeHQwb3dWOTlnQzlrUkszODhXb1EifQ.eyJzdWIiOiJTVEcyLU1ZSU5GTy1TRUxGLVRFU1QiLCJqdGkiOiJtZzBNcFl0dkcyQTRUOWFRZkxON0o3OEY3dVltUmZJVmVyRUhIUFpaIiwiYXVkIjoiaHR0cHM6Ly9zaXQuYXBpLm15aW5mby5nb3Yuc2cvY29tL3Y0L3Rva2VuIiwiaXNzIjoiU1RHMi1NWUlORk8tU0VMRi1URVNUIiwiaWF0IjoxNjYyNzE0OTk4LCJleHAiOjE2NjI3MTUyOTgsImNuZiI6eyJqa3QiOiJNMk9KVlRkZmlVdzlDMUhRa08xdlRpakxYR095cGNGdVQ1d2Q0LTVXZHpFIn19.OlluOi2RkwAavibqhGrDL4m6m91IlM_vnfu-4CY0pmkSbUDcKS_NBlggNrTNcc_wnbFBSqMYNo2V4Geh9Ucc6g
&code_verifier=Av6ktmeC1UsjHY677P-zf5Vk2x94W38Zu4pl4qhbeok'

The sample application will then send the constructed request to the /token API

index.js is the backend (server side) code.

callTokenAPI = async function (authCode, privateSigningKey, codeVerifier, sessionPopKeyPair) {
     
      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 this.securityHelper.generateJwkThumbprint(sessionPopKeyPair.publicKey)
    let strParams;
    // assemble params for Token API
    strParams = `grant_type=authorization_code` +
        "&code=" + authCode +
        "&redirect_uri=" + CONFIG.REDIRECT_URL +
        "&client_id=" + CONFIG.CLIENT_ID +
        "&code_verifier=" + codeVerifier +
        "&client_assertion_type=" + clientAssertionType +
        "&client_assertion=" + await this.securityHelper.generateClientAssertion(CONFIG.TOKEN_URL, CONFIG.CLIENT_ID, privateSigningKey, jktThumbprint);
     
    let params = querystring.parse(strParams);
    let dPoP= await this.securityHelper.generateDpop(CONFIG.TOKEN_URL, null, 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 tokenURL = (CONFIG.USE_PROXY && CONFIG.USE_PROXY == "Y") ? CONFIG.PROXY_TOKEN_URL : CONFIG.TOKEN_URL;
    let parsedTokenUrl = urlParser.parse(tokenURL);
    let tokenDomain = parsedTokenUrl.hostname;
    let tokenRequestPath = parsedTokenUrl.path;
     
    let accessToken = await requestHandler.getHttpsResponse(tokenDomain, tokenRequestPath, headers, method, params);
    return accessToken;
 };

The code example above creates the request to call the /token API.

[HTTP POST] 
https://test.api.myinfo.gov.sg/com/v4/token

3.2 Response

  • Response Type: application/json

PARAMETER
DESCRIPTION

access_token

The resulting access token for the code flow

token_type

Describes how the token can be used. (e.g. DPoP)

expires_in

Expiry of access token (in seconds)

refresh_token

Refresh token that can be used to exchange for another access token. Only available if client is configured to receive refresh token.

client_assertion

scope

Scopes requested, separated by space

Sample response from Token API in application/json:

Copy to clipboard

{
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IkFGTW5uS1JXVGFCWUVoTmZFQjZpUTVFckMxeXFHVnlaY2hIOEE3bmxfeU0ifQ.eyJzdWIiOiI5MTUyNjdmMC01OTM5LTAyMzAtNzhlNy1iOGNkYmFhYjg1MTgiLCJqdGkiOiJvMkZaVUpmSFZWVjA3aFpLTFZrc2Yxd0RBYnNUUUtWblFXM3RjZDI5Iiwic2NvcGUiOiJ1aW5maW4gbmFtZSBzZXggcmFjZSBuYXRpb25hbGl0eSBkb2IgZW1haWwgbW9iaWxlbm8gcmVnYWRkIGhvdXNpbmd0eXBlIGhkYnR5cGUiLCJleHBpcmVzX2luIjoxODAwLCJhdWQiOiJodHRwczovL3Rlc3QuYXBpLm15aW5mby5nb3Yuc2cvY29tL3Y0L3BlcnNvbiIsInJlYWxtIjoibXlpbmZvLWNvbSIsImlzcyI6Imh0dHBzOi8vdGVzdC5hcGkubXlpbmZvLmdvdi5zZy9zZXJ2aWNlYXV0aC9teWluZm8tY29tIiwiY2xpZW50Ijp7ImNsaWVudF9pZCI6IlNURzItTVlJTkZPLVNFTEYtVEVTVCIsImNsaWVudF9uYW1lIjoiTXlJbmZvIFNlbGYgVGVzdCBBcHAgR0NDIiwiZW50aXR5X3VlbiI6IlQxNkdCMDAwMkciLCJlbnRpdHlfbmFtZSI6IkdvdlRlY2gifSwiY25mIjp7ImprdCI6ImhHdHN1S3h3UmFMOE1NeDVqVjVUYUluSWpqOFNLS3N3VEVOOGcxaU9FY1EifSwiZXBrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ1c2UiOiJlbmMiLCJraWQiOiI3VGt5TWFqV0JYUlo3aVpmZ3lQUGZmMmdMMzloMlh0ZkpEemNzNXRjZXJNIiwieCI6Ik5mSXhKdWRCTzFfWEQ0RG5oa1ZLUG9uSHV4MExHMTJPM0QtWjJOZnN1RUUiLCJ5IjoiMkhHNmV5SmNFRUJWWlVwMVlsRjU3TFdNbEFJbXlTdU41d1FjZUVybW85OCIsImFsZyI6IkVDREgtRVMrQTI1NktXIn0sImlhdCI6MTY2NzIwMTY4OCwibmJmIjoxNjY3MjAxNjg4LCJleHAiOjE2NjcyMDM0ODh9.0CMpP4Whxk11XTMxg1fz6Srw2HkPiY-t2Tydb8ouQVpCwMNe6DW3Mu6KBdOo00ipa7Nlhw375zwmWAIT-8-3CQ",
  "token_type": "DPoP",
  "expires_in": 1799,
  "refresh_token": "bsAJGkONZJJjs0cR11ZQzGHzxLKwDoEwaApy0krW",
  "scope": "uinfin name sex race nationality dob email mobileno regadd housingtype hdbtype"
}

You should see the decoded access_token in the onscreen logs that looks like this:

Copy to clipboard

{
  "sub": "915267f0-5939-0230-78e7-b8cdbaab8518",
  "jti": "o2FZUJfHVVV07hZKLVksf1wDAbsTQKVnQW3tcd29",
  "scope": "uinfin name sex race nationality dob email mobileno regadd housingtype hdbtype",
  "expires_in": 1800,
  "aud": "https://test.api.myinfo.gov.sg/com/v4/person",
  "realm": "myinfo-com",
  "iss": "https://test.api.myinfo.gov.sg/serviceauth/myinfo-com",
  "client": {
    "client_id": "STG2-MYINFO-SELF-TEST",
    "client_name": "MyInfo Self Test App GCC",
    "entity_uen": "T16GB0002G",
    "entity_name": "GovTech"
  },
  "cnf": { "jkt": "hGtsuKxwRaL8MMx5jV5TaInIjj8SKKswTEN8g1iOEcQ" },
  "epk": {
    "kty": "EC",
    "crv": "P-256",
    "use": "enc",
    "kid": "7TkyMajWBXRZ7iZfgyPPff2gL39h2XtfJDzcs5tcerM",
    "x": "NfIxJudBO1_XD4DnhkVKPonHux0LG12O3D-Z2NfsuEE",
    "y": "2HG6eyJcEEBVZUp1YlF57LWMlAImySuN5wQceErmo98",
    "alg": "ECDH-ES+A256KW"
  },
  "iat": 1667201688,
  "nbf": 1667201688,
  "exp": 1667203488
}

This access_token is a representation of the permissions that was given by the user when he/she clicked on "I Agree" on the consent page.

Your application must validate the signature of the JWT to ensure the JWT was not modified in transit - intentionally or unintentionally.

Notice the sub attribute in the JSON token is the identifier of the person who logged in via MockPass. We will use this identifier for the /person API call next.


4. Person API

Now that you have the access_token, you can invoke the /person API to get the Person data.

The Person API must be called from a server-side component. It cannot be called from the browser or mobile native client application due to security reasons (there is no secure place to store your private key).

This API call is essentially the same as the one in our Tutorial 1, except that you will need to provide a valid access_token, or else we will reject your API request call.

4.1 Request

Request Parameters:

  • Method: GET

  • Agent: HTTP client

  • Response Type: json

i) Header:

PARAMETER
DESCRIPTION

Authorization

Include the access_token (JWT) from /Token API in this header prefixed with 'DPoP'.

DPoP

ii) Path Parameters:

PARAMETER
DESCRIPTION

sub

Identifier of user obtained from 'sub' attribute in access_token.

iii) Query parameters:

PARAMETER
DESCRIPTION

scope

Space separated list of scopes requested.

iii) Sample Code:

With reference to myinfo-connector/index.js in our sample client application connector code:

Copy to clipboard

callPersonAPI = async function (sub, accessToken, sessionPopKeyPair) {
    let urlLink;
     
    urlLink = CONFIG.PERSON_URL + "/" + sub;
    let cacheCtl = "no-cache";
    let method = constant.HTTP_METHOD.GET;
     
    // assemble params for Person API
    let strParams ="scope=" + encodeURIComponent(CONFIG.SCOPE);
     
    // assemble headers for Person API
    let strHeaders = "Cache-Control=" + cacheCtl;
    let headers = querystring.parse(strHeaders);
     
    let decodedToken = await this.securityHelper.verifyJWS(accessToken, CONFIG.AUTHORIZE_JWKS_URL);
    let ath = this.securityHelper.base64URLEncode(this.securityHelper.sha256(accessToken))
    let dpopToken = await this.securityHelper.generateDpopProof(urlLink, method, sessionPopKeyPair, ath);
    headers["dpop"] = dpopToken;
     
    headers["Authorization"] = "DPoP " + accessToken;
     
    logger.info("Authorization Header for MyInfo Person API: ", JSON.stringify(headers));
     
    // invoke person API
    let personURL = (CONFIG.USE_PROXY && CONFIG.USE_PROXY == "Y") ? CONFIG.PROXY_PERSON_URL : CONFIG.PERSON_URL;
    let parsedUrl = urlParser.parse(personURL);
    let domain = parsedUrl.hostname;
    let requestPath = parsedUrl.path + "/" + sub + "?" + strParams;
    //invoking https to do GET call
    let personData = await requestHandler.getHttpsResponse(domain, requestPath, headers, method, null);
  
return personData;
};

The code example above creates the request to call the /person API:

Copy to clipboard

[HTTP GET] 
https://test.api.myinfo.gov.sg/com/v4/person/915267f0-5939-0230-78e7-b8cdbaab8518?scope=uinfin%20name%20sex%20race%20nationality%20dob%20email%20mobileno%20regadd%20housingtype%20hdbtype

4.1.1 Providing the Access Token in the Request Header STEP 1: The access_token should be provided in the header of your /person API request under "Authorization" with Prefix "DPoP"

Copy to clipboard

{
  "Authorization": "DPoP eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IkFGTW5uS1JXVGFCWUVoTmZFQjZpUTVFckMxeXFHVnlaY2hIOEE3bmxfeU0ifQ.eyJzdWIiOiI5MTUyNjdmMC01OTM5LTAyMzAtNzhlNy1iOGNkYmFhYjg1MTgiLCJqdGkiOiJvMkZaVUpmSFZWVjA3aFpLTFZrc2Yxd0RBYnNUUUtWblFXM3RjZDI5Iiwic2NvcGUiOiJ1aW5maW4gbmFtZSBzZXggcmFjZSBuYXRpb25hbGl0eSBkb2IgZW1haWwgbW9iaWxlbm8gcmVnYWRkIGhvdXNpbmd0eXBlIGhkYnR5cGUiLCJleHBpcmVzX2luIjoxODAwLCJhdWQiOiJodHRwczovL3Rlc3QuYXBpLm15aW5mby5nb3Yuc2cvY29tL3Y0L3BlcnNvbiIsInJlYWxtIjoibXlpbmZvLWNvbSIsImlzcyI6Imh0dHBzOi8vdGVzdC5hcGkubXlpbmZvLmdvdi5zZy9zZXJ2aWNlYXV0aC9teWluZm8tY29tIiwiY2xpZW50Ijp7ImNsaWVudF9pZCI6IlNURzItTVlJTkZPLVNFTEYtVEVTVCIsImNsaWVudF9uYW1lIjoiTXlJbmZvIFNlbGYgVGVzdCBBcHAgR0NDIiwiZW50aXR5X3VlbiI6IlQxNkdCMDAwMkciLCJlbnRpdHlfbmFtZSI6IkdvdlRlY2gifSwiY25mIjp7ImprdCI6ImhHdHN1S3h3UmFMOE1NeDVqVjVUYUluSWpqOFNLS3N3VEVOOGcxaU9FY1EifSwiZXBrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ1c2UiOiJlbmMiLCJraWQiOiI3VGt5TWFqV0JYUlo3aVpmZ3lQUGZmMmdMMzloMlh0ZkpEemNzNXRjZXJNIiwieCI6Ik5mSXhKdWRCTzFfWEQ0RG5oa1ZLUG9uSHV4MExHMTJPM0QtWjJOZnN1RUUiLCJ5IjoiMkhHNmV5SmNFRUJWWlVwMVlsRjU3TFdNbEFJbXlTdU41d1FjZUVybW85OCIsImFsZyI6IkVDREgtRVMrQTI1NktXIn0sImlhdCI6MTY2NzIwMTY4OCwibmJmIjoxNjY3MjAxNjg4LCJleHAiOjE2NjcyMDM0ODh9.0CMpP4Whxk11XTMxg1fz6Srw2HkPiY-t2Tydb8ouQVpCwMNe6DW3Mu6KBdOo00ipa7Nlhw375zwmWAIT-8-3CQ"
}

STEP 2 The DPoP proof JWT should be provided in the header of your /person API request under "DPoP"

Copy to clipboard

{
  "DPoP": "eyJ0eXAiOiJkcG9wK2p3dCIsImp3ayI6eyJrdHkiOiJFQyIsImtpZCI6ImhHdHN1S3h3UmFMOE1NeDVqVjVUYUluSWpqOFNLS3N3VEVOOGcxaU9FY1EiLCJjcnYiOiJQLTI1NiIsIngiOiJMX0F3SklGeDZWUEk1RklIZFBwSnppLVZFNGdkUlhCU3Zld3ktTmhySFFFIiwieSI6IjF0czFnZENpTWhjUHI1WkJPSjNaSHdBY0YtUkJPVVd6MjJGX296SGRjbk0iLCJ1c2UiOiJzaWciLCJhbGciOiJFUzI1NiJ9LCJhbGciOiJFUzI1NiJ9.eyJodHUiOiJodHRwczovL3Rlc3QuYXBpLm15aW5mby5nb3Yuc2cvY29tL3Y0L3BlcnNvbi85MTUyNjdmMC01OTM5LTAyMzAtNzhlNy1iOGNkYmFhYjg1MTgiLCJodG0iOiJHRVQiLCJqdGkiOiJxc21vU21BVzF6WmtCTHNBUEZUWU8xNFQ1Ukl3eWxHQXFueHdCb3ViIiwiaWF0IjoxNjY3MjAxNjg5LCJleHAiOjE2NjcyMDE4MDksImF0aCI6InNGdjVQYnhGbXJKQ3BFY1pQYzA0aXNfWTdfOXJUMkk3Z2ZMVlNtMHI4djAifQ.MoMtWxT7igolVcGPY7qe15kyi5DxAz1XaJKlvip3b4H0E7RzwL38UE9u6nv3afMgGxQkOhWHU8tZigxlfk3lg"
}

STEP 3 Combine both "Authorization" and "DPoP" into the header of your /person API request, and you should see something like this in the onscreen logs:

Copy to clipboard

{
  "Cache-Control": "no-cache",
   
  "Authorization": "DPoP eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IkFGTW5uS1JXVGFCWUVoTmZFQjZpUTVFckMxeXFHVnlaY2hIOEE3bmxfeU0ifQ.eyJzdWIiOiI5MTUyNjdmMC01OTM5LTAyMzAtNzhlNy1iOGNkYmFhYjg1MTgiLCJqdGkiOiJvMkZaVUpmSFZWVjA3aFpLTFZrc2Yxd0RBYnNUUUtWblFXM3RjZDI5Iiwic2NvcGUiOiJ1aW5maW4gbmFtZSBzZXggcmFjZSBuYXRpb25hbGl0eSBkb2IgZW1haWwgbW9iaWxlbm8gcmVnYWRkIGhvdXNpbmd0eXBlIGhkYnR5cGUiLCJleHBpcmVzX2luIjoxODAwLCJhdWQiOiJodHRwczovL3Rlc3QuYXBpLm15aW5mby5nb3Yuc2cvY29tL3Y0L3BlcnNvbiIsInJlYWxtIjoibXlpbmZvLWNvbSIsImlzcyI6Imh0dHBzOi8vdGVzdC5hcGkubXlpbmZvLmdvdi5zZy9zZXJ2aWNlYXV0aC9teWluZm8tY29tIiwiY2xpZW50Ijp7ImNsaWVudF9pZCI6IlNURzItTVlJTkZPLVNFTEYtVEVTVCIsImNsaWVudF9uYW1lIjoiTXlJbmZvIFNlbGYgVGVzdCBBcHAgR0NDIiwiZW50aXR5X3VlbiI6IlQxNkdCMDAwMkciLCJlbnRpdHlfbmFtZSI6IkdvdlRlY2gifSwiY25mIjp7ImprdCI6ImhHdHN1S3h3UmFMOE1NeDVqVjVUYUluSWpqOFNLS3N3VEVOOGcxaU9FY1EifSwiZXBrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ1c2UiOiJlbmMiLCJraWQiOiI3VGt5TWFqV0JYUlo3aVpmZ3lQUGZmMmdMMzloMlh0ZkpEemNzNXRjZXJNIiwieCI6Ik5mSXhKdWRCTzFfWEQ0RG5oa1ZLUG9uSHV4MExHMTJPM0QtWjJOZnN1RUUiLCJ5IjoiMkhHNmV5SmNFRUJWWlVwMVlsRjU3TFdNbEFJbXlTdU41d1FjZUVybW85OCIsImFsZyI6IkVDREgtRVMrQTI1NktXIn0sImlhdCI6MTY2NzIwMTY4OCwibmJmIjoxNjY3MjAxNjg4LCJleHAiOjE2NjcyMDM0ODh9.0CMpP4Whxk11XTMxg1fz6Srw2HkPiY-t2Tydb8ouQVpCwMNe6DW3Mu6KBdOo00ipa7Nlhw375zwmWAIT-8-3CQ",
   
  "DPoP": "eyJ0eXAiOiJkcG9wK2p3dCIsImp3ayI6eyJrdHkiOiJFQyIsImtpZCI6ImhHdHN1S3h3UmFMOE1NeDVqVjVUYUluSWpqOFNLS3N3VEVOOGcxaU9FY1EiLCJjcnYiOiJQLTI1NiIsIngiOiJMX0F3SklGeDZWUEk1RklIZFBwSnppLVZFNGdkUlhCU3Zld3ktTmhySFFFIiwieSI6IjF0czFnZENpTWhjUHI1WkJPSjNaSHdBY0YtUkJPVVd6MjJGX296SGRjbk0iLCJ1c2UiOiJzaWciLCJhbGciOiJFUzI1NiJ9LCJhbGciOiJFUzI1NiJ9.eyJodHUiOiJodHRwczovL3Rlc3QuYXBpLm15aW5mby5nb3Yuc2cvY29tL3Y0L3BlcnNvbi85MTUyNjdmMC01OTM5LTAyMzAtNzhlNy1iOGNkYmFhYjg1MTgiLCJodG0iOiJHRVQiLCJqdGkiOiJxc21vU21BVzF6WmtCTHNBUEZUWU8xNFQ1Ukl3eWxHQXFueHdCb3ViIiwiaWF0IjoxNjY3MjAxNjg5LCJleHAiOjE2NjcyMDE4MDksImF0aCI6InNGdjVQYnhGbXJKQ3BFY1pQYzA0aXNfWTdfOXJUMkk3Z2ZMVlNtMHI4djAifQ.MoMtWxT7igolVcGPY7qe15kyi5DxAz1XaJKlvip3b4H0E7RzwL38UE9u6nv3afMgGxQkOhWHU8tZigxlfk3lg"
}

Once this is ready, you may call our /person API and receive the JWE response of the data, which requires decoding and decrypting in the next step.

4.2 Response

  • Response Type: application/json

Copy to clipboard

eyJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiRUNESC1FUytBMjU2S1ciLCJraWQiOiI3VGt5TWFqV0JYUlo3aVpmZ3lQUGZmMmdMMzloMlh0ZkpEemNzNXRjZXJNIiwiZXBrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoibUVaMHhpRmQ4SV9nbERTVDRYWHZaUTgwN2E4X1lpN0ZaYkxmdnhZUEtJZyIsInkiOiJoNEEtOV9tYndJeVQ3UlVnVzNBVV9fQjg2TFYyWUhTWFlZN1l6ekEtX2NjIn19.-tkyW9doM-nvfi5iLIX30livPwFb4Ocpj_sH9lf3EBT_665a4obEiQ.YInOs5ZA_wA0kfAA.ESz3K9C-K7BUtifjT-sLlam7sddzAmpfW0QeF5oDrDYOLJXwgwCYbZCwXP7BBkI0TbccTdatQja76Vs2Y-OWsSDS-AOvg46HWUmUkSnzQAREJ_fxPGhDhDLvmBwLl4Y64zVOHDwZFC9dfnJAH8_2ezM03kqatw1gjs5gmipaicdoGZNA3doy33J6W2uAnjl6eunw47ABlu1PKdizumYnISZjPYWxIGLI3BMSfQSo9arvXT9R7sodhmVMb_KlRG9tnbqazvXwjafRs4B9moT7a2pTNts2hVKZ72Oknl-YpED4pkmx7IoV8CUHU0JIrtAnD_fkZlAl1zUSYtJgy7EXJ11T-UktkBWGzXswQQm913YV0ZYiwBkK80exOoGq-40YTDcFnhAqAJQW3N7Ro2nX4Mk4ujBxtxB-j25rtYmIG1-lU0MPepTzFrTLupzrslkPHyfW8a5LystczDLPH7ryCYUAKw1ex87Wbf784mmkd62QfxLjAhvK3fHh0MA_naOBoIzX89o7B2EjAZ015YJvsIKRPb2JM-Y1_Ll5mFmxttMk17ItjDfcxCqQE9dU_4_X3Z8A6LW0HOSMjrIop-9WvmWKCK3DzpgWw_x4H7dlbTPsOKM7cVnP8_LqNMSmmxjskJeBZ4XMOG9G273RvrZbgA8POS0zESTvkrwULTwPFr4fsfDP8t82WWBzsuwuXXNqE_BE-McfoyZJZ-a6iWbMyp8b3CSrLj5neCYHQSGJ14ZJagfOoz8BgTzqJxUoeQdg6K14bdP2Wdwu32q207al7riw35tFU45WFy8lUuIKURsznadUeyG1GzBJ6Rl7zjVmFmtGReawCbGrFLZDxkHl6520YbDVHqnjHbBX4vnneoISdx3YhSHdd08rHNSOPPOrbpv22bWsLCr-6PKZzzM9XG4J4oloDxwMZgwJ-fmExlCwRml9aywlEArbs0UqLdjC9mtngNb4Sw1kNhUleVzXRD3qkKZj1Zzzei-oHN-LEz9hWVMtAdy-bpuq8eVYt437Td2IQnCMQ-f3hA8FqaASLVlfjrbNm0GwRQpki3s6hA2FlUEXlE3RGyOYB9Z9jyf8AB1WQ9QfWpOPUNKlf8pmqS7f-Ym0EVabTFtTT-QNHXWo5CAQ1RZ7K_sxmiLSiMsE-V6V7DFjq0b7bXPiCFbKAyJW21gC_t_G8Shg7nzXtxKY2YEOCFMOjn-RsnQAWYlJZ9sCpiL3PoueLdK2ouAq9KebqpP-syM8jlwTh6ZrL8k_InYCSZAWvCRdqf85hu4qoKxTFd7biVpeJQ1fQb9sLGHihSMx3_kOOsS-OUctuVy_nElcuFaVy4vGk6bjDetED8egwwQZn3oRtbrQeye0filIYw_hcJyXzwNzGKUox7C8aIV6-Mi4x0fJTQPPJrJyEU4fSJC9auGOi75CWPQOf-Kd8GOpr8vLQ8FULaPB3S1zVfhXN7q08Qcp_MolotPPFfBhn7VMM7ai5JG4vhB-oti3gsih4xaIDiYK98Nitqp_OnY34keiEjzxWL8PQJugTyXiGuM6Gft-Xu07OrOzYC-CqtX7QOUhLSSQefP1HhyvuSAocCvgWQ2Q7EydphmlCJAgzlIwXtePcQ-YKqM_bbJ5TZmD_of4T9-Ab-VwNdgcor_Ro1rq3c6zbzTsKxaJ1SeHRSzIfyfz854TbZd4PiJwERYp4dSt-I-sPW21h90CJXCujS--2QzgVeOkSHlUI94dkl54-ahiKd8UlnU_N24_uVsiVYpqkh2Ouce8uy9FRQAL6ZRh5bOC78GWKQEyMvMDWEZl8myrlSiKWm4OFH65hiBZx6uoNIramaMKQNVZOMFLiTUCOoqJc_Xk-LrNzh7FNdfzP9NgbqfPdwKwVeuy4lWLttYOq2UHXVIU_u4aHEjkUuGVO0O2Fz_Ig_pIyTSIQGkhTC7MQjzWfMTuUCPphh4x7bO74VzqprsN35TxxOVvD9iTpDZ-kg5fJ53j2f5G413q8JmCMHhf3rqX7N8bPTHXlkvHoeCMwc7TMyU4ugJOzxQgMqoO78iWHhr4PxriotwB3vvnvzMsg4fVPRzwUWiD_adBhWB_ZZOnoxQHftb9fmcIP6DGyNne11_4VsuIPmifdSDixBVV6AOk39hkQDbBMQctGPat2TXVo8kl3vg_sgqkTDobccEWT5YnQ3XRpQCrD7V9Z5P9u64_rSeFxz1dBLFHe39sytwFtJT-NzhDAN_5UWyNln5uS-R5WtNARjP5b7W50oQntUql1smZiH3o-xqittFs-QoPeH94D5qtLUpK-XF1fWFJrNaL_GhVBkH0S56fYGfceDs-JZ73AePN5lNbFBVLVuA55QMzBHM_Y8k7zcu6cWIRa15z3UdariVcGXsaurFfTYpP0ABmKtJPbBotEEBWb-N7gvdXuH25MLpEXtqjh6GLXcuRMwIUeQ9mcQKFjh2tJF0jf4kEnMsVs9XtK8pO47owp0TAR1iLre80ryuLcbaSoj_Cm4T5IJORuURKftjfQsoC9NMpPLV8dJrBT0hjv2QgZrQmjyZ1lZwZa1cjVy2_o72epuJsdSgf_aSJ1Pq7ucHnr9WnOmvlwU0IWQ.bjsOSR3XIMI6WMNkJ2gCqg

The response payload from our /person API (for test and production environments) is first signed, then encrypted:

  • Signing is done using the JWS (JSON Web Signature) format

  • Encryption is done using the JWE (JSON Web Encryption) Compact Serialization format

Encryption protects the data at rest, while a signed payload means, if necessary, you can pass this signed payload to a 3rd party where they can verify the payload's integrity with our public JWKS.

4.2.1 Decrypt & Verify the Person Response To read the payload, you have to perform the following steps in order:

After the above steps, your application can extract the payload in JSON format.

After which, you should see the response as below in the logs.

Copy to clipboard

{
  "uinfin": {
    "lastupdated": "2022-10-27",
    "source": "1",
    "classification": "C",
    "value": "S6005048A"
  },
  "name": {
    "lastupdated": "2022-10-27",
    "source": "1",
    "classification": "C",
    "value": "ANDY LAU"
  },
  "sex": {
    "lastupdated": "2022-10-27",
    "code": "M",
    "source": "1",
    "classification": "C",
    "desc": "MALE"
  },
  "race": {
    "lastupdated": "2022-10-27",
    "code": "CN",
    "source": "1",
    "classification": "C",
    "desc": "CHINESE"
  },
  "nationality": {
    "lastupdated": "2022-10-27",
    "code": "SG",
    "source": "1",
    "classification": "C",
    "desc": "SINGAPORE CITIZEN"
  },
  "dob": {
    "lastupdated": "2022-10-27",
    "source": "1",
    "classification": "C",
    "value": "1988-10-06"
  },
  "email": { 
    "lastupdated": "2022-10-27",
    "source": "4", 
    "classification": "C", 
    "value": "" 
  },
  "mobileno": {
    "lastupdated": "2022-10-27",
    "source": "4",
    "classification": "C",
    "areacode": { "value": "" },
    "prefix": { "value": "" },
    "nbr": { "value": "" }
  },
  "regadd": {
    "country": { "code": "SG", "desc": "SINGAPORE" },
    "unit": { "value": "10" },
    "street": { "value": "ANCHORVALE DRIVE" },
    "lastupdated": "2022-10-27",
    "block": { "value": "319" },
    "source": "1",
    "postal": { "value": "542319" },
    "classification": "C",
    "floor": { "value": "38" },
    "type": "SG",
    "building": { "value": "" }
  },
  "housingtype": {
    "lastupdated": "2022-10-27",
    "code": "",
    "source": "1",
    "classification": "C",
    "desc": ""
  },
  "hdbtype": {
    "lastupdated": "2022-10-27",
    "code": "115",
    "source": "1",
    "classification": "C",
    "desc": "5-ROOM FLAT (HDB)"
  }
}

Once you have decrypted the JWE and verified the JWS, you will get the Person JSON data format and use this data to pre-fill your application form.


Summary

You've successfully used the OAuth2.1 process to get the Person data from the Myinfo test environment. You are now ready to use the same method to integrate your application with Myinfo Production APIs.

For Staging and Production APIs, please ensure you have different keypairs and different JWKS endpoints configured for each environment. Your business user/partner should submit these as part of the onboarding process.

Value resulting from performing Base64 encoding of a SHA256 hash of code_verifier (cryptographic random value generate on server side.) Refer to the section on Proof Key for Code Exchange (PKCE) for more information (Reference : )

your callback URL for Myinfo to return with the authorisation code. For our sample application the callback url is

With reference to views/html/index.html in the code:

You may also use test personas found in the .

The authcode given by the Authorize API. (Reference: )

The assertion being used to authenticate the client. (Reference: )

Cryptographic random string generated and kept secret on server-side, it's corresponding to code_challenge sent in the Authorize API (Reference: )

Sample Code: With reference to myinfo-connector/index.js in our sample

The assertion being used to authenticate the client. (Reference: )

Notice the response contains an access_token, which is in the JWT (JSON Web Token) format. (Reference: )

The access_token can be verified using Myinfo JWK URI and decoded.(Reference: )

Include the generated Demonstration of Proof of Possession token. (Reference: )

STEP 1: Decrypt the payload with your application's private key (Reference: )

STEP 2: Validate the decrypted payload signature with Myinfo public key (JWKS URI) (Reference: )

Example: 915267f0-5939-0230-78e7-b8cdbaab8518     
Example: scope=name nationality   
OAuth 2.1
Overview
Install Node.js and NPM for Windows
Install Node.js and NPM for Linux
Install Node.js and NPM for Mac
Github
sample application
Personas
sample client application connector
JWT
JWS
JWE
JWS
PKCE
http://localhost:3001/callback
Authorize
Client Assertion
PKCE
Client Assertion
DPoP
Consent Screen