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

This tutorial will call the APIs from your server-based (or running from localhost) application.\
\
Myinfo uses [OAuth 2.1](https://oauth.net/2.1/) for our Authentication and Authorisation flow.\
\
For a brief overview of the end to end flow, please refer to our [Overview](https://docs.developer.singpass.gov.sg/docs/legacy-myinfo-v3-v4/technical-specifications/myinfo-v4)

***

## 1. Download & Setup Sample Client Application<br>

### **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.

* [Install Node.js and NPM for Windows](http://blog.teamtreehouse.com/install-node-js-npm-windows)
* [Install Node.js and NPM for Linux](http://blog.teamtreehouse.com/install-node-js-npm-linux)
* [Install Node.js and NPM for Mac](http://blog.teamtreehouse.com/install-node-js-npm-mac)

### **1.2 Download the sample client application**

Please download the sample client application from our [Github](https://github.com/singpass/myinfo-demo-app-v4).<br>

### **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.<br>

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         | 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 : [PKCE](https://api.singpass.gov.sg/library/myinfo/developers/pkce)) |
| code\_challenge\_method | Hashing method used in producing code\_challenge. Only supports 'S256'                                                                                                                                                                                                                                    |
| redirect\_uri           | your callback URL for Myinfo to return with the authorisation code. For our sample application the callback url is <http://localhost:3001/callback>                                                                                                                                                       |
| response\_type          | Response type for authorisation code flow - must be "code".                                                                                                                                                                                                                                               |

### **2.1 Code Example for Authorize API**

With reference to views/html/index.html in the [sample application](https://github.com/singpass/myinfo-demo-app-v4) code:

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

You may also use test personas found in the [Personas](https://docs.developer.singpass.gov.sg/docs/testing/myinfo-test-personas).

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

{% hint style="info" %}
Mockpass is a mock Singpass/Corppass login page for testing in development consistently
{% endhint %}

### **2.3 Consent Page**

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

<figure><img src="https://1982108655-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fk67iNluSpweCPqhbIY5t%2Fuploads%2FyuB0HC1aGJhAXVaVe5rP%2Fimage.png?alt=media&#x26;token=3318543c-835b-4765-918a-62acfc0b3f11" alt=""><figcaption><p>Consent Screen</p></figcaption></figure>

### **2.4 Retrieve Authorization Code**<br>

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`.

{% hint style="info" %}
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).
{% endhint %}

### 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                    | The authcode given by the Authorize API. (Reference: [Authorize](https://api.singpass.gov.sg/library/myinfo/developers/tutorial2#authorize))                                                                          |
| grant\_type             | <p>Default: "authorization\_code"<br><br>Grant type for getting token (default "authorization\_code")</p>                                                                                                             |
| client\_id              | Unique ID for your application.                                                                                                                                                                                       |
| redirect\_uri           | Application's callback URL.                                                                                                                                                                                           |
| client\_assertion       | The assertion being used to authenticate the client. (Reference: [Client Assertion](https://api.singpass.gov.sg/library/myinfo/developers/clientassertion))                                                           |
| client\_assertion\_type | <p>Default: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"<br><br>The format of the assertion, which is defined to be 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'</p>                          |
| code\_verifier          | Cryptographic random string generated and kept secret on server-side, it's corresponding to code\_challenge sent in the Authorize API (Reference: [PKCE](https://api.singpass.gov.sg/library/myinfo/developers/pkce)) |

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

**Sample Code:**\
With reference to myinfo-connector/index.js in our sample [sample client application connector](https://github.com/singpass/myinfo-demo-app-v4)

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 | The assertion being used to authenticate the client. (Reference: [Client Assertion](https://api.singpass.gov.sg/library/myinfo/developers/clientassertion)) |
| 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"
}
```

Notice the response contains an access\_token, which is in the JWT (JSON Web Token) format. (Reference: [JWT](https://api.singpass.gov.sg/library/myinfo/developers/jwt))

The access\_token can be verified using Myinfo JWK URI and decoded.(Reference: [JWS](https://api.singpass.gov.sg/library/myinfo/developers/jwt))

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.

{% hint style="info" %}
Your application must validate the signature of the JWT to ensure the JWT was not modified in transit - intentionally or unintentionally.&#x20;
{% endhint %}

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.

{% hint style="info" %}
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).
{% endhint %}

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          | Include the generated Demonstration of Proof of Possession token. (Reference: [DPoP](https://api.singpass.gov.sg/library/myinfo/developers/dpop)) |

**ii) Path Parameters:**

<table><thead><tr><th>PARAMETER</th><th>DESCRIPTION</th></tr></thead><tbody><tr><td>sub</td><td><p>Identifier of user obtained from 'sub' attribute in access_token. </p><pre><code>Example: 915267f0-5939-0230-78e7-b8cdbaab8518     
</code></pre></td></tr></tbody></table>

**iii) Query parameters:**

<table><thead><tr><th>PARAMETER</th><th>DESCRIPTION</th></tr></thead><tbody><tr><td>scope</td><td><p>Space separated list of scopes requested.</p><pre><code>Example: scope=name nationality   
</code></pre></td></tr></tbody></table>

**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
```

<br>

**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.&#x20;

### 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.<br>

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

* **STEP 1**: Decrypt the payload with your application's private key (Reference: [JWE](https://api.singpass.gov.sg/library/myinfo/developer/technical-concepts/jwt))
* **STEP 2**: Validate the decrypted payload signature with Myinfo public key (JWKS URI) (Reference: [JWS](https://api.singpass.gov.sg/library/myinfo/developer/technical-concepts/jwt))

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.&#x20;

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.

{% hint style="info" %}
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.
{% endhint %}
