@ndustrial/contxt-sdk
Advanced tools
Comparing version 0.0.2 to 0.0.4
388
es/index.js
@@ -29,3 +29,4 @@ import axios from 'axios'; | ||
auth: { | ||
authorizationPath: '/callback' | ||
authorizationPath: '/callback', | ||
tokenExpiresAtBufferMs: 5 * 60 * 1000 | ||
} | ||
@@ -62,3 +63,16 @@ }; | ||
var defineProperty = function (obj, key, value) { | ||
if (key in obj) { | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: true, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} else { | ||
obj[key] = value; | ||
} | ||
return obj; | ||
}; | ||
@@ -138,2 +152,3 @@ var _extends = Object.assign || function (target) { | ||
var Config = function () { | ||
function Config(userConfig, externalModules) { | ||
@@ -146,4 +161,4 @@ classCallCheck(this, Config); | ||
externalModules: externalModules, | ||
customModuleConfigs: userConfig.customModuleConfigs, | ||
env: userConfig.env | ||
customModuleConfigs: userConfig.auth.customModuleConfigs, | ||
env: userConfig.auth.env | ||
}); | ||
@@ -168,2 +183,3 @@ | ||
} | ||
}, { | ||
@@ -180,3 +196,2 @@ key: '_getAudiences', | ||
return _extends({}, this._getInternalAudiences({ | ||
@@ -188,2 +203,3 @@ customModuleConfigs: customModuleConfigs, | ||
} | ||
}, { | ||
@@ -207,2 +223,3 @@ key: '_getExternalAudiences', | ||
} | ||
}, { | ||
@@ -235,2 +252,3 @@ key: '_getInternalAudiences', | ||
var Facilities = function () { | ||
function Facilities(sdk, request) { | ||
@@ -249,2 +267,3 @@ classCallCheck(this, Facilities); | ||
} | ||
}, { | ||
@@ -260,2 +279,3 @@ key: "getAll", | ||
var Request = function () { | ||
function Request(sdk, audienceName) { | ||
@@ -267,5 +287,7 @@ var _this = this; | ||
this._insertHeaders = function (config) { | ||
config.headers.common.Authorization = 'Bearer ' + _this._sdk.auth.getCurrentApiToken(_this._audienceName); | ||
return _this._sdk.auth.getCurrentApiToken(_this._audienceName).then(function (apiToken) { | ||
config.headers.common.Authorization = 'Bearer ' + apiToken; | ||
return config; | ||
return config; | ||
}); | ||
}; | ||
@@ -290,2 +312,3 @@ | ||
} | ||
}, { | ||
@@ -301,2 +324,3 @@ key: 'get', | ||
} | ||
}, { | ||
@@ -312,2 +336,3 @@ key: 'head', | ||
} | ||
}, { | ||
@@ -323,2 +348,3 @@ key: 'options', | ||
} | ||
}, { | ||
@@ -334,2 +360,3 @@ key: 'patch', | ||
} | ||
}, { | ||
@@ -345,2 +372,3 @@ key: 'post', | ||
} | ||
}, { | ||
@@ -356,2 +384,3 @@ key: 'put', | ||
} | ||
}, { | ||
@@ -367,2 +396,3 @@ key: 'request', | ||
} | ||
}]); | ||
@@ -374,11 +404,2 @@ return Request; | ||
/** | ||
* Check if we're required to add a port number. | ||
* | ||
* @see https://url.spec.whatwg.org/#default-port | ||
* @param {Number|String} port Port number we need to check | ||
* @param {String} protocol Protocol we need to check against. | ||
* @returns {Boolean} Is it a default port for the given protocol | ||
* @api private | ||
*/ | ||
var requiresPort = function required(port, protocol) { | ||
@@ -414,9 +435,2 @@ protocol = protocol.split(':')[0]; | ||
/** | ||
* Decode a URI encoded string. | ||
* | ||
* @param {String} input The URI encoded string. | ||
* @returns {String} The decoded string. | ||
* @api private | ||
*/ | ||
function decode(input) { | ||
@@ -426,9 +440,2 @@ return decodeURIComponent(input.replace(/\+/g, ' ')); | ||
/** | ||
* Simple query string parser. | ||
* | ||
* @param {String} query The query string that needs to be parsed. | ||
* @returns {Object} | ||
* @api public | ||
*/ | ||
function querystring(query) { | ||
@@ -439,7 +446,2 @@ var parser = /([^=?&]+)=?([^&]*)/g | ||
// | ||
// Little nifty parsing hack, leverage the fact that RegExp.exec increments | ||
// the lastIndex property so we can continue executing this loop until we've | ||
// parsed all results. | ||
// | ||
for (; | ||
@@ -453,10 +455,2 @@ part = parser.exec(query); | ||
/** | ||
* Transform a query string to an object. | ||
* | ||
* @param {Object} obj Object that should be transformed. | ||
* @param {String} prefix Optional prefix. | ||
* @returns {String} | ||
* @api public | ||
*/ | ||
function querystringify(obj, prefix) { | ||
@@ -467,5 +461,2 @@ prefix = prefix || ''; | ||
// | ||
// Optionally prefix with a '?' if needed | ||
// | ||
if ('string' !== typeof prefix) prefix = '?'; | ||
@@ -482,5 +473,2 @@ | ||
// | ||
// Expose the module. | ||
// | ||
var stringify = querystringify; | ||
@@ -497,46 +485,14 @@ var parse = querystring; | ||
/** | ||
* These are the parse rules for the URL parser, it informs the parser | ||
* about: | ||
* | ||
* 0. The char it Needs to parse, if it's a string it should be done using | ||
* indexOf, RegExp using exec and NaN means set as current value. | ||
* 1. The property we should set when parsing this value. | ||
* 2. Indication if it's backwards or forward parsing, when set as number it's | ||
* the value of extra chars that should be split off. | ||
* 3. Inherit from location if non existing in the parser. | ||
* 4. `toLowerCase` the resulting value. | ||
*/ | ||
var rules = [ | ||
['#', 'hash'], // Extract from the back. | ||
['?', 'query'], // Extract from the back. | ||
['/', 'pathname'], // Extract from the back. | ||
['@', 'auth', 1], // Extract from the front. | ||
[NaN, 'host', undefined, 1, 1], // Set left over value. | ||
[/:(\d+)$/, 'port', undefined, 1], // RegExp the back. | ||
[NaN, 'hostname', undefined, 1, 1] // Set left over. | ||
['#', 'hash'], | ||
['?', 'query'], | ||
['/', 'pathname'], | ||
['@', 'auth', 1], | ||
[NaN, 'host', undefined, 1, 1], | ||
[/:(\d+)$/, 'port', undefined, 1], | ||
[NaN, 'hostname', undefined, 1, 1] | ||
]; | ||
/** | ||
* These properties should not be copied or inherited from. This is only needed | ||
* for all non blob URL's as a blob URL does not include a hash, only the | ||
* origin. | ||
* | ||
* @type {Object} | ||
* @private | ||
*/ | ||
var ignore = { hash: 1, query: 1 }; | ||
/** | ||
* The location object differs when your code is loaded through a normal page, | ||
* Worker or through a worker using a blob. And with the blobble begins the | ||
* trouble as the location object will contain the URL of the blob, not the | ||
* location of the page where our code is loaded in. The actual origin is | ||
* encoded in the `pathname` so we can thankfully generate a good "default" | ||
* location from it so we can generate proper relative URL's again. | ||
* | ||
* @param {Object|String} loc Optional default location object. | ||
* @returns {Object} lolcation object. | ||
* @api public | ||
*/ | ||
function lolcation(loc) { | ||
@@ -568,17 +524,2 @@ loc = loc || commonjsGlobal.location || {}; | ||
/** | ||
* @typedef ProtocolExtract | ||
* @type Object | ||
* @property {String} protocol Protocol matched in the URL, in lowercase. | ||
* @property {Boolean} slashes `true` if protocol is followed by "//", else `false`. | ||
* @property {String} rest Rest of the URL that is not part of the protocol. | ||
*/ | ||
/** | ||
* Extract protocol information from a URL with/without double slash ("//"). | ||
* | ||
* @param {String} address URL we want to extract from. | ||
* @return {ProtocolExtract} Extracted information. | ||
* @api private | ||
*/ | ||
function extractProtocol(address) { | ||
@@ -594,10 +535,2 @@ var match = protocolre.exec(address); | ||
/** | ||
* Resolve a relative URL pathname against a base URL pathname. | ||
* | ||
* @param {String} relative Pathname of the relative URL. | ||
* @param {String} base Pathname of the base URL. | ||
* @return {String} Resolved pathname. | ||
* @api private | ||
*/ | ||
function resolve(relative, base) { | ||
@@ -629,13 +562,2 @@ var path = (base || '/').split('/').slice(0, -1).concat(relative.split('/')) | ||
/** | ||
* The actual URL instance. Instead of returning an object we've opted-in to | ||
* create an actual constructor as it's much more memory efficient and | ||
* faster and it pleases my OCD. | ||
* | ||
* @constructor | ||
* @param {String} address URL we want to parse. | ||
* @param {Object|String} location Location defaults for relative paths. | ||
* @param {Boolean|Function} parser Parser for the query string. | ||
* @api public | ||
*/ | ||
function URL(address, location, parser) { | ||
@@ -652,13 +574,2 @@ if (!(this instanceof URL)) { | ||
// | ||
// The following if statements allows this module two have compatibility with | ||
// 2 different API: | ||
// | ||
// 1. Node.js's `url.parse` api which accepts a URL, boolean as arguments | ||
// where the boolean indicates that the query string should also be parsed. | ||
// | ||
// 2. The `URL` interface of the browser which accepts a URL, object as | ||
// arguments. The supplied object will be used as default values / fall-back | ||
// for relative paths. | ||
// | ||
if ('object' !== type && 'string' !== type) { | ||
@@ -673,5 +584,2 @@ parser = location; | ||
// | ||
// Extract protocol information before running the instructions. | ||
// | ||
extracted = extractProtocol(address || ''); | ||
@@ -683,6 +591,2 @@ relative = !extracted.protocol && !extracted.slashes; | ||
// | ||
// When the authority component is absent the URL starts with a path | ||
// component. | ||
// | ||
if (!extracted.slashes) instructions[2] = [/(.*)/, 'pathname']; | ||
@@ -716,19 +620,7 @@ | ||
// | ||
// Hostname, host and protocol should be lowercased so they can be used to | ||
// create a proper `origin`. | ||
// | ||
if (instruction[4]) url[key] = url[key].toLowerCase(); | ||
} | ||
// | ||
// Also parse the supplied query string in to an object. If we're supplied | ||
// with a custom parser as function use that instead of the default build-in | ||
// parser. | ||
// | ||
if (parser) url.query = parser(url.query); | ||
// | ||
// If the URL is relative, resolve the pathname against the base URL. | ||
// | ||
if ( | ||
@@ -743,7 +635,2 @@ relative | ||
// | ||
// We should not add port numbers if they are already the default port number | ||
// for a given protocol. As the host also contains the port number we're going | ||
// override it with the hostname which contains no port number. | ||
// | ||
if (!requiresPort(url.port, url.protocol)) { | ||
@@ -754,5 +641,2 @@ url.host = url.hostname; | ||
// | ||
// Parse down the `auth` for the username and password. | ||
// | ||
url.username = url.password = ''; | ||
@@ -769,21 +653,5 @@ if (url.auth) { | ||
// | ||
// The href is just the compiled result. | ||
// | ||
url.href = url.toString(); | ||
} | ||
/** | ||
* This is convenience method for changing properties in the URL instance to | ||
* insure that they all propagate correctly. | ||
* | ||
* @param {String} part Property we need to adjust. | ||
* @param {Mixed} value The newly assigned value. | ||
* @param {Boolean|Function} fn When setting the query, it will be the function | ||
* used to parse the query. | ||
* When setting the protocol, double slash will be | ||
* removed from the final url if it is true. | ||
* @returns {URL} | ||
* @api public | ||
*/ | ||
function set$1(part, value, fn) { | ||
@@ -868,9 +736,2 @@ var url = this; | ||
/** | ||
* Transform the properties back in to a valid and full URL string. | ||
* | ||
* @param {Function} stringify Optional query stringify function. | ||
* @returns {String} | ||
* @api public | ||
*/ | ||
function toString(stringify) { | ||
@@ -905,6 +766,2 @@ if (!stringify || 'function' !== typeof stringify) stringify = querystringify_1.stringify; | ||
// | ||
// Expose the URL parser and some additional properties that might be useful for | ||
// others or testing. | ||
// | ||
URL.extractProtocol = extractProtocol; | ||
@@ -917,2 +774,3 @@ URL.location = lolcation; | ||
var Auth0WebAuth = function () { | ||
function Auth0WebAuth(sdk) { | ||
@@ -948,2 +806,3 @@ classCallCheck(this, Auth0WebAuth); | ||
} | ||
}, { | ||
@@ -954,2 +813,3 @@ key: 'getCurrentApiToken', | ||
} | ||
}, { | ||
@@ -960,12 +820,15 @@ key: 'getProfile', | ||
return new Promise(function (resolve, reject) { | ||
_this._auth0.client.userInfo(_this.getCurrentAccessToken(), function (err, profile) { | ||
if (err) { | ||
reject(err); | ||
} | ||
return this.getCurrentAccessToken().then(function (accessToken) { | ||
return new Promise(function (resolve, reject) { | ||
_this._auth0.client.userInfo(accessToken, function (err, profile) { | ||
if (err) { | ||
reject(err); | ||
} | ||
resolve(profile); | ||
resolve(profile); | ||
}); | ||
}); | ||
}); | ||
} | ||
}, { | ||
@@ -1007,2 +870,3 @@ key: 'handleAuthentication', | ||
} | ||
}, { | ||
@@ -1015,2 +879,3 @@ key: 'isAuthenticated', | ||
} | ||
}, { | ||
@@ -1021,2 +886,3 @@ key: 'logIn', | ||
} | ||
}, { | ||
@@ -1033,2 +899,3 @@ key: 'logOut', | ||
} | ||
}, { | ||
@@ -1039,2 +906,3 @@ key: '_defaultOnRedirect', | ||
} | ||
}, { | ||
@@ -1059,13 +927,19 @@ key: '_getApiToken', | ||
} | ||
}, { | ||
key: '_getCurrentTokenByType', | ||
value: function _getCurrentTokenByType(type) { | ||
var propertyName = type + 'Token'; | ||
var _this4 = this; | ||
if (!(this._sessionInfo && this._sessionInfo[propertyName])) { | ||
throw new Error('No ' + type + ' token found'); | ||
} | ||
return new Promise(function (resolve, reject) { | ||
var propertyName = type + 'Token'; | ||
return this._sessionInfo[propertyName]; | ||
if (!(_this4._sessionInfo && _this4._sessionInfo[propertyName])) { | ||
reject(new Error('No ' + type + ' token found')); | ||
} | ||
resolve(_this4._sessionInfo[propertyName]); | ||
}); | ||
} | ||
}, { | ||
@@ -1079,2 +953,3 @@ key: '_getRedirectPathname', | ||
} | ||
}, { | ||
@@ -1089,9 +964,10 @@ key: '_loadSession', | ||
} | ||
}, { | ||
key: '_parseWebAuthHash', | ||
value: function _parseWebAuthHash() { | ||
var _this4 = this; | ||
var _this5 = this; | ||
return new Promise(function (resolve, reject) { | ||
_this4._auth0.parseHash(function (err, hashResponse) { | ||
_this5._auth0.parseHash(function (err, hashResponse) { | ||
if (err || !hashResponse) { | ||
@@ -1105,2 +981,3 @@ return reject(err || new Error('No valid tokens returned from auth0')); | ||
} | ||
}, { | ||
@@ -1119,3 +996,109 @@ key: '_saveSession', | ||
var TYPE = 'auth0WebAuth'; | ||
var MachineAuth = function () { | ||
function MachineAuth(sdk) { | ||
classCallCheck(this, MachineAuth); | ||
this._sdk = sdk; | ||
this._sessionInfo = {}; | ||
if (!this._sdk.config.auth.clientId) { | ||
throw new Error('clientId is required for the MachineAuth config'); | ||
} | ||
if (!this._sdk.config.auth.clientSecret) { | ||
throw new Error('clientSecret is required for the MachineAuth config'); | ||
} | ||
} | ||
createClass(MachineAuth, [{ | ||
key: 'getCurrentApiToken', | ||
value: function getCurrentApiToken(audienceName) { | ||
if (this.isAuthenticated(audienceName)) { | ||
return Promise.resolve(this._sessionInfo[audienceName].apiToken); | ||
} | ||
return this._getNewSessionInfo(audienceName).then(function (sessionInfo) { | ||
return sessionInfo.apiToken; | ||
}); | ||
} | ||
}, { | ||
key: 'isAuthenticated', | ||
value: function isAuthenticated(audienceName) { | ||
if (!(this._sessionInfo && this._sessionInfo[audienceName])) { | ||
return false; | ||
} | ||
var _sessionInfo$audience = this._sessionInfo[audienceName], | ||
apiToken = _sessionInfo$audience.apiToken, | ||
expiresAt = _sessionInfo$audience.expiresAt; | ||
var tokenExpiresAtBufferMs = this._sdk.config.auth.tokenExpiresAtBufferMs || 0; | ||
var bufferedExpiresAt = expiresAt - tokenExpiresAtBufferMs; | ||
return !!(apiToken && bufferedExpiresAt > Date.now()); | ||
} | ||
}, { | ||
key: '_getNewSessionInfo', | ||
value: function _getNewSessionInfo(audienceName) { | ||
var _this = this; | ||
var audience = this._sdk.config.audiences[audienceName]; | ||
if (!(audience && audience.clientId)) { | ||
return Promise.reject(new Error('No valid audience found')); | ||
} | ||
if (!this._tokenPromise) { | ||
this._tokenPromise = axios.post(this._sdk.config.audiences.contxtAuth.host + '/v1/oauth/token', { | ||
audience: audience.clientId, | ||
client_id: this._sdk.config.auth.clientId, | ||
client_secret: this._sdk.config.auth.clientSecret, | ||
grant_type: 'client_credentials' | ||
}).then(function (_ref) { | ||
var data = _ref.data; | ||
return { | ||
apiToken: data.access_token, | ||
expiresAt: Date.now() + data.expires_in * 1000 | ||
}; | ||
}).then(function (sessionInfo) { | ||
_this._saveSession(audienceName, sessionInfo); | ||
_this._tokenPromise = null; | ||
return sessionInfo; | ||
}).catch(function (err) { | ||
if (!(err.response && err.response.status)) { | ||
throw new Error('There was a problem getting a token from the ContxtAuth server. Please check your configuration settings.'); | ||
} | ||
throw err; | ||
}); | ||
} | ||
return this._tokenPromise; | ||
} | ||
}, { | ||
key: '_saveSession', | ||
value: function _saveSession(audienceName, sessionInfo) { | ||
this._sessionInfo = _extends({}, this._sessionInfo, defineProperty({}, audienceName, sessionInfo)); | ||
} | ||
}]); | ||
return MachineAuth; | ||
}(); | ||
var TYPE$1 = 'machineAuth'; | ||
var TYPES = { | ||
AUTH0_WEB_AUTH: TYPE, | ||
MACHINE_AUTH: TYPE$1 | ||
}; | ||
var ContxtSdk = function () { | ||
function ContxtSdk(_ref) { | ||
@@ -1141,5 +1124,8 @@ var _ref$config = _ref.config, | ||
switch (sessionType) { | ||
case 'auth0WebAuth': | ||
case TYPES.AUTH0_WEB_AUTH: | ||
return new Auth0WebAuth(this); | ||
case TYPES.MACHINE_AUTH: | ||
return new MachineAuth(this); | ||
default: | ||
@@ -1149,2 +1135,3 @@ throw new Error('Invalid sessionType provided'); | ||
} | ||
}, { | ||
@@ -1155,2 +1142,3 @@ key: '_createRequest', | ||
} | ||
}, { | ||
@@ -1157,0 +1145,0 @@ key: '_decorate', |
388
lib/index.js
@@ -33,3 +33,4 @@ 'use strict'; | ||
auth: { | ||
authorizationPath: '/callback' | ||
authorizationPath: '/callback', | ||
tokenExpiresAtBufferMs: 5 * 60 * 1000 | ||
} | ||
@@ -66,3 +67,16 @@ }; | ||
var defineProperty = function (obj, key, value) { | ||
if (key in obj) { | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: true, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} else { | ||
obj[key] = value; | ||
} | ||
return obj; | ||
}; | ||
@@ -142,2 +156,3 @@ var _extends = Object.assign || function (target) { | ||
var Config = function () { | ||
function Config(userConfig, externalModules) { | ||
@@ -150,4 +165,4 @@ classCallCheck(this, Config); | ||
externalModules: externalModules, | ||
customModuleConfigs: userConfig.customModuleConfigs, | ||
env: userConfig.env | ||
customModuleConfigs: userConfig.auth.customModuleConfigs, | ||
env: userConfig.auth.env | ||
}); | ||
@@ -172,2 +187,3 @@ | ||
} | ||
}, { | ||
@@ -184,3 +200,2 @@ key: '_getAudiences', | ||
return _extends({}, this._getInternalAudiences({ | ||
@@ -192,2 +207,3 @@ customModuleConfigs: customModuleConfigs, | ||
} | ||
}, { | ||
@@ -211,2 +227,3 @@ key: '_getExternalAudiences', | ||
} | ||
}, { | ||
@@ -239,2 +256,3 @@ key: '_getInternalAudiences', | ||
var Facilities = function () { | ||
function Facilities(sdk, request) { | ||
@@ -253,2 +271,3 @@ classCallCheck(this, Facilities); | ||
} | ||
}, { | ||
@@ -264,2 +283,3 @@ key: "getAll", | ||
var Request = function () { | ||
function Request(sdk, audienceName) { | ||
@@ -271,5 +291,7 @@ var _this = this; | ||
this._insertHeaders = function (config) { | ||
config.headers.common.Authorization = 'Bearer ' + _this._sdk.auth.getCurrentApiToken(_this._audienceName); | ||
return _this._sdk.auth.getCurrentApiToken(_this._audienceName).then(function (apiToken) { | ||
config.headers.common.Authorization = 'Bearer ' + apiToken; | ||
return config; | ||
return config; | ||
}); | ||
}; | ||
@@ -294,2 +316,3 @@ | ||
} | ||
}, { | ||
@@ -305,2 +328,3 @@ key: 'get', | ||
} | ||
}, { | ||
@@ -316,2 +340,3 @@ key: 'head', | ||
} | ||
}, { | ||
@@ -327,2 +352,3 @@ key: 'options', | ||
} | ||
}, { | ||
@@ -338,2 +364,3 @@ key: 'patch', | ||
} | ||
}, { | ||
@@ -349,2 +376,3 @@ key: 'post', | ||
} | ||
}, { | ||
@@ -360,2 +388,3 @@ key: 'put', | ||
} | ||
}, { | ||
@@ -371,2 +400,3 @@ key: 'request', | ||
} | ||
}]); | ||
@@ -378,11 +408,2 @@ return Request; | ||
/** | ||
* Check if we're required to add a port number. | ||
* | ||
* @see https://url.spec.whatwg.org/#default-port | ||
* @param {Number|String} port Port number we need to check | ||
* @param {String} protocol Protocol we need to check against. | ||
* @returns {Boolean} Is it a default port for the given protocol | ||
* @api private | ||
*/ | ||
var requiresPort = function required(port, protocol) { | ||
@@ -418,9 +439,2 @@ protocol = protocol.split(':')[0]; | ||
/** | ||
* Decode a URI encoded string. | ||
* | ||
* @param {String} input The URI encoded string. | ||
* @returns {String} The decoded string. | ||
* @api private | ||
*/ | ||
function decode(input) { | ||
@@ -430,9 +444,2 @@ return decodeURIComponent(input.replace(/\+/g, ' ')); | ||
/** | ||
* Simple query string parser. | ||
* | ||
* @param {String} query The query string that needs to be parsed. | ||
* @returns {Object} | ||
* @api public | ||
*/ | ||
function querystring(query) { | ||
@@ -443,7 +450,2 @@ var parser = /([^=?&]+)=?([^&]*)/g | ||
// | ||
// Little nifty parsing hack, leverage the fact that RegExp.exec increments | ||
// the lastIndex property so we can continue executing this loop until we've | ||
// parsed all results. | ||
// | ||
for (; | ||
@@ -457,10 +459,2 @@ part = parser.exec(query); | ||
/** | ||
* Transform a query string to an object. | ||
* | ||
* @param {Object} obj Object that should be transformed. | ||
* @param {String} prefix Optional prefix. | ||
* @returns {String} | ||
* @api public | ||
*/ | ||
function querystringify(obj, prefix) { | ||
@@ -471,5 +465,2 @@ prefix = prefix || ''; | ||
// | ||
// Optionally prefix with a '?' if needed | ||
// | ||
if ('string' !== typeof prefix) prefix = '?'; | ||
@@ -486,5 +477,2 @@ | ||
// | ||
// Expose the module. | ||
// | ||
var stringify = querystringify; | ||
@@ -501,46 +489,14 @@ var parse = querystring; | ||
/** | ||
* These are the parse rules for the URL parser, it informs the parser | ||
* about: | ||
* | ||
* 0. The char it Needs to parse, if it's a string it should be done using | ||
* indexOf, RegExp using exec and NaN means set as current value. | ||
* 1. The property we should set when parsing this value. | ||
* 2. Indication if it's backwards or forward parsing, when set as number it's | ||
* the value of extra chars that should be split off. | ||
* 3. Inherit from location if non existing in the parser. | ||
* 4. `toLowerCase` the resulting value. | ||
*/ | ||
var rules = [ | ||
['#', 'hash'], // Extract from the back. | ||
['?', 'query'], // Extract from the back. | ||
['/', 'pathname'], // Extract from the back. | ||
['@', 'auth', 1], // Extract from the front. | ||
[NaN, 'host', undefined, 1, 1], // Set left over value. | ||
[/:(\d+)$/, 'port', undefined, 1], // RegExp the back. | ||
[NaN, 'hostname', undefined, 1, 1] // Set left over. | ||
['#', 'hash'], | ||
['?', 'query'], | ||
['/', 'pathname'], | ||
['@', 'auth', 1], | ||
[NaN, 'host', undefined, 1, 1], | ||
[/:(\d+)$/, 'port', undefined, 1], | ||
[NaN, 'hostname', undefined, 1, 1] | ||
]; | ||
/** | ||
* These properties should not be copied or inherited from. This is only needed | ||
* for all non blob URL's as a blob URL does not include a hash, only the | ||
* origin. | ||
* | ||
* @type {Object} | ||
* @private | ||
*/ | ||
var ignore = { hash: 1, query: 1 }; | ||
/** | ||
* The location object differs when your code is loaded through a normal page, | ||
* Worker or through a worker using a blob. And with the blobble begins the | ||
* trouble as the location object will contain the URL of the blob, not the | ||
* location of the page where our code is loaded in. The actual origin is | ||
* encoded in the `pathname` so we can thankfully generate a good "default" | ||
* location from it so we can generate proper relative URL's again. | ||
* | ||
* @param {Object|String} loc Optional default location object. | ||
* @returns {Object} lolcation object. | ||
* @api public | ||
*/ | ||
function lolcation(loc) { | ||
@@ -572,17 +528,2 @@ loc = loc || commonjsGlobal.location || {}; | ||
/** | ||
* @typedef ProtocolExtract | ||
* @type Object | ||
* @property {String} protocol Protocol matched in the URL, in lowercase. | ||
* @property {Boolean} slashes `true` if protocol is followed by "//", else `false`. | ||
* @property {String} rest Rest of the URL that is not part of the protocol. | ||
*/ | ||
/** | ||
* Extract protocol information from a URL with/without double slash ("//"). | ||
* | ||
* @param {String} address URL we want to extract from. | ||
* @return {ProtocolExtract} Extracted information. | ||
* @api private | ||
*/ | ||
function extractProtocol(address) { | ||
@@ -598,10 +539,2 @@ var match = protocolre.exec(address); | ||
/** | ||
* Resolve a relative URL pathname against a base URL pathname. | ||
* | ||
* @param {String} relative Pathname of the relative URL. | ||
* @param {String} base Pathname of the base URL. | ||
* @return {String} Resolved pathname. | ||
* @api private | ||
*/ | ||
function resolve(relative, base) { | ||
@@ -633,13 +566,2 @@ var path = (base || '/').split('/').slice(0, -1).concat(relative.split('/')) | ||
/** | ||
* The actual URL instance. Instead of returning an object we've opted-in to | ||
* create an actual constructor as it's much more memory efficient and | ||
* faster and it pleases my OCD. | ||
* | ||
* @constructor | ||
* @param {String} address URL we want to parse. | ||
* @param {Object|String} location Location defaults for relative paths. | ||
* @param {Boolean|Function} parser Parser for the query string. | ||
* @api public | ||
*/ | ||
function URL(address, location, parser) { | ||
@@ -656,13 +578,2 @@ if (!(this instanceof URL)) { | ||
// | ||
// The following if statements allows this module two have compatibility with | ||
// 2 different API: | ||
// | ||
// 1. Node.js's `url.parse` api which accepts a URL, boolean as arguments | ||
// where the boolean indicates that the query string should also be parsed. | ||
// | ||
// 2. The `URL` interface of the browser which accepts a URL, object as | ||
// arguments. The supplied object will be used as default values / fall-back | ||
// for relative paths. | ||
// | ||
if ('object' !== type && 'string' !== type) { | ||
@@ -677,5 +588,2 @@ parser = location; | ||
// | ||
// Extract protocol information before running the instructions. | ||
// | ||
extracted = extractProtocol(address || ''); | ||
@@ -687,6 +595,2 @@ relative = !extracted.protocol && !extracted.slashes; | ||
// | ||
// When the authority component is absent the URL starts with a path | ||
// component. | ||
// | ||
if (!extracted.slashes) instructions[2] = [/(.*)/, 'pathname']; | ||
@@ -720,19 +624,7 @@ | ||
// | ||
// Hostname, host and protocol should be lowercased so they can be used to | ||
// create a proper `origin`. | ||
// | ||
if (instruction[4]) url[key] = url[key].toLowerCase(); | ||
} | ||
// | ||
// Also parse the supplied query string in to an object. If we're supplied | ||
// with a custom parser as function use that instead of the default build-in | ||
// parser. | ||
// | ||
if (parser) url.query = parser(url.query); | ||
// | ||
// If the URL is relative, resolve the pathname against the base URL. | ||
// | ||
if ( | ||
@@ -747,7 +639,2 @@ relative | ||
// | ||
// We should not add port numbers if they are already the default port number | ||
// for a given protocol. As the host also contains the port number we're going | ||
// override it with the hostname which contains no port number. | ||
// | ||
if (!requiresPort(url.port, url.protocol)) { | ||
@@ -758,5 +645,2 @@ url.host = url.hostname; | ||
// | ||
// Parse down the `auth` for the username and password. | ||
// | ||
url.username = url.password = ''; | ||
@@ -773,21 +657,5 @@ if (url.auth) { | ||
// | ||
// The href is just the compiled result. | ||
// | ||
url.href = url.toString(); | ||
} | ||
/** | ||
* This is convenience method for changing properties in the URL instance to | ||
* insure that they all propagate correctly. | ||
* | ||
* @param {String} part Property we need to adjust. | ||
* @param {Mixed} value The newly assigned value. | ||
* @param {Boolean|Function} fn When setting the query, it will be the function | ||
* used to parse the query. | ||
* When setting the protocol, double slash will be | ||
* removed from the final url if it is true. | ||
* @returns {URL} | ||
* @api public | ||
*/ | ||
function set$1(part, value, fn) { | ||
@@ -872,9 +740,2 @@ var url = this; | ||
/** | ||
* Transform the properties back in to a valid and full URL string. | ||
* | ||
* @param {Function} stringify Optional query stringify function. | ||
* @returns {String} | ||
* @api public | ||
*/ | ||
function toString(stringify) { | ||
@@ -909,6 +770,2 @@ if (!stringify || 'function' !== typeof stringify) stringify = querystringify_1.stringify; | ||
// | ||
// Expose the URL parser and some additional properties that might be useful for | ||
// others or testing. | ||
// | ||
URL.extractProtocol = extractProtocol; | ||
@@ -921,2 +778,3 @@ URL.location = lolcation; | ||
var Auth0WebAuth = function () { | ||
function Auth0WebAuth(sdk) { | ||
@@ -952,2 +810,3 @@ classCallCheck(this, Auth0WebAuth); | ||
} | ||
}, { | ||
@@ -958,2 +817,3 @@ key: 'getCurrentApiToken', | ||
} | ||
}, { | ||
@@ -964,12 +824,15 @@ key: 'getProfile', | ||
return new Promise(function (resolve, reject) { | ||
_this._auth0.client.userInfo(_this.getCurrentAccessToken(), function (err, profile) { | ||
if (err) { | ||
reject(err); | ||
} | ||
return this.getCurrentAccessToken().then(function (accessToken) { | ||
return new Promise(function (resolve, reject) { | ||
_this._auth0.client.userInfo(accessToken, function (err, profile) { | ||
if (err) { | ||
reject(err); | ||
} | ||
resolve(profile); | ||
resolve(profile); | ||
}); | ||
}); | ||
}); | ||
} | ||
}, { | ||
@@ -1011,2 +874,3 @@ key: 'handleAuthentication', | ||
} | ||
}, { | ||
@@ -1019,2 +883,3 @@ key: 'isAuthenticated', | ||
} | ||
}, { | ||
@@ -1025,2 +890,3 @@ key: 'logIn', | ||
} | ||
}, { | ||
@@ -1037,2 +903,3 @@ key: 'logOut', | ||
} | ||
}, { | ||
@@ -1043,2 +910,3 @@ key: '_defaultOnRedirect', | ||
} | ||
}, { | ||
@@ -1063,13 +931,19 @@ key: '_getApiToken', | ||
} | ||
}, { | ||
key: '_getCurrentTokenByType', | ||
value: function _getCurrentTokenByType(type) { | ||
var propertyName = type + 'Token'; | ||
var _this4 = this; | ||
if (!(this._sessionInfo && this._sessionInfo[propertyName])) { | ||
throw new Error('No ' + type + ' token found'); | ||
} | ||
return new Promise(function (resolve, reject) { | ||
var propertyName = type + 'Token'; | ||
return this._sessionInfo[propertyName]; | ||
if (!(_this4._sessionInfo && _this4._sessionInfo[propertyName])) { | ||
reject(new Error('No ' + type + ' token found')); | ||
} | ||
resolve(_this4._sessionInfo[propertyName]); | ||
}); | ||
} | ||
}, { | ||
@@ -1083,2 +957,3 @@ key: '_getRedirectPathname', | ||
} | ||
}, { | ||
@@ -1093,9 +968,10 @@ key: '_loadSession', | ||
} | ||
}, { | ||
key: '_parseWebAuthHash', | ||
value: function _parseWebAuthHash() { | ||
var _this4 = this; | ||
var _this5 = this; | ||
return new Promise(function (resolve, reject) { | ||
_this4._auth0.parseHash(function (err, hashResponse) { | ||
_this5._auth0.parseHash(function (err, hashResponse) { | ||
if (err || !hashResponse) { | ||
@@ -1109,2 +985,3 @@ return reject(err || new Error('No valid tokens returned from auth0')); | ||
} | ||
}, { | ||
@@ -1123,3 +1000,109 @@ key: '_saveSession', | ||
var TYPE = 'auth0WebAuth'; | ||
var MachineAuth = function () { | ||
function MachineAuth(sdk) { | ||
classCallCheck(this, MachineAuth); | ||
this._sdk = sdk; | ||
this._sessionInfo = {}; | ||
if (!this._sdk.config.auth.clientId) { | ||
throw new Error('clientId is required for the MachineAuth config'); | ||
} | ||
if (!this._sdk.config.auth.clientSecret) { | ||
throw new Error('clientSecret is required for the MachineAuth config'); | ||
} | ||
} | ||
createClass(MachineAuth, [{ | ||
key: 'getCurrentApiToken', | ||
value: function getCurrentApiToken(audienceName) { | ||
if (this.isAuthenticated(audienceName)) { | ||
return Promise.resolve(this._sessionInfo[audienceName].apiToken); | ||
} | ||
return this._getNewSessionInfo(audienceName).then(function (sessionInfo) { | ||
return sessionInfo.apiToken; | ||
}); | ||
} | ||
}, { | ||
key: 'isAuthenticated', | ||
value: function isAuthenticated(audienceName) { | ||
if (!(this._sessionInfo && this._sessionInfo[audienceName])) { | ||
return false; | ||
} | ||
var _sessionInfo$audience = this._sessionInfo[audienceName], | ||
apiToken = _sessionInfo$audience.apiToken, | ||
expiresAt = _sessionInfo$audience.expiresAt; | ||
var tokenExpiresAtBufferMs = this._sdk.config.auth.tokenExpiresAtBufferMs || 0; | ||
var bufferedExpiresAt = expiresAt - tokenExpiresAtBufferMs; | ||
return !!(apiToken && bufferedExpiresAt > Date.now()); | ||
} | ||
}, { | ||
key: '_getNewSessionInfo', | ||
value: function _getNewSessionInfo(audienceName) { | ||
var _this = this; | ||
var audience = this._sdk.config.audiences[audienceName]; | ||
if (!(audience && audience.clientId)) { | ||
return Promise.reject(new Error('No valid audience found')); | ||
} | ||
if (!this._tokenPromise) { | ||
this._tokenPromise = axios.post(this._sdk.config.audiences.contxtAuth.host + '/v1/oauth/token', { | ||
audience: audience.clientId, | ||
client_id: this._sdk.config.auth.clientId, | ||
client_secret: this._sdk.config.auth.clientSecret, | ||
grant_type: 'client_credentials' | ||
}).then(function (_ref) { | ||
var data = _ref.data; | ||
return { | ||
apiToken: data.access_token, | ||
expiresAt: Date.now() + data.expires_in * 1000 | ||
}; | ||
}).then(function (sessionInfo) { | ||
_this._saveSession(audienceName, sessionInfo); | ||
_this._tokenPromise = null; | ||
return sessionInfo; | ||
}).catch(function (err) { | ||
if (!(err.response && err.response.status)) { | ||
throw new Error('There was a problem getting a token from the ContxtAuth server. Please check your configuration settings.'); | ||
} | ||
throw err; | ||
}); | ||
} | ||
return this._tokenPromise; | ||
} | ||
}, { | ||
key: '_saveSession', | ||
value: function _saveSession(audienceName, sessionInfo) { | ||
this._sessionInfo = _extends({}, this._sessionInfo, defineProperty({}, audienceName, sessionInfo)); | ||
} | ||
}]); | ||
return MachineAuth; | ||
}(); | ||
var TYPE$1 = 'machineAuth'; | ||
var TYPES = { | ||
AUTH0_WEB_AUTH: TYPE, | ||
MACHINE_AUTH: TYPE$1 | ||
}; | ||
var ContxtSdk = function () { | ||
function ContxtSdk(_ref) { | ||
@@ -1145,5 +1128,8 @@ var _ref$config = _ref.config, | ||
switch (sessionType) { | ||
case 'auth0WebAuth': | ||
case TYPES.AUTH0_WEB_AUTH: | ||
return new Auth0WebAuth(this); | ||
case TYPES.MACHINE_AUTH: | ||
return new MachineAuth(this); | ||
default: | ||
@@ -1153,2 +1139,3 @@ throw new Error('Invalid sessionType provided'); | ||
} | ||
}, { | ||
@@ -1159,2 +1146,3 @@ key: '_createRequest', | ||
} | ||
}, { | ||
@@ -1161,0 +1149,0 @@ key: '_decorate', |
{ | ||
"name": "@ndustrial/contxt-sdk", | ||
"version": "0.0.2", | ||
"version": "0.0.4", | ||
"description": "", | ||
@@ -8,3 +8,5 @@ "main": "lib/index.js", | ||
"scripts": { | ||
"build": "rollup -c", | ||
"build": "npm run build:js && npm run build:docs", | ||
"build:docs": "node ./support/docs", | ||
"build:js": "rollup -c", | ||
"coverage": "nyc npm run test:js", | ||
@@ -18,3 +20,3 @@ "lint": "eslint --format 'node_modules/eslint-friendly-formatter' '+(src|support)/**/*.js'", | ||
"test:js:inspect": "npm run test:js -- --inspect-brk", | ||
"watch": "npm run build -- --watch" | ||
"watch": "npm run build:js -- --watch" | ||
}, | ||
@@ -27,3 +29,3 @@ "author": "ndustrial.io <dev@ndustrial.io> (ndustrial.io)", | ||
"devDependencies": { | ||
"auth0-js": "^9.0.0", | ||
"auth0-js": "^9.3.0", | ||
"axios": "~0.17.0", | ||
@@ -43,2 +45,5 @@ "babel-eslint": "^8.2.1", | ||
"faker": "^4.1.0", | ||
"fast-glob": "^2.1.0", | ||
"jsdoc-to-markdown": "^4.0.1", | ||
"lodash.sortby": "^4.7.0", | ||
"lodash.times": "^4.3.2", | ||
@@ -49,2 +54,3 @@ "mocha": "^4.1.0", | ||
"rollup-plugin-babel": "^3.0.3", | ||
"rollup-plugin-cleanup": "^2.0.0", | ||
"rollup-plugin-commonjs": "^8.2.6", | ||
@@ -58,5 +64,5 @@ "rollup-plugin-node-resolve": "^3.0.2", | ||
"peerDependencies": { | ||
"auth0-js": "^9.0.0", | ||
"auth0-js": "^9.3.0", | ||
"axios": "~0.17.0" | ||
} | ||
} |
# contxt-sdk | ||
## Building the package | ||
## Installation | ||
The `contxt-sdk` can be installed with NPM: | ||
``` | ||
npm install --save @ndustrial/contxt-sdk | ||
``` | ||
There are two peer dependencies for `contxt-sdk`, `auth0-js` and `axios`. If you don't already have a compatible version installed, run: | ||
``` | ||
npm install --save auth0-js@^9.0.0 axios@~0.17.0 | ||
``` | ||
## Getting Started | ||
Once installed, the minimum configuration you need to get going is to include the `clientId` of your application (from Auth0) and a string with the type of authentication you want to use (`auth0WebAuth` or `machineAuth`). | ||
``` | ||
import ContxtSdk from '@ndustrial/contxt-sdk'; | ||
const contxtSdk = new ContxtSdk({ | ||
config: { | ||
auth: { | ||
clientId: 'example clientId from auth0' | ||
} | ||
}, | ||
sessionType: 'auth0WebAuth' | ||
}); | ||
contxtSdk.facilities.getAll().then((facilities) => { | ||
console.log(`all of my facilities: ${JSON.stringify(facilities)}`); | ||
}); | ||
``` | ||
Information about using the auth0WebAuth and machineAuth modules is available in the API docs [here (auth0WebAuth)](https://github.com/ndustrialio/contxt-sdk-js/blob/master/docs/Auth0WebAuth.md) and [here (machineAuth)](https://github.com/ndustrialio/contxt-sdk-js/blob/master/docs/MachineAuth.md). Additional information about configuration options can also be found in the [API docs](https://github.com/ndustrialio/contxt-sdk-js/blob/master/docs/ContxtSdk.md). | ||
## Adding in external modules | ||
At times when building your application, there might be a Contxt API that you need to reach that is not currently included in the `contxt-sdk` package. To help out with this, we've created a way to include an external module into the SDK when creating an SDK instance that allows the external module to act as a first class extension of the SDK's API. | ||
To do this, just include information about the module when creating your `contxt-sdk` instance: | ||
``` | ||
import ContxtSdk from 'contxt-sdk'; | ||
import NewModule from './NewModule'; | ||
const contxtSdk = new ContxtSdk({ | ||
config: { | ||
auth: { | ||
clientId: 'example clientId from auth0' | ||
} | ||
}, | ||
externalModules: { | ||
newModule: { | ||
clientId: 'The Auth0 Id of the API you are communicated with', | ||
host: 'http://newModule.example.com', | ||
module: NewModule | ||
} | ||
}, | ||
sessionType: 'auth0WebAuth' | ||
}); | ||
contxtSdk.newModule.doWork(); | ||
``` | ||
When we decorate your external module into your SDK instance, it is treated just like one of the native, internal modules and is provided with the SDK instance (so you can use other parts of the SDK from your new module) and its own request module, which will handle API tokens if you are working with a Contxt API. | ||
``` | ||
class NewModule { | ||
constructor(sdk, request) { | ||
this._baseUrl = `${sdk.config.audiences.newModule.host}/v1`; | ||
this._request = request; | ||
this._sdk = sdk; | ||
} | ||
doWork() { | ||
return this._request.patch(`${this._baseUrl}/data`, { work: 'finished' }); | ||
} | ||
} | ||
export default NewModule; | ||
``` | ||
## Development | ||
### Building the package | ||
[rollup.js](https://rollupjs.org/guide/en) is used to build the source code into CommonJS and ES6 modules that can be used for distribution. These modules are both built by running one command: `npm run build`. If you'd like to continuously create builds as files are changed (i.e. if you are developing new features and have set things up correctly with `npm link` to serve the newly updated files to your app), you can run `npm run watch`. | ||
## Testing & Code Quality | ||
### Testing & Code Quality | ||
@@ -9,0 +95,0 @@ Some important NPM tasks for running the test suite: |
import babel from 'rollup-plugin-babel'; | ||
import cleanup from 'rollup-plugin-cleanup'; | ||
import commonjs from 'rollup-plugin-commonjs'; | ||
@@ -31,4 +32,7 @@ import nodeResolve from 'rollup-plugin-node-resolve'; | ||
plugins: ['external-helpers'] | ||
}), | ||
cleanup({ | ||
maxEmptyLines: 1 | ||
}) | ||
] | ||
}; |
export default { | ||
auth: { | ||
authorizationPath: '/callback' | ||
authorizationPath: '/callback', | ||
tokenExpiresAtBufferMs: 5 * 60 * 1000 // 5 minutes | ||
} | ||
}; |
import defaultAudiences from './audiences'; | ||
import defaultConfigs from './defaults'; | ||
/** | ||
* A single audience used for authenticating and communicating with an individual API. | ||
* | ||
* @typedef {Object} Audience | ||
* @param {string} config.clientId Client Id provided by Auth0 for the environment you are | ||
* trying to communicate with | ||
* @param {string} config.host Hostname for the API that corresponds with the clientId provided | ||
*/ | ||
/** | ||
* A custom audience that will override the configuration of an individual module. Consists of | ||
* either a reference to an environment that already exists or a clientId and host for a | ||
* custom environment. | ||
* | ||
* @typedef {Object} CustomAudience | ||
* @param {string} [config.clientId] Client Id provided by Auth0 for the environment you are | ||
* trying to communicate with | ||
* @param {string} [config.env] The SDK provided environment name you are trying to reach | ||
* @param {string} [config.host] Hostname for the API that corresponds with the clientId provided | ||
*/ | ||
/** | ||
* An object of audiences that corresponds to all the different environments available for a | ||
* single module. | ||
* | ||
* @typedef {Object.<string, Audience>} Environments | ||
*/ | ||
/** | ||
* An external module to be integrated into the SDK as a first class citizen. Includes information | ||
* for authenticating and communicating with an individual API and the external module itself. | ||
* | ||
* @typedef {Object} ExternalModule | ||
* @param {string} config.clientId Client Id provided by Auth0 for the environment you are | ||
* trying to communicate with | ||
* @param {string} config.host Hostname for the API that corresponds with the clientId provided | ||
* @param {function} config.module The module that will be decorated into the SDK | ||
*/ | ||
/** | ||
* User provided configuration options | ||
* | ||
* @typedef {Object} UserConfig | ||
* @property {Object} auth User assigned configurations specific for their authentication methods | ||
* @property {string} [auth.authorizationPath] Path Auth0WebAuth process should redirect to after a | ||
* successful sign in attempt | ||
* @property {string} auth.clientId Client Id provided by Auth0 for this application | ||
* @property {string} [auth.clientSecret] Client secret provided by Auth0 for this application. This | ||
* is optional for the auth0WebAuth SessionType, but required for the machineAuth SessionType | ||
* @property {Object.<string, CustomAudience>} [auth.customModuleConfigs] Custom environment setups | ||
* for individual modules. Requires clientId/host or env | ||
* @property {string} [auth.env = 'production'] The environment that every module should use for | ||
* their clientId and host | ||
* @property {function} [auth.onRedirect = (pathname) => { window.location = pathname; }] A redirect | ||
* method used for navigating through Auth0 callbacks in Web applications | ||
* @property {number} [auth.tokenExpiresAtBufferMs = 300000] The time (in milliseconds) before a | ||
* token truly expires that we consider it expired (i.e. the token's expiresAt - this = calculated | ||
* expiresAt). Defaults to 5 minutes. | ||
*/ | ||
/** | ||
* Module that merges user assigned configurations with default configurations. | ||
* | ||
* @typicalname contxtSdk.config | ||
*/ | ||
class Config { | ||
/** | ||
* @param {UserConfig} userConfig The user provided configuration options | ||
* @param {Object.<string, ExternalModule>} [externalModules] User provided external modules that should be treated as | ||
* first class citizens | ||
*/ | ||
constructor(userConfig, externalModules) { | ||
@@ -10,4 +80,4 @@ Object.assign(this, userConfig); | ||
externalModules, | ||
customModuleConfigs: userConfig.customModuleConfigs, | ||
env: userConfig.env | ||
customModuleConfigs: userConfig.auth.customModuleConfigs, | ||
env: userConfig.auth.env | ||
}); | ||
@@ -21,2 +91,13 @@ | ||
/** | ||
* Parses a custom module configuration for a valid environment/audience. Requires either a | ||
* clientId and host, or an environment that matches a default audience/environment. | ||
* | ||
* @param {CustomAudience} config A custom audience configuration to parse | ||
* | ||
* @returns {Audience} | ||
* @throws {Error} | ||
* | ||
* @private | ||
*/ | ||
_getAudienceFromCustomConfig(config, audiences) { | ||
@@ -35,2 +116,17 @@ if (config.clientId && config.host) { | ||
/** | ||
* Reconciles the main environment with custom environments and external modules. | ||
* | ||
* @param {Object} options | ||
* @param {Object.<string, CustomAudience>} [options.customModuleConfigs = {}] Any custom | ||
* configurations for internal modules | ||
* @param {string} [options.env = 'production'] The base environment for any | ||
* non-overridden modules | ||
* @param {Object.<string, ExternalModule>} [options.externalModules = {}] An object of external | ||
* modules from which to build a set of audiences | ||
* | ||
* @returns {Object.<string, Audience>} | ||
* | ||
* @private | ||
*/ | ||
_getAudiences(options = {}) { | ||
@@ -53,2 +149,14 @@ const { | ||
/** | ||
* Builds up the audiences for external modules. | ||
* | ||
* @param {Object} | ||
* @param {Object.<string, ExternalModule>} externalModules An object of external modules from | ||
* which to build a set of audiences | ||
* | ||
* @returns {Object.<string, Audience>} | ||
* @throws {Error} | ||
* | ||
* @private | ||
*/ | ||
_getExternalAudiences({ externalModules }) { | ||
@@ -69,2 +177,16 @@ return Object.keys(externalModules).reduce((memo, key) => { | ||
/** | ||
* Reconciles the main environment with custom environments to build up audiences for | ||
* internal modules. | ||
* | ||
* @param {Object.<string, Environments>} audiences All possible audiences/environments for | ||
* internal modules | ||
* @param {Object.<string, CustomAudience>} customModuleConfigs Any custom configurations for | ||
* internal modules | ||
* @param {string} env The base environment for any non-overridden modules | ||
* | ||
* @returns {Object.<string, Audience>} | ||
* | ||
* @private | ||
*/ | ||
_getInternalAudiences({ audiences, customModuleConfigs, env }) { | ||
@@ -71,0 +193,0 @@ return Object.keys(audiences).reduce((memo, key) => { |
@@ -25,5 +25,3 @@ import times from 'lodash.times'; | ||
baseAuthConfigs = { | ||
clientId: faker.internet.password() | ||
}; | ||
baseConfigs = { | ||
clientId: faker.internet.password(), | ||
customModuleConfigs: { | ||
@@ -34,2 +32,5 @@ [faker.hacker.adjective()]: fixture.build('audience') | ||
}; | ||
baseConfigs = { | ||
[faker.lorem.word()]: faker.helpers.createTransaction() | ||
}; | ||
expectedAudiences = fixture.build('defaultAudiences'); | ||
@@ -54,4 +55,4 @@ expectedExternalModules = { | ||
expect(getAudiences).to.be.calledWith({ | ||
customModuleConfigs: baseConfigs.customModuleConfigs, | ||
env: baseConfigs.env, | ||
customModuleConfigs: baseAuthConfigs.customModuleConfigs, | ||
env: baseAuthConfigs.env, | ||
externalModules: expectedExternalModules | ||
@@ -58,0 +59,0 @@ }); |
@@ -0,2 +1,38 @@ | ||
/** | ||
* @typedef {Object} Facility | ||
* @property {string} address1 | ||
* @property {string} address2 | ||
* @property {string} city | ||
* @property {string} created_at ISO 8601 Extended Format date/time string | ||
* @property {number} id | ||
* @property {Object} Info | ||
* @property {string} name | ||
* @property {Object} Organization | ||
* @property {string} Organization.id UUID formatted id | ||
* @property {string} Organization.name | ||
* @property {string} Organization.created_at ISO 8601 Extended Format date/time string | ||
* @property {string} Organization.updated_at ISO 8601 Extended Format date/time string | ||
* @property {string} state | ||
* @property {Object[]} tags | ||
* @property {number} tags[].id | ||
* @property {number} tags[].facility_id | ||
* @property {string} tags[].name | ||
* @property {string} tags[].created_at ISO 8601 Extended Format date/time string | ||
* @property {string} tags[].updated_at ISO 8601 Extended Format date/time string | ||
* @property {string} timezone An IANA Time Zone Database string, i.e. America/Los_Angeles | ||
* @property {number} weather_location_id | ||
* @property {string} zip US Zip Code | ||
*/ | ||
/** | ||
* Module that provides access to, and the manipulation | ||
* of, information about different facilities | ||
* | ||
* @typicalname contxtSdk.facilities | ||
*/ | ||
class Facilities { | ||
/** | ||
* @param {Object} sdk An instance of the SDK so the module can communicate with other modules | ||
* @param {Object} request An instance of the request module tied to this module's audience. | ||
*/ | ||
constructor(sdk, request) { | ||
@@ -8,2 +44,19 @@ this._baseUrl = `${sdk.config.audiences.facilities.host}/v1`; | ||
/** | ||
* Gets information about a facility | ||
* | ||
* API Endpoint: '/facilities/:facilityId' | ||
* Method: GET | ||
* | ||
* @param {number|string} facilityId The id of the facility | ||
* | ||
* @returns {Promise} | ||
* @fulfill {Facility} Information about a facility | ||
* @reject {Error} | ||
* | ||
* @example | ||
* contxtSdk.facilities.get(25) | ||
* .then((facility) => console.log(facility)); | ||
* .catch((err) => console.log(err)); | ||
*/ | ||
get(facilityId) { | ||
@@ -13,2 +66,17 @@ return this._request.get(`${this._baseUrl}/facilities/${facilityId}`); | ||
/** | ||
* Gets a list of all facilities | ||
* | ||
* API Endpoint: '/facilities' | ||
* Method: GET | ||
* | ||
* @returns {Promise} | ||
* @fulfill {Facility[]} Information about all facilities | ||
* @reject {Error} | ||
* | ||
* @example | ||
* contxtSdk.facilities.getAll() | ||
* .then((facilities) => console.log(facilities)); | ||
* .catch((err) => console.log(err)); | ||
*/ | ||
getAll() { | ||
@@ -15,0 +83,0 @@ return this._request.get(`${this._baseUrl}/facilities`); |
@@ -6,3 +6,62 @@ import Config from './config'; | ||
/** | ||
* An adapter that allows the SDK to authenticate with different services and manage various tokens. | ||
* Can authenticate with a service like Auth0 and then with Contxt or can communicate directly | ||
* with Contxt. The adapter must implement required methods, but most methods are optional. Some of | ||
* the optional methods are documented below. | ||
* | ||
* @typedef {Object} SessionType | ||
* @property {function} [getCurrentAccessToken] Provides a current access token from Auth0 that is | ||
* used for profile information and can be used to get API token for Contxt itself | ||
* @property {function} getCurrentApiToken Provides a current API token that is used across | ||
* different Contxt services | ||
* @property {function} [getProfile] Provides profile information about the current user | ||
* @property {function} [handleAuthentication] Is called by front-end code in the Auth0 reference | ||
* implementation to handle getting the access token from Auth0 | ||
* @property {function} isAuthenticated Tells caller if the current user is authenticated. | ||
* @property {function} [logIn] Is used by front-end code in the Auth0 reference implementation to | ||
* start the sign in process | ||
* @property {function} [logOut] Is used by the front-end code in the Auth0 reference implementation | ||
* to sign the user out | ||
*/ | ||
/** | ||
* ContxtSdk constructor | ||
* | ||
* @example | ||
* import ContxtSdk from '@ndustrial/contxt-sdk'; | ||
* import ExternalModule1 from './ExternalModule1'; | ||
* import history from '../services/history'; | ||
* | ||
* const contxtSdk = new ContxtSdk({ | ||
* config: { | ||
* auth: { | ||
* clientId: 'Auth0 client id of the application being built', | ||
* customModuleConfigs: { | ||
* facilities: { | ||
* env: 'production' | ||
* } | ||
* }, | ||
* env: 'staging', | ||
* onRedirect: (pathname) => history.push(pathname) | ||
* } | ||
* }, | ||
* externalModules: { | ||
* externalModule1: { | ||
* clientId: 'Auth0 client id of the external module', | ||
* host: 'https://www.example.com/externalModule1', | ||
* module: ExternalModule1 | ||
* } | ||
* }, | ||
* sessionType: 'auth0WebAuth' | ||
* }); | ||
*/ | ||
class ContxtSdk { | ||
/** | ||
* @param {UserConfig} config The user provided configuration options | ||
* @param {Object.<string, ExternalModule>} [externalModules] User provided external modules that | ||
* should be treated as first class citizens | ||
* @param {string} sessionType The type of auth session you wish to use (e.g. auth0WebAuth | ||
* or machine) | ||
*/ | ||
constructor({ config = {}, externalModules = {}, sessionType }) { | ||
@@ -17,7 +76,20 @@ this.config = new Config(config, externalModules); | ||
/** | ||
* Returns a new instance of the session type requested | ||
* | ||
* @param {string} sessionType | ||
* | ||
* @returns {SessionType} sessionType | ||
* @throws {Error} | ||
* | ||
* @private | ||
*/ | ||
_createAuthSession(sessionType) { | ||
switch (sessionType) { | ||
case 'auth0WebAuth': | ||
case sessionTypes.TYPES.AUTH0_WEB_AUTH: | ||
return new sessionTypes.Auth0WebAuth(this); | ||
case sessionTypes.TYPES.MACHINE_AUTH: | ||
return new sessionTypes.MachineAuth(this); | ||
default: | ||
@@ -28,2 +100,12 @@ throw new Error('Invalid sessionType provided'); | ||
/** | ||
* Returns an instance of the Request module that is tied to the requested audience | ||
* | ||
* @param {string} audienceName The audience name of the service you are trying to reach | ||
* (e.g. facilities or feeds) | ||
* | ||
* @returns {Object} Request module | ||
* | ||
* @private | ||
*/ | ||
_createRequest(audienceName) { | ||
@@ -33,2 +115,10 @@ return new Request(this, audienceName); | ||
/** | ||
* Decorates custom modules onto the SDK instance so they behave as first-class citizens. | ||
* | ||
* @param {Object} modules | ||
* @param {function} modules.module | ||
* | ||
* @private | ||
*/ | ||
_decorate(modules) { | ||
@@ -35,0 +125,0 @@ Object.keys(modules).forEach((moduleName) => { |
@@ -14,3 +14,5 @@ import times from 'lodash.times'; | ||
baseConfig = {}; | ||
baseConfig = { | ||
auth: {} | ||
}; | ||
}); | ||
@@ -49,4 +51,4 @@ | ||
contxtSdk = new ContxtSdk({ | ||
config: baseConfig, | ||
externalModules: expectedExternalModules, | ||
config: baseConfig, | ||
sessionType: expectedAuthSessionType | ||
@@ -80,3 +82,4 @@ }); | ||
[ | ||
{ sessionType: 'auth0WebAuth', moduleName: 'Auth0WebAuth' } | ||
{ sessionType: 'auth0WebAuth', moduleName: 'Auth0WebAuth' }, | ||
{ sessionType: 'machineAuth', moduleName: 'MachineAuth' } | ||
].forEach(function(authSessionConfig) { | ||
@@ -83,0 +86,0 @@ it(`returns a new ${authSessionConfig.sessionType} session`, function() { |
import axios from 'axios'; | ||
class Request { | ||
/** | ||
* @param {Object} sdk An instance of the SDK so the module can communicate with other modules | ||
* @param {string} audienceName The audience name for this instance. Used when grabbing a | ||
* Bearer token | ||
*/ | ||
constructor(sdk, audienceName) { | ||
@@ -12,2 +17,9 @@ this._audienceName = audienceName; | ||
/** | ||
* Makes a DELETE request | ||
* See more at {@link https://github.com/axios/axios axios} | ||
* | ||
* @returns {Promise} | ||
* @fulfill {string|number|object|array} data | ||
*/ | ||
delete(...args) { | ||
@@ -18,2 +30,9 @@ return this._axios.delete(...args) | ||
/** | ||
* Makes a GET request | ||
* See more at {@link https://github.com/axios/axios axios} | ||
* | ||
* @returns {Promise} | ||
* @fulfill {string|number|object|array} data | ||
*/ | ||
get(...args) { | ||
@@ -24,2 +43,9 @@ return this._axios.get(...args) | ||
/** | ||
* Makes a HEAD request | ||
* See more at {@link https://github.com/axios/axios axios} | ||
* | ||
* @returns {Promise} | ||
* @fulfill {string|number|object|array} data | ||
*/ | ||
head(...args) { | ||
@@ -30,2 +56,9 @@ return this._axios.head(...args) | ||
/** | ||
* Makes an OPTIONS request | ||
* See more at {@link https://github.com/axios/axios axios} | ||
* | ||
* @returns {Promise} | ||
* @fulfill {string|number|object|array} data | ||
*/ | ||
options(...args) { | ||
@@ -36,2 +69,9 @@ return this._axios.options(...args) | ||
/** | ||
* Makes a PATCH request | ||
* See more at {@link https://github.com/axios/axios axios} | ||
* | ||
* @returns {Promise} | ||
* @fulfill {string|number|object|array} data | ||
*/ | ||
patch(...args) { | ||
@@ -42,2 +82,9 @@ return this._axios.patch(...args) | ||
/** | ||
* Makes a POST request | ||
* See more at {@link https://github.com/axios/axios axios} | ||
* | ||
* @returns {Promise} | ||
* @fulfill {string|number|object|array} data | ||
*/ | ||
post(...args) { | ||
@@ -48,2 +95,9 @@ return this._axios.post(...args) | ||
/** | ||
* Makes a PUT request | ||
* See more at {@link https://github.com/axios/axios axios} | ||
* | ||
* @returns {Promise} | ||
* @fulfill {string|number|object|array} data | ||
*/ | ||
put(...args) { | ||
@@ -54,2 +108,9 @@ return this._axios.put(...args) | ||
/** | ||
* Makes a request | ||
* See more at {@link https://github.com/axios/axios axios} | ||
* | ||
* @returns {Promise} | ||
* @fulfill {string|number|object|array} data | ||
*/ | ||
request(...args) { | ||
@@ -60,6 +121,21 @@ return this._axios.request(...args) | ||
/** | ||
* Decorates custom modules onto the SDK instance so they behave as first-class citizens. | ||
* | ||
* @param {Object} config | ||
* @param {Object} config.headers | ||
* @param {Object} config.headers.common | ||
* | ||
* @returns {Promise} | ||
* @fulfill {Object} axios.js config | ||
* | ||
* @private | ||
*/ | ||
_insertHeaders = (config) => { | ||
config.headers.common.Authorization = `Bearer ${this._sdk.auth.getCurrentApiToken(this._audienceName)}`; | ||
return this._sdk.auth.getCurrentApiToken(this._audienceName) | ||
.then((apiToken) => { | ||
config.headers.common.Authorization = `Bearer ${apiToken}`; | ||
return config; | ||
return config; | ||
}); | ||
} | ||
@@ -66,0 +142,0 @@ } |
@@ -102,6 +102,6 @@ import axios from 'axios'; | ||
describe('_insertHeaders', function() { | ||
let config; | ||
let expectedAudienceName; | ||
let expectedToken; | ||
let initialConfig; | ||
let promise; | ||
let sdk; | ||
@@ -120,3 +120,3 @@ | ||
auth: { | ||
getCurrentApiToken: this.sandbox.stub().returns(expectedToken) | ||
getCurrentApiToken: this.sandbox.stub().resolves(expectedToken) | ||
} | ||
@@ -126,3 +126,3 @@ }; | ||
const request = new Request(sdk, expectedAudienceName); | ||
config = request._insertHeaders(initialConfig); | ||
promise = request._insertHeaders(initialConfig); | ||
}); | ||
@@ -134,6 +134,13 @@ | ||
it('appends an Authorization header', function() { | ||
expect(config.headers.common.Authorization).to.equal(`Bearer ${expectedToken}`); | ||
it('returns a resolved promise', function() { | ||
return expect(promise).to.fulfilled; | ||
}); | ||
it('resolves a config with an Authorization header appended', function() { | ||
return promise.then((config) => { | ||
expect(config).to.equal(initialConfig); | ||
expect(config.headers.common.Authorization).to.equal(`Bearer ${expectedToken}`); | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -5,3 +5,60 @@ import auth0 from 'auth0-js'; | ||
/** | ||
* @typedef {Object} UserProfile | ||
* @property {string} name | ||
* @property {string} nickname | ||
* @property {string} picture URL to an avatar | ||
* @property {string} sub The Subject Claim of the user's JWT | ||
* @property {string} updated_at ISO 8601 Extended Format date/time string | ||
*/ | ||
/** | ||
* @typedef {Object} Auth0WebAuthSessionInfo | ||
* @property {string} accessToken | ||
* @property {string} apiToken | ||
* @property {number} expiresAt | ||
*/ | ||
/** | ||
* A SessionType that allows the user to initially authenticate with Auth0 and then gain a valid JWT | ||
* from the Contxt Auth service. This would only be used in web applications. You will need to | ||
* integrate this module's `logIn`, `logOut`, and `handleAuthentication` methods with your UI | ||
* elements. `logIn` would be tied to a UI element to log the user in. `logOut` would be tied to a | ||
* UI element to log the user out. `handleAuthentication` would be tied with your application's | ||
* router and would be called when visting the route defined by `config.authorizationPath` (the | ||
* default is `/callback`). | ||
* | ||
* @type SessionType | ||
* | ||
* @typicalname contxtSdk.auth | ||
* | ||
* @example | ||
* import ContxtSdk from '@ndustrial/contxt-sdk'; | ||
* import history from '../services/history'; | ||
* | ||
* const contxtSdk = new ContxtSDK({ | ||
* config: { | ||
* auth: { | ||
* clientId: '<client id>', | ||
* onRedirect: (pathname) => history.push(pathname) | ||
* } | ||
* }, | ||
* sessionType: 'auth0WebAuth' | ||
* }); | ||
*/ | ||
class Auth0WebAuth { | ||
/** | ||
* @param {Object} sdk An instance of the SDK so the module can communicate with other modules | ||
* @param {Object} sdk.audiences | ||
* @param {Object} sdk.audiences.contxtAuth | ||
* @param {string} sdk.audiences.contxtAuth.clientId The Auth0 client id of the | ||
* Contxt Auth environment | ||
* @param {Object} sdk.config | ||
* @param {Object} sdk.config.auth | ||
* @param {string} sdk.config.auth.authorizationPath Path that is called by Auth0 after | ||
* successfully authenticating | ||
* @param {string} sdk.config.auth.clientId The Auth0 client id of this application | ||
* @param {function} [sdk.config.auth.onRedirect] Redirect method used when navigating between | ||
* Auth0 callbacks | ||
*/ | ||
constructor(sdk) { | ||
@@ -30,2 +87,8 @@ this._sdk = sdk; | ||
/** | ||
* Gets the current access token (used to communicate with Auth0 & Contxt Auth) | ||
* | ||
* @returns {Promise} | ||
* @fulfills {string} accessToken | ||
*/ | ||
getCurrentAccessToken() { | ||
@@ -35,2 +98,8 @@ return this._getCurrentTokenByType('access'); | ||
/** | ||
* Gets the current API token (used to communicate with other Contxt APIs) | ||
* | ||
* @returns {Promise} | ||
* @fulfills {string} apiToken | ||
*/ | ||
getCurrentApiToken() { | ||
@@ -40,14 +109,32 @@ return this._getCurrentTokenByType('api'); | ||
/** | ||
* Gets the current user's profile from Auth0 | ||
* | ||
* @returns {Promise} | ||
* @fulfill {UserProfile} | ||
* @rejects {Error} | ||
*/ | ||
getProfile() { | ||
return new Promise((resolve, reject) => { | ||
this._auth0.client.userInfo(this.getCurrentAccessToken(), (err, profile) => { | ||
if (err) { | ||
reject(err); | ||
} | ||
return this.getCurrentAccessToken() | ||
.then((accessToken) => { | ||
return new Promise((resolve, reject) => { | ||
this._auth0.client.userInfo(accessToken, (err, profile) => { | ||
if (err) { | ||
reject(err); | ||
} | ||
resolve(profile); | ||
resolve(profile); | ||
}); | ||
}); | ||
}); | ||
}); | ||
} | ||
/** | ||
* Routine that takes unparsed information from Auth0, uses it to get a valid API token, and then | ||
* redirects to the correct page in the application. | ||
* | ||
* @returns {Promise} | ||
* @fulfill {Auth0WebAuthSessionInfo} | ||
* @rejects {Error} | ||
*/ | ||
handleAuthentication() { | ||
@@ -90,2 +177,7 @@ return this._parseWebAuthHash() | ||
/** | ||
* Tells caller if the current user is authenticated. | ||
* | ||
* @returns {boolean} | ||
*/ | ||
isAuthenticated() { | ||
@@ -98,2 +190,5 @@ const hasTokens = !!(this._sessionInfo && this._sessionInfo.accessToken && | ||
/** | ||
* Starts the Auth0 log in process | ||
*/ | ||
logIn() { | ||
@@ -103,2 +198,5 @@ this._auth0.authorize(); | ||
/** | ||
* Logs the user out by removing any stored session info and redirecting to the root | ||
*/ | ||
logOut() { | ||
@@ -114,2 +212,8 @@ this._sessionInfo = {}; | ||
/** | ||
* Default method used for redirecting around the web application. Overridden by `onRedirect` in | ||
* the auth config | ||
* | ||
* @private | ||
*/ | ||
_defaultOnRedirect(pathname) { | ||
@@ -119,2 +223,10 @@ window.location = pathname; | ||
/** | ||
* Requests an access token from Contxt Auth with the correct audiences. | ||
* | ||
* @returns {Promise} | ||
* @fulfill {string} accessToken | ||
* | ||
* @private | ||
*/ | ||
_getApiToken(accessToken) { | ||
@@ -137,12 +249,31 @@ return axios | ||
/** | ||
* Returns the type of token requested if it exists. | ||
* | ||
* @param {string} type The type of token requested. Only valid types are `access` and `api` | ||
* | ||
* @returns {Promise} | ||
* @fulfills {string} token | ||
* @rejects {Error} | ||
* | ||
* @private | ||
*/ | ||
_getCurrentTokenByType(type) { | ||
const propertyName = `${type}Token`; | ||
return new Promise((resolve, reject) => { | ||
const propertyName = `${type}Token`; | ||
if (!(this._sessionInfo && this._sessionInfo[propertyName])) { | ||
throw new Error(`No ${type} token found`); | ||
} | ||
if (!(this._sessionInfo && this._sessionInfo[propertyName])) { | ||
reject(new Error(`No ${type} token found`)); | ||
} | ||
return this._sessionInfo[propertyName]; | ||
resolve(this._sessionInfo[propertyName]); | ||
}); | ||
} | ||
/** | ||
* Grabs a stored redirect pathname that may have been stored in another part of the | ||
* web application | ||
* | ||
* @private | ||
*/ | ||
_getRedirectPathname() { | ||
@@ -155,2 +286,12 @@ const redirectPathname = localStorage.getItem('redirect_pathname'); | ||
/** | ||
* Loads a saved session from local storage | ||
* | ||
* @returns {Object} session | ||
* @returns {string} session.accessToken | ||
* @returns {string} session.apiToken | ||
* @returns {number} session.expiresAt | ||
* | ||
* @private | ||
*/ | ||
_loadSession() { | ||
@@ -164,2 +305,11 @@ return { | ||
/** | ||
* Wraps Auth0's method for parsing hash information after a successful authentication. | ||
* | ||
* @returns {Promise} | ||
* @fulfill {Object} hashResponse Information returned from Auth0 | ||
* @rejects {Error} | ||
* | ||
* @private | ||
*/ | ||
_parseWebAuthHash() { | ||
@@ -177,2 +327,12 @@ return new Promise((resolve, reject) => { | ||
/** | ||
* Saves a session in local storage for future use | ||
* | ||
* @param {Object} sessionInfo | ||
* @param {string} sessionInfo.accessToken | ||
* @param {string} sessionInfo.apiToken | ||
* @param {number} sessionInfo.expiresAt | ||
* | ||
* @private | ||
*/ | ||
_saveSession(sessionInfo) { | ||
@@ -187,2 +347,3 @@ this._sessionInfo = sessionInfo; | ||
export const TYPE = 'auth0WebAuth'; | ||
export default Auth0WebAuth; |
@@ -136,5 +136,5 @@ import auth0 from 'auth0-js'; | ||
describe(`getCurrent${tokenType}Token`, function() { | ||
let currentToken; | ||
let expectedToken; | ||
let getCurrentTokenByType; | ||
let promise; | ||
@@ -145,7 +145,7 @@ beforeEach(function() { | ||
getCurrentTokenByType = this.sandbox.stub(Auth0WebAuth.prototype, '_getCurrentTokenByType') | ||
.returns(expectedToken); | ||
.resolves(expectedToken); | ||
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession'); | ||
const auth0WebAuth = new Auth0WebAuth(sdk); | ||
currentToken = auth0WebAuth[`getCurrent${tokenType}Token`](); | ||
promise = auth0WebAuth[`getCurrent${tokenType}Token`](); | ||
}); | ||
@@ -157,4 +157,5 @@ | ||
it('returns a current token', function() { | ||
expect(currentToken).to.equal(expectedToken); | ||
it('returns a promise with the token', function() { | ||
return expect(promise).to.be.fulfilled | ||
.and.to.eventually.equal(expectedToken); | ||
}); | ||
@@ -177,3 +178,3 @@ }); | ||
getCurrentAccessToken = this.sandbox.stub(Auth0WebAuth.prototype, 'getCurrentAccessToken') | ||
.returns(expectedAccessToken); | ||
.resolves(expectedAccessToken); | ||
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession'); | ||
@@ -195,4 +196,5 @@ webAuthSession.client = { | ||
it("gets the user's profile", function() { | ||
expect(webAuthSession.client.userInfo) | ||
.to.be.calledWith(expectedAccessToken); | ||
return promise.then(() => { | ||
expect(webAuthSession.client.userInfo).to.be.calledWith(expectedAccessToken); | ||
}); | ||
}); | ||
@@ -206,19 +208,2 @@ | ||
context("there is no access token available to get a user's profile", function() { | ||
let auth0WebAuth; | ||
let promise; | ||
beforeEach(function() { | ||
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession'); | ||
webAuthSession.client = { userInfo: this.sandbox.stub() }; | ||
auth0WebAuth = new Auth0WebAuth(sdk); | ||
promise = auth0WebAuth.getProfile(); | ||
}); | ||
it('returns a rejected promise', function() { | ||
return expect(promise).to.be.rejected; | ||
}); | ||
}); | ||
context("there is an error getting a users's profile", function() { | ||
@@ -231,2 +216,4 @@ let expectedError; | ||
this.sandbox.stub(Auth0WebAuth.prototype, 'getCurrentAccessToken') | ||
.resolves(faker.internet.password()); | ||
this.sandbox.stub(Auth0WebAuth.prototype, '_loadSession'); | ||
@@ -590,9 +577,10 @@ webAuthSession.client = { | ||
it('throws an error when there is no current token of that type', function() { | ||
it('returns a rejected promise when there is no current token of that type', function() { | ||
const type = faker.hacker.adjective(); | ||
const fn = () => auth0WebAuth._getCurrentTokenByType(type); | ||
expect(fn).to.throw(`No ${type} token found`); | ||
const promise = auth0WebAuth._getCurrentTokenByType(type); | ||
return expect(promise).to.be.rejectedWith(`No ${type} token found`); | ||
}); | ||
it('returns a current token', function() { | ||
it('returns a resolved promise with the current token', function() { | ||
const type = faker.hacker.adjective(); | ||
@@ -602,5 +590,6 @@ const expectedToken = faker.internet.password(); | ||
auth0WebAuth._sessionInfo = { [`${type}Token`]: expectedToken }; | ||
const currentToken = auth0WebAuth._getCurrentTokenByType(type); | ||
const promise = auth0WebAuth._getCurrentTokenByType(type); | ||
expect(currentToken).to.equal(expectedToken); | ||
return expect(promise).to.be.fulfilled | ||
.and.to.eventually.equal(expectedToken); | ||
}); | ||
@@ -607,0 +596,0 @@ }); |
@@ -1,5 +0,17 @@ | ||
import Auth0WebAuth from './auth0WebAuth'; | ||
import Auth0WebAuth, { | ||
TYPE as AUTH0_WEB_AUTH | ||
} from './auth0WebAuth'; | ||
import MachineAuth, { | ||
TYPE as MACHINE_AUTH | ||
} from './machineAuth'; | ||
const TYPES = { | ||
AUTH0_WEB_AUTH, | ||
MACHINE_AUTH | ||
}; | ||
export { | ||
Auth0WebAuth | ||
Auth0WebAuth, | ||
MachineAuth, | ||
TYPES | ||
}; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
328792
50
4574
121
30
4