Skip to content

Commit

Permalink
Merge pull request #13 from mojaloop/feature/MBXSDK-9
Browse files Browse the repository at this point in the history
Add OAuth2.0 periodic token refresh functionality
  • Loading branch information
bushjames authored Sep 12, 2019
2 parents 0bc8587 + d33319c commit 402a4a3
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 85 deletions.
27 changes: 17 additions & 10 deletions src/lib/mojaloop-requests/mojaloopRequests.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const buildUrl = common.buildUrl;
const throwOrJson = common.throwOrJson;

const JwsSigner = require('../jws').signer;
const WSO2Auth = require('./wso2auth');


/**
Expand Down Expand Up @@ -58,11 +59,16 @@ class MojaloopRequests {

// Switch or peer DFSP endpoint
this.peerEndpoint = `${this.transportScheme}://${config.peerEndpoint}`;

this.wso2Auth = new WSO2Auth({
...config.wso2Auth,
logger: config.logger
});
}


/**
* Executes a GET /parties request for the specified identifier type and identifier
* Executes a GET /parties request for the specified identifier type and identifier
*
* @returns {object} - JSON response body if one was received
*/
Expand Down Expand Up @@ -127,7 +133,7 @@ class MojaloopRequests {
async putQuotesError(quoteId, error, destFspId) {
return this._put(`quotes/${quoteId}/error`, 'quotes', error, destFspId);
}


/**
* Executes a POST /transfers request for the specified transfer prepare
Expand Down Expand Up @@ -157,14 +163,14 @@ class MojaloopRequests {
async putTransfersError(transferId, error, destFspId) {
return this._put(`transfers/${transferId}/error`, 'transfers', error, destFspId);
}


/**
* Utility function for building outgoing request headers as required by the mojaloop api spec
*
* @returns {object} - headers object for use in requests to mojaloop api endpoints
*/
_buildHeaders (method, resourceType, dest) {
async _buildHeaders (method, resourceType, dest) {
let headers = {
'content-type': `application/vnd.interoperability.${resourceType}+json;version=1.0`,
'date': new Date().toUTCString(),
Expand All @@ -176,8 +182,9 @@ class MojaloopRequests {
}

//Need to populate Bearer Token for WS02 if Sim is pointing to WS02
if(this.config.wso2BearerToken) {
headers['Authorization'] = `Bearer ${this.config.wso2BearerToken}`;
const token = await this.wso2Auth.getToken();
if(token) {
headers['Authorization'] = `Bearer ${token}`;
}

// dont add accept header to PUT requests
Expand All @@ -193,10 +200,10 @@ class MojaloopRequests {
const reqOpts = {
method: 'GET',
uri: buildUrl(this.peerEndpoint, url),
headers: this._buildHeaders('GET', resourceType, dest),
headers: await this._buildHeaders('GET', resourceType, dest),
agent: this.agent,
resolveWithFullResponse: true,
simple: false
simple: false
};

// Note we do not JWS sign requests with no body i.e. GET requests
Expand All @@ -216,7 +223,7 @@ class MojaloopRequests {
const reqOpts = {
method: 'PUT',
uri: buildUrl(this.peerEndpoint, url),
headers: this._buildHeaders('PUT', resourceType, dest),
headers: await this._buildHeaders('PUT', resourceType, dest),
body: body,
agent: this.agent,
resolveWithFullResponse: true,
Expand All @@ -242,7 +249,7 @@ class MojaloopRequests {
const reqOpts = {
method: 'POST',
uri: buildUrl(this.peerEndpoint, url),
headers: this._buildHeaders('POST', resourceType, dest),
headers: await this._buildHeaders('POST', resourceType, dest),
body: body,
agent: this.agent,
resolveWithFullResponse: true,
Expand Down
87 changes: 87 additions & 0 deletions src/lib/mojaloop-requests/wso2auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**************************************************************************
* (C) Copyright ModusBox Inc. 2019 - All rights reserved. *
* *
* This file is made available under the terms of the license agreement *
* specified in the corresponding source code repository. *
* *
* ORIGINAL AUTHOR: *
* Yevhen Kyriukha - [email protected] *
**************************************************************************/

'use strict';

const request = require('request-promise-native');

const DEFAULT_REFRESH_INTERVAL_SECONDS = 3600;

/**
* Obtain WSO2 bearer token and periodically refresh it
*/
class WSO2Auth {
/**
*
* @param {Object} opts
* @param {String} opts.logger
* @param {String} [opts.clientKey] Customer Key
* @param {String} [opts.clientSecret] Customer Secret
* @param {String} [opts.tokenEndpoint] WSO2 Endpoint URL
* @param {String} [opts.refreshSeconds] WSO2 token refresh interval in seconds
* @param {String} [opts.staticToken] WSO2 static bearer token
*/
constructor(opts) {
this.logger = opts.logger;
this.refreshSeconds = opts.refreshSeconds || DEFAULT_REFRESH_INTERVAL_SECONDS;

if (this.refreshSeconds <= 0) {
throw new Error('WSO2 auth config: token must be a positive integer value');
}
if (!this.logger) {
throw new Error('WSO2 auth config requires logger property');
}

if (opts.tokenEndpoint && opts.clientKey && opts.clientSecret) {
this.basicToken = Buffer.from(`${opts.clientKey}:${opts.clientSecret}`)
.toString('base64');
this.endpoint = opts.tokenEndpoint;
} else if (opts.staticToken) {
this.logger.info('WSO2 auth config token API data not set, fallback to static token');
this.token = opts.staticToken;
} else {
// throw new Error('WSO2 auth error: neither token API data nor static token is set');
this.token = null;
}
}

async refreshToken() {
this.logger.debug('WSO2 token refresh initiated');
const reqOpts = {
method: 'POST',
uri: this.endpoint,
headers: {
'Authorization': `Basic ${this.basicToken}`,
'Content-Type': 'application/x-www-form-urlencoded'
},
body: {
grant_type: 'client_credentials'
},
json: true
};
try {
const response = await request(reqOpts);
this.token = response.access_token;
this.logger.debug('WSO2 token refreshed successfully');
} catch (error) {
this.logger.error(`Error performing WSO2 token refresh: ${error.cause}`);
}
}

async getToken() {
if (this.token === undefined && !this.tokenRefreshInterval) {
await this.refreshToken();
this.tokenRefreshInterval = setInterval(this.refreshToken.bind(this), this.refreshSeconds * 1000);
}
return this.token;
}
}

module.exports = WSO2Auth;
Loading

0 comments on commit 402a4a3

Please sign in to comment.