Change api auth to oauth #52

Merged
bilal.catic merged 2 commits from change-API-auth-to-oauth into master 2019-08-31 00:08:17 +02:00
9 changed files with 211 additions and 6 deletions

View File

@@ -62,6 +62,7 @@ const officeRnDAPIErrors = {
FAILED_TO_DELETE_FEES: 'Failed to delete fees in ORD',
FAILED_TO_ADD_FEES: 'Failed to add fees in ORD',
MEMBERSHIPS_ARE_NOT_LOADED_CORRECTLY: 'Memberships are not loaded correctly',
OAUTH_FAILED: 'Failed to fetch new OAUTH token',
};
const integrationServiceErrors = {
PROCESSING_TRY_AGAIN: 'Incident calculations are in progress. Please try again in a few minutes',

8
cronServices/oauth.js Normal file
View File

@@ -0,0 +1,8 @@
'use strict';
const { refreshOauthToken } = require('../services/officeRnD/oauth');
refreshOauthToken().then(() => process.exit()).catch((error) => {
console.log(error);
process.exit();
});

View File

@@ -1,7 +1,6 @@
BASIC_AUTH_USERNAME=username
BASIC_AUTH_PASSWORD=password
OFFICE_RnD_TOKEN=token for Office RnD API requests
MAX_BACK_TO_BACK_DIFFERENCE=Time in minutes
EARLIEST_UNLOCK=Time in minutes
@@ -32,6 +31,10 @@ DISCOUNT_LEVEL_2_HOURS=Hours requred to apply DISCOUNT_LEVEL_2_PERCENTAGE discou
DISCOUNT_LEVEL_2_PERCENTAGE=Discount to apply in percentage, if DISCOUNT_LEVEL_2_HOURS of billable hours is booked
DISCOUNT_PLANS=Plan names for which discount is available. Comma-separated
ORD_OAUTH_CLIENT_ID=Client ID for this app, created in ORD; Used to obtain OAUTH token
ORD_OAUTH_CLIENT_SECRET=Client secret for this app; Used to obtain OAUTH token
ORD_OAUTH_URL=https://identity.officernd.com/oauth/token
#More about pool option : http://docs.sequelizejs.com/class/lib/sequelize.js~Sequelize.html
DB_POOL_MAX_CONNECTIONS=Maximum number of connection in pool (ex. 18)
DB_POOL_ACQUIRE=The maximum time, in milliseconds, that pool will try to get connection before throwing error (ex. 120000)

View File

@@ -1,10 +1,28 @@
const axios = require('axios');
const officeRnDToken = process.env.OFFICE_RnD_TOKEN;
const { getToken } = require('../services/officeRnD/oauth');
console.log('API file');
const API = axios.create({
baseURL: 'https://app.officernd.com/api/v1/organizations/sima-space-test-environment',
headers: {'Authorization': `Bearer ${officeRnDToken}`}
headers: {'Authorization': `Bearer TOKEN`}
});
// request interceptor
API.interceptors.request.use((config) => {
return new Promise((resolve, reject) => {
getToken(true)
.then((token) => {
config.headers.Authorization = `Bearer ${token}`;
resolve(config);
})
.catch((error) => {
reject(error);
});
});
}, (error) => {
// Do something with request error
return Promise.reject(error);
});
module.exports = {

View File

@@ -0,0 +1,27 @@
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('accessTokens', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
token: Sequelize.TEXT,
validUntil: Sequelize.DATE,
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('accessTokens');
}
};

12
models/accessToken.js Normal file
View File

@@ -0,0 +1,12 @@
'use strict';
module.exports = (sequelize, DataTypes) => {
const processing = sequelize.define('accessToken', {
token: DataTypes.TEXT,
validUntil: DataTypes.DATE,
}, {});
processing.associate = function(models) {
// associations can be defined here
};
return processing;
};

23
package-lock.json generated
View File

@@ -684,8 +684,7 @@
"decode-uri-component": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
"dev": true
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
},
"deep-extend": {
"version": "0.6.0",
@@ -3029,6 +3028,16 @@
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
},
"query-string": {
"version": "6.8.2",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-6.8.2.tgz",
"integrity": "sha512-J3Qi8XZJXh93t2FiKyd/7Ec6GNifsjKXUsVFkSBj/kjLsDylWhnCz4NT1bkPcKotttPW+QbKGqqPH8OoI2pdqw==",
"requires": {
"decode-uri-component": "^0.2.0",
"split-on-first": "^1.0.0",
"strict-uri-encode": "^2.0.0"
}
},
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@@ -3519,6 +3528,11 @@
"through": "2"
}
},
"split-on-first": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
"integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="
},
"split-string": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
@@ -3562,6 +3576,11 @@
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
},
"strict-uri-encode": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
"integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",

