2. Trigger POEMS API Login (authorize API)
The authorization endpoint triggers POEMS API login which requires user to login with POEMS account.
Refer to for user login credentials.
After you are logged in, click "
View Orders"
Open
views/html/index.html in the sample application and look for the following lines of code:
index.html file will be the frontend(client side) code
//function to redirect to POEMS API login page
function callLoginApi() {
var authorizeUrl = authApiUrl + "?response_type=code"
+ "&client_id=" + clientId
+ "&deviceId=MyDemoFP"
+ "&state=" + state
+ "&redirect_uri=" + redirectUrl;
window.location = authorizeUrl;
}
This code triggers the authorize API.
It calls the AUTH API URL below (with some additional parameters):
https://sandboxapi.poems.com.sg/api-gateway/pspl/auth/1.0/oauth/authorize
Note that this API is using HTTPS GET.
You will notice that the URL contains the following parameters:
PARAMETER |
Type |
DESCRIPTION |
response_type |
query |
value must be "code" |
client_id |
query |
unique ID for your application. For our sample application, use POEMS-DEMO |
deviceId |
query |
Unique device id. This can be finger print id for browser, device id for mobile and unique key for desktop application. |
state |
query |
identifier to reconcile query and response. This will be sent back to you via the callback URL. Use a unique system generated number for each and every call. |
redirect_uri |
query |
your callback URL for POEMS API Gateway to return with the authorisation code. For our sample application this is http://localhost:3000/callback |
a) Login with POEMS Account
Use this test ID and password to login to POEMS API Gateway:
User Name:
2222222 Password:
156156qw
b) Get the Authorisation Code
If you have followed the steps above correctly, the authorize api should return you an authorisation code by accessing your callback URL. You should now see something like this in your server side callback.
http://localhost:3000/callback?code=c516Bn&state=123
The dialog below shows the server side callback function.
/* GET callback. */
router.get('/callback', function (req, res, next) {
if (!_.isUndefined(req.query.code) && !_.isEmpty(req.query.code)) {
//recieve access code from authorize api after login
console.log("access code: ".yellow + req.query.code.green);
// exchange access code for access token
callTokenAPI(req.query.code, false,
function (data) {
...
} else {
res.send("You are not authorize to see this page.");
}
...
Notice the parameter
code - It is the authorisation code that your application will used to invoke the next API (token). In the example above, the authorisation code is
c516Bn. We will use this authorisation code in the next token API call.
The
token API calls will follow once you have received the code in the server side.
We will look at the code for token APIs in the following sections.
3. Call the Token API (with the authorisation code)
Now that you have the
authorisation code, you can invoke the
token API to get the access token.
In the sample application we call this API from a server-side component. It is recommended that it should not be called from the browser or mobile native client application to prevent exposing access token to unauthorized entities.
Open
routes/index.js in our sample client application and search for the following:
index.js is the backend (server side) code.
//to prepare request for access TOKEN API
function prepareRequestForAccessToken(code) {
var cacheCtl = "no-cache";
var contentType = "application/x-www-form-urlencoded";
var result;
// assemble params for Token API
var strParams = "grant_type=authorization_code" +
"&code=" + code +
"&redirect_uri=" + _redirectUrl +
"&client_id=" + _clientId +
"&client_secret=" + _clientSecret;
var params = querystring.parse(strParams);
...
result = {
params: params,
headers: headers
}
return result;
}
// function to request for TOKEN API
function callTokenAPI(tokenParam, isRefresh, callback) {
var headers;
var params;
var requestHeadersAndParams;
// determine token parameters for access token request or refresh token request
if (isRefresh) {
requestHeadersAndParams = prepareRequestForRefreshToken(tokenParam);
} else {
requestHeadersAndParams = prepareRequestForAccessToken(tokenParam);
}
headers = requestHeadersAndParams.headers;
params = requestHeadersAndParams.params;
var request = restClient.post(_tokenApiUrl);
...
}
The code is creating the request to call TOKEN API:
https://sandboxapi.poems.com.sg/api-gateway/pspl/auth/1.0/oauth/token
Note that this API is using HTTPS POST.
The parameters in the POST body are as follows:
PARAMETER |
Type |
DESCRIPTION |
grant_type |
form |
grant type for getting token ("authorization_code") |
Code |
form |
the authcode given by the authorize API. |
redirect_uri |
form |
your callback URL for POEMS API Gateway to validate. For our sample application this is http://localhost:3000/callback |
client_id |
form |
unique ID for your application. For our sample application, use POEMS-DEMO |
client_secret |
form |
secret key given to your application during registration. For our sample application, this is 87F8D5C4-5CE9-4C12-A0CD-0A876CDF3ACE |
The sample application will then send the constructed request to the API Gateway, and you should see the following response from the npm console logs:
Response from Token API:
{
"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsic2VydmVyMSJdLCJ1c2VyX25hbWUiOiJDdXN0b21pemVQcmluY2lwYWxDb25maWd1cmUgW3VzZXJBY2NvdW50Tm89MDAwMDAwMCwgc2Vzc2lvbklkPTcyQzU5QTIwLUVCQUEtNEEzMS04M0Y2LTUwMTQ3MTQwODQ0NywgYWNjb3VudFR5cGU9ViwgZGV2aWNlSWQ9TXlEZW1vRlBdIiwic2NvcGUiOlsicmVhZCJdLCJhY2NvdW50Tm8iOiIwMDAwMDAwIiwiYWNjb3VudFR5cGUiOiJWIiwic2Vzc2lvbklkIjoiNzJDNTlBMjAtRUJBQS00QTMxLTgzRjYtNTAxNDcxNDA4NDQ3IiwiZXhwIjoxNTM3MDk1MTU2LCJkZXZpY2VJZCI6Ik15RGVtb0ZQIiwiYXV0aG9yaXRpZXMiOlsicG1vYmlsZSJdLCJqdGkiOiJjY2JiMmE4MS03OGQ3LTQ2YzQtOWQ0MC04ZTYwMjAzM2U4OWIiLCJjbGllbnRfaWQiOiJwbW9iaWxlMiJ9.roEsJ3JOgXFFVVEf_AMXoqdewSdwun7Dq3WTcwLBpnQQviaI1HinzxiKN8eSuIgwjr4TpaoHLHvti9ZKIHhD-RMBpY6zm0oucwNnLjtbghIWlvHrYOG7iI8S2UgG0eqSDpgX1rtCJftMM3k-P-_J_uo2-XaGWhDaBSwbpXHbM4aUZVru_383DkqZQEy5JRbAAJ220DSBcjifjnHDT7UrJJY-GSRSNW8jXJP40JzalE-e33pe6pFCr_IMGAhJqqRapMchomjyM97zSYfeuOOXEi0GpWD-ibEz6F9AvwueFmOk3T2ABgx8RQLSnWfQrea2RhUEkiwtc2AIElJEl5VuLg",
"token_type":"bearer",
"refresh_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsic2VydmVyMSJdLCJ1c2VyX25hbWUiOiJDdXN0b21pemVQcmluY2lwYWxDb25maWd1cmUgW3VzZXJBY2NvdW50Tm89MDAwMDAwMCwgc2Vzc2lvbklkPTcyQzU5QTIwLUVCQUEtNEEzMS04M0Y2LTUwMTQ3MTQwODQ0NywgYWNjb3VudFR5cGU9ViwgZGV2aWNlSWQ9TXlEZW1vRlBdIiwic2NvcGUiOlsicmVhZCJdLCJhY2NvdW50Tm8iOiIwMDAwMDAwIiwiYWNjb3VudFR5cGUiOiJWIiwiYXRpIjoiY2NiYjJhODEtNzhkNy00NmM0LTlkNDAtOGU2MDIwMzNlODliIiwic2Vzc2lvbklkIjoiNzJDNTlBMjAtRUJBQS00QTMxLTgzRjYtNTAxNDcxNDA4NDQ3IiwiZXhwIjoxNTM3MTMwMTU2LCJkZXZpY2VJZCI6Ik15RGVtb0ZQIiwiYXV0aG9yaXRpZXMiOlsicG1vYmlsZSJdLCJqdGkiOiJjYTAzNTU3MS1hMjBhLTRiNzUtOTk2ZC02ZjViN2U3YWNjZmQiLCJjbGllbnRfaWQiOiJwbW9iaWxlMiJ9.Wtem1uc6Wqn61nKfOonwr1oF9WJknaf5UqDueDnpUkYTeQJOoNcJRkvj6ga7BF31yEa-asVeLedkHS5Phkh7dsVrGMsKoidXzFXUsfvhXkhhtP8NAq9D5poyWEl_dJJWzcpBB7-Tj5CpUZ85FopXeYVVhMp7aKMHqvA0Ojrj8_CFvPK0pPY4sBLcoLV-zfMShykTivVqdQMjrE7ImCsJAjsqA28QIaJqGWajMJbTwRixf9PEk2vX6OmPuO0jqtcsd7kPi10tlu8mHI3q3IDWtvW5g-G2rBgCJAg6JiUV4MqigV4nb_fQU0iwf1ZT0VGpQ7Y3Sfvx76IrEJ58zrQ7dw",
"expires_in":999,
"scope":"read",
"accountNo":"2222222",
"accountType":"V",
"sessionId":"72C59A20-EBAA-4A31-83F6-501471408447",
"deviceId":"MyDemoFingerPrint",
"jti":"ccbb2a81-78d7-46c4-9d40-8e602033e89b"
}
Notice the response contains an "
access_token" and "
refresh_token", which is in the JWT (JSON Web Token) format. We use "
expires_in" to determine token expiration time. If the token is expired we call token API to request for new access token. We will describe the token renewal process below
NOTE: token contains built-in additional information about login user session which is also encoded in the access_token string to be determined by Gateway. We will only need the encoded access_token in order to successfully call the POEMS API.
4. Call the Refresh Token API (access token is expired)
Now that you have the
refresh token, you can invoke the token API to get the new access token.
NOTE: Our JWT access token is a stateless session token. Expires_in determine when access token will expire. To avoid session time out, we check token expire each and every call. If token expired, we call refresh token api to request for new access token. Unlike token api request, refresh token api request does not require user to authorize again.
Open
routes/index.js in our sample client application and search for the following:
index.js is the backend (server side) code.
//to prepare request for refresh TOKEN API
function prepareRequestForRefreshToken(refreshToken) {
var cacheCtl = "no-cache";
var contentType = "application/x-www-form-urlencoded";
var result;
// assemble params for Token API
var strParams = "grant_type=refresh_token" +
"&client_id=" + _clientId +
"&refresh_token=" + refreshToken;
var params = querystring.parse(strParams);
...
result = {
params: params,
headers: headers
}
return result;
}
// function to request for TOKEN API
function callTokenAPI(tokenParam, isRefresh, callback) {
var headers;
var params;
var requestHeadersAndParams;
// determine token parameters for access token request or refresh token request
if (isRefresh) {
requestHeadersAndParams = prepareRequestForRefreshToken(tokenParam);
} else {
requestHeadersAndParams = prepareRequestForAccessToken(tokenParam);
}
headers = requestHeadersAndParams.headers;
params = requestHeadersAndParams.params;
var request = restClient.post(_tokenApiUrl);
...
}
The code is creating the request to call TOKEN API:
https://sandboxapi.poems.com.sg/api-gateway/pspl/auth/1.0/oauth/token
Note that this API is using HTTPS POST.
The parameters in the POST body are as follows:
PARAMETER |
Type |
DESCRIPTION |
grant_type |
form |
grant type for getting new token ("refresh_token") |
client_id |
form |
unique ID for your application. For our sample application, use POEMS-DEMO |
refresh_token |
form |
JWT refresh token to request for new access token. |
The sample application will then send the constructed request to the API Gateway, and you should see the following response from the npm console logs:
Response from Token API:
{"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsic2VydmVyMSJdLCJ1c2VyX25hbWUiOiJDdXN0b21pemVQcmluY2lwYWxDb25maWd1cmUgW3VzZXJBY2NvdW50Tm89MDAwMDAwMCwgc2Vzc2lvbklkPTcyQzU5QTIwLUVCQUEtNEEzMS04M0Y2LTUwMTQ3MTQwODQ0NywgYWNjb3VudFR5cGU9ViwgZGV2aWNlSWQ9TXlEZW1vRlBdIiwic2NvcGUiOlsicmVhZCJdLCJhY2NvdW50Tm8iOiIwMDAwMDAwIiwiYWNjb3VudFR5cGUiOiJWIiwic2Vzc2lvbklkIjoiNzJDNTlBMjAtRUJBQS00QTMxLTgzRjYtNTAxNDcxNDA4NDQ3IiwiZXhwIjoxNTM3MDk1MTU2LCJkZXZpY2VJZCI6Ik15RGVtb0ZQIiwiYXV0aG9yaXRpZXMiOlsicG1vYmlsZSJdLCJqdGkiOiJjY2JiMmE4MS03OGQ3LTQ2YzQtOWQ0MC04ZTYwMjAzM2U4OWIiLCJjbGllbnRfaWQiOiJwbW9iaWxlMiJ9.roEsJ3JOgXFFVVEf_AMXoqdewSdwun7Dq3WTcwLBpnQQviaI1HinzxiKN8eSuIgwjr4TpaoHLHvti9ZKIHhD-RMBpY6zm0oucwNnLjtbghIWlvHrYOG7iI8S2UgG0eqSDpgX1rtCJftMM3k-P-_J_uo2-XaGWhDaBSwbpXHbM4aUZVru_383DkqZQEy5JRbAAJ220DSBcjifjnHDT7UrJJY-GSRSNW8jXJP40JzalE-e33pe6pFCr_IMGAhJqqRapMchomjyM97zSYfeuOOXEi0GpWD-ibEz6F9AvwueFmOk3T2ABgx8RQLSnWfQrea2RhUEkiwtc2AIElJEl5VuLg","token_type":"bearer",
"refresh_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsic2VydmVyMSJdLCJ1c2VyX25hbWUiOiJDdXN0b21pemVQcmluY2lwYWxDb25maWd1cmUgW3VzZXJBY2NvdW50Tm89MDAwMDAwMCwgc2Vzc2lvbklkPTcyQzU5QTIwLUVCQUEtNEEzMS04M0Y2LTUwMTQ3MTQwODQ0NywgYWNjb3VudFR5cGU9ViwgZGV2aWNlSWQ9TXlEZW1vRlBdIiwic2NvcGUiOlsicmVhZCJdLCJhY2NvdW50Tm8iOiIwMDAwMDAwIiwiYWNjb3VudFR5cGUiOiJWIiwiYXRpIjoiY2NiYjJhODEtNzhkNy00NmM0LTlkNDAtOGU2MDIwMzNlODliIiwic2Vzc2lvbklkIjoiNzJDNTlBMjAtRUJBQS00QTMxLTgzRjYtNTAxNDcxNDA4NDQ3IiwiZXhwIjoxNTM3MTMwMTU2LCJkZXZpY2VJZCI6Ik15RGVtb0ZQIiwiYXV0aG9yaXRpZXMiOlsicG1vYmlsZSJdLCJqdGkiOiJjYTAzNTU3MS1hMjBhLTRiNzUtOTk2ZC02ZjViN2U3YWNjZmQiLCJjbGllbnRfaWQiOiJwbW9iaWxlMiJ9.Wtem1uc6Wqn61nKfOonwr1oF9WJknaf5UqDueDnpUkYTeQJOoNcJRkvj6ga7BF31yEa-asVeLedkHS5Phkh7dsVrGMsKoidXzFXUsfvhXkhhtP8NAq9D5poyWEl_dJJWzcpBB7-Tj5CpUZ85FopXeYVVhMp7aKMHqvA0Ojrj8_CFvPK0pPY4sBLcoLV-zfMShykTivVqdQMjrE7ImCsJAjsqA28QIaJqGWajMJbTwRixf9PEk2vX6OmPuO0jqtcsd7kPi10tlu8mHI3q3IDWtvW5g-G2rBgCJAg6JiUV4MqigV4nb_fQU0iwf1ZT0VGpQ7Y3Sfvx76IrEJ58zrQ7dw",
"expires_in":999,"scope":"read","accountNo":"2222222","accountType":"V","sessionId":"72C59A20-EBAA-4A31-83F6-501471408447","deviceId":"MyDemoFingerPrint","jti":"ccbb2a81-78d7-46c4-9d40-8e602033e89b"}
5. Call the POEMS API (with the access token)
Now that you have the
access token, you can invoke the
token API to get the data.
Click . for more info about the POEMS API
NOTE: This API should be called from a server-side component.
You will need to provide a valid access token or your API call will be rejected.
Open
routes/index.js in our sample client application and search for the following:
index.js is the backend (server side) code.
//function to call orders api
function callTodayOrdersAPI(accessToken, callback) {
var result;
// assemble params for orders API
var strParams = "osVersion=1" +
"&language=1";
var params = querystring.parse(strParams);
// assemble headers for orders API
var strHeaders = "x-api-key=" + _x_api_key;
var headers = querystring.parse(strHeaders);
_.set(headers, "Authorization", "Bearer " + accessToken);
...
var request = restClient.get(_todayOrderApiUrl);
...
}
The code is creating the request to call the following POEMS API:
https://sandboxapi.poems.com.sg/api-gateway/pspl/mobile2/1.0/global/order/today
Note that this API is called using HTTPS
GET.
The additional parameters are as follows:
PARAMETER |
Type |
DESCRIPTION |
x_api_key |
header |
Api key provided to you during registration. For our sample application, this is B8A6F3CF-93FA-4116-8424-31E4DFEC10EB |
Authorization |
header |
Access token requested. Format should look like this.
"Bearer [access_token]".
|
Providing the Access Token in the Request Header
The access token should be provided in the
header of your API call under "Bearer".
You should see something like this in the npm console logs:
Request Header for Order API:
{"x-api-key":"B8A6F3CF-93FA-4116-8424-31E4DFEC10EB","Authorization":"Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsic2VydmVyMSJdLCJ1c2VyX25hbWUiOiJDdXN0b21pemVQcmluY2lwYWxDb25maWd1cmUgW3VzZXJBY2NvdW50Tm89MDAwMDAwMCwgc2Vzc2lvbklkPTcyQzU5QTIwLUVCQUEtNEEzMS04M0Y2LTUwMTQ3MTQwODQ0NywgYWNjb3VudFR5cGU9ViwgZGV2aWNlSWQ9TXlEZW1vRlBdIiwic2NvcGUiOlsicmVhZCJdLCJhY2NvdW50Tm8iOiIwMDAwMDAwIiwiYWNjb3VudFR5cGUiOiJWIiwic2Vzc2lvbklkIjoiNzJDNTlBMjAtRUJBQS00QTMxLTgzRjYtNTAxNDcxNDA4NDQ3IiwiZXhwIjoxNTM3MDk1MTA3LCJkZXZpY2VJZCI6Ik15RGVtb0ZQIiwiYXV0aG9yaXRpZXMiOlsicG1vYmlsZSJdLCJqdGkiOiJkMDljNWMxZS0zY2Q5LTQyZjYtOWEzMy00NmQ0MzIxYWQwMDUiLCJjbGllbnRfaWQiOiJwbW9iaWxlMiJ9.ZJbi8YdtmvemYWgdDFKYbAt8I5x1ugPzJMwEj18jV9llDqXEKGVP_XouKtsChyZOy-H38Qe8zLNH4MBWjH6ShNJxFr0DlTMLtTunhHaefV1YfQK97kc9fzQsVZuUOT6T241Yo-wTsuKGIRkfoBvxRhG1n8y_Lmsew7Xxzeg6FcgFpXmUabwGHO3mMAsYW8CUYgPED7uVqhB7EQsNgVDSpRyhLLGKARUSryHyMEUC_PYIU6OlimnFd0zfHX2XUNkhoMWv7gOu7yILlEpjaWtpVt6B9Y4IKgEers7FTXsJumpX4Yk-4rAEyQzozTvOGkcV4xGsFX84nBSinGvgb2Kz_g"}
Once this is done, you should see the orders JSON in the npm console logs that looks like this:
Response from Order API:
{"msg":"Success","code":1,"passwordRequire":true,"twoFARequire":false,"newOrderPMPTopic":"ZZ|ZZ_2222222|POEMS","orders":[{"counterID":"FT/SIMEX/SIMEX/CNU18","name":
"SGX FTSE CHINA A50 INDEX - SEP 2018","symbol":"CNU18","product":"FT","productIcon":"FUT","action":"BUY","status":"OP","statusColor":"GRAY","submittedPrice":"11,240
","submittedQty":"1","remainingQty":null,"submittedTime":"03 Sep 2018 10:48 AM","executedPrice":"0","executedQty":"0","updatedTime":null,"orderNo":"86506","lotSize"
:null,"stopPrice":"","stopLimitPrice":"","ocoPrice":null,"trailingStop":null,"triggeredType":null,"units":null,"investedAmmount":null,"nav":null,"netSalesCharge":nu
ll,"receivedTime":"","executedTime":"","amendURI":"","withdrawURI":"/FT/orders/withdraw/86506","isTradeable":false,"isInfoAvailable":false,"orderDetailsURI":"/FT/or
ders/today/86506","orderPMPTopic":null,"orderType":"Limit","orderStatusDesc":"Order Pending","cancelURI":null,"referenceNo":null,"delayIndicator":"","market":"SIMEX
","exchange":"SIMEX","paymentCurrency":null,"latestUpdatedTime":"2018-09-03 10:48:02"},{"counterID":"FT/SIMEX/SIMEX/SGU18","name":"MSCI SINGAPORE INDX - SEP 2018","
symbol":"SGU18","product":"FT","productIcon":"FUT","action":"BUY","status":"OP","statusColor":"GRAY","submittedPrice":"362","submittedQty":"1","remainingQty":null,"
submittedTime":"03 Sep 2018 10:46 AM","executedPrice":"0","executedQty":"0","updatedTime":null,"orderNo":"86505","lotSize":null,"stopPrice":"","stopLimitPrice":"","
ocoPrice":null,"trailingStop":null,"triggeredType":null,"units":null,"investedAmmount":null,"nav":null,"netSalesCharge":null,"receivedTime":"","executedTime":"","am
endURI":"","withdrawURI":"/FT/orders/withdraw/86505","isTradeable":false,"isInfoAvailable":false,"orderDetailsURI":"/FT/orders/today/86505","orderPMPTopic":null,"or
derType":"Limit","orderStatusDesc":"Order Pending","cancelURI":null,"referenceNo":null,"delayIndicator":"","market":"SIMEX","exchange":"SIMEX","paymentCurrency":nul
l,"latestUpdatedTime":"2018-09-03 10:46:21"}]}
5. Use the JSON object to Show data
If you have followed the steps above correctly, you should have gotten the data JSON object from poems api.
SUMMARY
You've successfully used the OAuth2 process to get the data from POEMS API sandbox environment.