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:
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.
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:
4.1.1 Providing the Access Token in the Request HeaderSTEP 1: The access_token should be provided in the header of your /person API request under "Authorization" with Prefix "DPoP"
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:
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.
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: )