View File

@@ -16,7 +16,8 @@
"start-client": "cd client && yarn start",
"start": "node server.js",
"heroku-postbuild": "NPM_CONFIG_PRODUCTION=false npm install --prefix client && npm run build --prefix client",
"check-booking-changes": "node ./cronServices/checkBookingChanges.js"
"check-booking-changes": "node ./cronServices/checkBookingChanges.js",
"refresh-oauth": "node ./cronServices/oauth.js"
},
"engines": {
"node": "11.12.x"
@@ -32,6 +33,7 @@
"moment": "^2.24.0",
"moment-timezone": "^0.5.25",
"pg": "^7.11.0",
"query-string": "^6.8.2",
"sequelize": "^5.8.6",
"sequelize-cli": "^5.4.0"
},

115
services/officeRnD/oauth.js Normal file
View File

@@ -0,0 +1,115 @@
'use strict';
require('dotenv').config();
const moment = require('moment');
const queryString = require('query-string');
const { API } = require('../../helpers/api');
const db = require('../../models/index');
const { officeRnDAPIErrors } = require('../../constants/constants');
const updateToken = (token, validUntil) => {
return new Promise((resolve, reject) => {
const values = {token, validUntil};
db.accessToken.update(values, {where:{}})
.then(([numberOfUpdatedRows]) => {
if (numberOfUpdatedRows > 0){
resolve(true);
}else{
db.accessToken.bulkCreate([{token, validUntil}])
.then(() => {
resolve(false);
})
.catch((error) => reject(error));
}
})
.catch((error) => reject(error));
});
};
const getToken = (returnTokenOnly = false) => {
return new Promise((resolve, reject) => {
db.accessToken.findAll()
.then((results) => {
if (results && results.length > 0){
const token = results[0].getDataValue('token');
const validUntil = results[0].getDataValue('validUntil');
const validUntilMoment = moment(validUntil);
if (validUntilMoment.isBefore(moment())){
refreshOauthToken()
.then((oauthResult) => {
if (returnTokenOnly){
resolve(oauthResult.token);
}else{
resolve(oauthResult);
}
})
.catch((error) => reject(error));
}else{
if (returnTokenOnly){
resolve(token);
}else {
resolve({token, validUntil});
}
}
}else{
refreshOauthToken()
.then((oauthResult) => {
if (returnTokenOnly){
resolve(oauthResult.token);
}else{
resolve(oauthResult);
}
})
.catch((error) => reject(error));
}
})
.catch((error) => reject(error));
});
};
const refreshOauthToken = () => {
return new Promise((resolve, reject) => {
const clientID = process.env.ORD_OAUTH_CLIENT_ID;
const clientSecret = process.env.ORD_OAUTH_CLIENT_SECRET;
const OAUTHUrl = process.env.ORD_OAUTH_URL;
const OAUTHRequestBody = {
client_id: clientID,
client_secret: clientSecret,
grant_type: 'client_credentials',
scope: 'officernd.api.read officernd.api.write',
};
const config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
};
API.post(OAUTHUrl, queryString.stringify(OAUTHRequestBody), config)
.then((oauthResponse) => {
const responseData = oauthResponse && oauthResponse.data ? oauthResponse.data : null;
if (responseData){
const { access_token: accessToken, expires_in: expiresIn } = responseData;
if (accessToken && expiresIn){
const validUntil = moment().add(expiresIn, 'seconds').toISOString();
updateToken(accessToken, validUntil)
.then(() => resolve({token: accessToken, validUntil}))
.catch((error) => reject(error));
}else{
reject(officeRnDAPIErrors.OAUTH_FAILED);
}
}else{
reject(officeRnDAPIErrors.OAUTH_FAILED);
}
})
.catch((error) => {
console.log('Error obtaining OAUTH token : ', error);
reject();
});
});
};
module.exports = {
getToken,
refreshOauthToken,
};