fluro
Version:
Promise based HTTP Fluro client for the browser and node.js
1,238 lines (874 loc) • 47.1 kB
HTML
<html lang="en">
<head>
<meta charset="utf-8">
<title>fluro.auth.js - Documentation</title>
<script src="scripts/prettify/prettify.js"></script>
<script src="scripts/prettify/lang-css.js"></script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
<script src="scripts/nav.js" defer></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
<label for="nav-trigger" class="navicon-button x">
<div class="navicon"></div>
</label>
<label for="nav-trigger" class="overlay"></label>
<nav >
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="access.html">access</a><ul class='methods'><li data-type='method'><a href="access.html#.addEventListener">addEventListener</a></li><li data-type='method'><a href="access.html#.can">can</a></li><li data-type='method'><a href="access.html#.canDeleteItem">canDeleteItem</a></li><li data-type='method'><a href="access.html#.canEditItem">canEditItem</a></li><li data-type='method'><a href="access.html#.canKnowOf">canKnowOf</a></li><li data-type='method'><a href="access.html#.canViewItem">canViewItem</a></li><li data-type='method'><a href="access.html#.has">has</a></li><li data-type='method'><a href="access.html#.isAuthor">isAuthor</a></li><li data-type='method'><a href="access.html#.removeAllListeners">removeAllListeners</a></li><li data-type='method'><a href="access.html#.removeEventListener">removeEventListener</a></li><li data-type='method'><a href="access.html#.retrieveActionableRealms">retrieveActionableRealms</a></li><li data-type='method'><a href="access.html#.retrieveCurrentSession">retrieveCurrentSession</a></li><li data-type='method'><a href="access.html#.setDefaultApplication">setDefaultApplication</a></li></ul></li><li><a href="api.html">api</a><ul class='methods'><li data-type='method'><a href="api.html#.delete">delete</a></li><li data-type='method'><a href="api.html#.generateEndpointURL">generateEndpointURL</a></li><li data-type='method'><a href="api.html#.get">get</a></li><li data-type='method'><a href="api.html#.post">post</a></li><li data-type='method'><a href="api.html#.put">put</a></li></ul></li><li><a href="app.device.html">device</a></li><li><a href="asset.html">asset</a><ul class='methods'><li data-type='method'><a href="asset.html#.avatarUrl">avatarUrl</a></li><li data-type='method'><a href="asset.html#.downloadUrl">downloadUrl</a></li><li data-type='method'><a href="asset.html#.filesize">filesize</a></li><li data-type='method'><a href="asset.html#.getUrl">getUrl</a></li><li data-type='method'><a href="asset.html#.imageUrl">imageUrl</a></li><li data-type='method'><a href="asset.html#.playerUrl">playerUrl</a></li><li data-type='method'><a href="asset.html#.posterUrl">posterUrl</a></li><li data-type='method'><a href="asset.html#.typeFromMime">typeFromMime</a></li></ul></li><li><a href="auth.html">auth</a><ul class='methods'><li data-type='method'><a href="auth.html#.addEventListener">addEventListener</a></li><li data-type='method'><a href="auth.html#.changeAccount">changeAccount</a></li><li data-type='method'><a href="auth.html#.getCurrentToken">getCurrentToken</a></li><li data-type='method'><a href="auth.html#.getCurrentUser">getCurrentUser</a></li><li data-type='method'><a href="auth.html#.impersonate">impersonate</a></li><li data-type='method'><a href="auth.html#.login">login</a></li><li data-type='method'><a href="auth.html#.logout">logout</a></li><li data-type='method'><a href="auth.html#.refreshAccessToken">refreshAccessToken</a></li><li data-type='method'><a href="auth.html#.removeAllListeners">removeAllListeners</a></li><li data-type='method'><a href="auth.html#.removeEventListener">removeEventListener</a></li><li data-type='method'><a href="auth.html#.retrieveUserFromResetToken">retrieveUserFromResetToken</a></li><li data-type='method'><a href="auth.html#.sendResetPasswordRequest">sendResetPasswordRequest</a></li><li data-type='method'><a href="auth.html#.set">set</a></li><li data-type='method'><a href="auth.html#.signup">signup</a></li><li data-type='method'><a href="auth.html#.updateUserWithToken">updateUserWithToken</a></li></ul></li><li><a href="cache.html">cache</a><ul class='methods'><li data-type='method'><a href="cache.html#.get">get</a></li><li data-type='method'><a href="cache.html#.reset">reset</a></li></ul></li><li><a href="components.html">components</a><ul class='methods'><li data-type='method'><a href="components.html#.hydrateModel">hydrateModel</a></li></ul></li><li><a href="content.html">content</a><ul class='methods'><li data-type='method'><a href="content.html#.duplicate">duplicate</a></li><li data-type='method'><a href="content.html#.external">external</a></li><li data-type='method'><a href="content.html#.filter">filter</a></li><li data-type='method'><a href="content.html#.form">form</a></li><li data-type='method'><a href="content.html#.get">get</a></li><li data-type='method'><a href="content.html#.getMultiple">getMultiple</a></li><li data-type='method'><a href="content.html#.keys">keys</a></li><li data-type='method'><a href="content.html#.list">list</a></li><li data-type='method'><a href="content.html#.query">query</a></li><li data-type='method'><a href="content.html#.related">related</a></li><li data-type='method'><a href="content.html#.retrieve">retrieve</a></li><li data-type='method'><a href="content.html#.slug">slug</a></li><li data-type='method'><a href="content.html#.submitInteraction">submitInteraction</a></li><li data-type='method'><a href="content.html#.submitPost">submitPost</a></li><li data-type='method'><a href="content.html#.thread">thread</a></li><li data-type='method'><a href="content.html#.values">values</a></li></ul></li><li><a href="date.html">date</a><ul class='methods'><li data-type='method'><a href="date.html#.countdown">countdown</a></li><li data-type='method'><a href="date.html#.dateFromID">dateFromID</a></li><li data-type='method'><a href="date.html#.formatDate">formatDate</a></li><li data-type='method'><a href="date.html#.getAge">getAge</a></li><li data-type='method'><a href="date.html#.groupEventByDate">groupEventByDate</a></li><li data-type='method'><a href="date.html#.isDifferentTimezoneThanUser">isDifferentTimezoneThanUser</a></li><li data-type='method'><a href="date.html#.isMultiDayEvent">isMultiDayEvent</a></li><li data-type='method'><a href="date.html#.localDate">localDate</a></li><li data-type='method'><a href="date.html#.militaryTimestamp">militaryTimestamp</a></li><li data-type='method'><a href="date.html#.readableEventDate">readableEventDate</a></li><li data-type='method'><a href="date.html#.readableEventTime">readableEventTime</a></li><li data-type='method'><a href="date.html#.timeago">timeago</a></li><li data-type='method'><a href="date.html#.timeline">timeline</a></li><li data-type='method'><a href="date.html#.timestampToAmPm">timestampToAmPm</a></li><li data-type='method'><a href="date.html#.timezones">timezones</a></li></ul></li><li><a href="fluro.html">fluro</a></li><li><a href="types.html">types</a><ul class='methods'><li data-type='method'><a href="types.html#.all">all</a></li><li data-type='method'><a href="types.html#.basicTypes">basicTypes</a></li><li data-type='method'><a href="types.html#.get">get</a></li><li data-type='method'><a href="types.html#.mapDefinitionItems">mapDefinitionItems</a></li><li data-type='method'><a href="types.html#.parentType">parentType</a></li><li data-type='method'><a href="types.html#.postableTypes">postableTypes</a></li><li data-type='method'><a href="types.html#.readable">readable</a></li><li data-type='method'><a href="types.html#.reloadTerminology">reloadTerminology</a></li><li data-type='method'><a href="types.html#.retrieve">retrieve</a></li><li data-type='method'><a href="types.html#.subTypes">subTypes</a></li><li data-type='method'><a href="types.html#.term">term</a></li></ul></li><li><a href="utils.html">utils</a><ul class='methods'><li data-type='method'><a href="utils.html#.arrayIDs">arrayIDs</a></li><li data-type='method'><a href="utils.html#.comma">comma</a></li><li data-type='method'><a href="utils.html#.currencySymbol">currencySymbol</a></li><li data-type='method'><a href="utils.html#.errorMessage">errorMessage</a></li><li data-type='method'><a href="utils.html#.extractFromArray">extractFromArray</a></li><li data-type='method'><a href="utils.html#.formatCurrency">formatCurrency</a></li><li data-type='method'><a href="utils.html#.getDefaultValueForField">getDefaultValueForField</a></li><li data-type='method'><a href="utils.html#.getFlattenedFields">getFlattenedFields</a></li><li data-type='method'><a href="utils.html#.getStringID">getStringID</a></li><li data-type='method'><a href="utils.html#.guid">guid</a></li><li data-type='method'><a href="utils.html#.hash">hash</a></li><li data-type='method'><a href="utils.html#.injectScript">injectScript</a></li><li data-type='method'><a href="utils.html#.machineName">machineName</a></li><li data-type='method'><a href="utils.html#.mapParameters">mapParameters</a></li><li data-type='method'><a href="utils.html#.matchInArray">matchInArray</a></li><li data-type='method'><a href="utils.html#.processCardPrioritySort">processCardPrioritySort</a></li></ul></li><li><a href="video.html">video</a><ul class='methods'><li data-type='method'><a href="video.html#.hhmmss">hhmmss</a></li><li data-type='method'><a href="video.html#.readableMilliseconds">readableMilliseconds</a></li><li data-type='method'><a href="video.html#.readableSeconds">readableSeconds</a></li></ul></li></ul>
</nav>
<div id="main">
<h1 class="page-title">fluro.auth.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>import axios from 'axios';
import _ from 'lodash';
import { EventDispatcher } from './fluro.utils';
///////////////////////////////////////////////////
/**
* Creates a new FluroAuth instance.
* This module provides a number of helper functions for authentication, logging in, signing up, generating and refreshing tokens
* @alias auth
* @constructor
* @hideconstructor
* @param {FluroCore} fluro A reference to the parent instance of the FluroCore module. This module is usually created by a FluroCore instance that passes itself in as the first argument.
*/
var FluroAuth = function(fluro) {
if (!fluro.api) {
throw new Error(`Can't Instantiate FluroAuth before FluroAPI exists`);
}
//Keep track of any refresh requests
var inflightRefreshRequest;
///////////////////////////////////////////////////
var defaultStore = {};
var store = defaultStore;
///////////////////////////////////////////////////
///////////////////////////////////////////////////
var service = {
debug: false,
}
Object.defineProperty(service, 'store', {
value: store,
writable: false,
});
//Create a new dispatcher
var dispatcher = new EventDispatcher();
dispatcher.bootstrap(service);
// console.log('New Dispatcher!', dispatcher)
///////////////////////////////////////////////////
///////////////////////////////////////////////////
function dispatch(parameters) {
//Get the current user
var user = store.user;
console.log('dispatch user!')
//Dispatch the change to the listeners
if (service.onChange) {
service.onChange(user);
}
//Dispatch the change event
dispatcher.dispatch('change', user, parameters);
}
///////////////////////////////////////////////////
function log(message) {
if (service.debug) {
console.log(message);
}
}
///////////////////////////////////////////////////
/**
*
* Sets the current user data, often from localStorage or after new session data
* has been generated from the server after signing in
* @alias auth.set
* @param {Object} user The user session object
* @example
* FluroAsset.set({firstName:'Jeff', lastName:'Andrews', ...})
*/
service.set = function(user, parameters, ignoreEvent) {
store.user = user;
log('fluro.auth > user set');
return dispatch(parameters)
}
///////////////////////////////////////////////////
/**
*
* Deletes the user session object, clears all Fluro caches and tokens
* from memory
* @alias auth.logout
* @example
* fluro.auth.logout()
*/
service.logout = function() {
//Unauthenticated
// delete store.token;
delete store.user;
fluro.cache.reset();
// delete store.refreshToken;
// delete store.expires;
log('fluro.auth > user logout');
if (fluro.withCredentials) {
//Logout of the current application
window.location.href = '/fluro/logout';
}
// if(window && window.localStorage) {
// window.localStorage.removeItem('fluro.user');
// }
return dispatch()
}
///////////////////////////////////////////////////
/**
*
* Retrieves a new session object for a Fluro global user for a specified account
* This will only work if the user has a persona in that account
* @alias auth.changeAccount
* @param {String} accountID The _id of the account you wish to log in to
* @param {Object} options
* @param {Object} options.disableAutoAuthenticate By default this function will set the current user session
* to account you are changing in to.
* If you want to generate the session without affecting your current session you can set disableAutoAuthenticate to true
* @return {Promise} Resolves to the user session object, or rejects with the responding error
* @example
* fluro.auth.changeAccount('5be504eabf33991239599d63').then(function(userSession) {
* //New user session will be set automatically
* var newUserSession = fluro.auth.getCurrentUser();
* })
* fluro.auth.changeAccount('5be504eabf33991239599d63', {disableAutoAuthenticate:true}).then(function(userSession) {
* //Set the session manually
* fluro.auth.set(userSession)
* })
*/
service.changeAccount = function(accountID, options) {
//Ensure we just have the ID
accountID = fluro.utils.getStringID(accountID);
//////////////////////////
if (!options) {
options = {};
}
//////////////////////////
//Change the users current tokens straight away
var autoAuthenticate = true;
if (options.disableAutoAuthenticate) {
autoAuthenticate = false;
}
//////////////////////////
var promise = fluro.api.post(`/token/account/${accountID}`)
promise.then(function(res) {
if (autoAuthenticate) {
fluro.cache.reset();
service.set(res.data);
}
}, function(err) {});
return promise;
}
///////////////////////////////////////////////////
/**
*
* Impersonates a persona and sets the current session to match the specified persona's context
* @alias auth.impersonate
* @param {String} personaID The _id of the persona you wish to impersonate
* @param {Object} options
* @return {Promise} Resolves to the user session object, or rejects with the responding error
* @example
* fluro.auth.impersonate('5be504eabf33991239599d63')
* .then(function(userSession) {
* //New user session will be set automatically
* var newUserSession = fluro.auth.getCurrentUser();
* })
*/
service.impersonate = function(personaID, options) {
//Ensure we just have the ID
personaID = fluro.utils.getStringID(personaID);
//////////////////////////
if (!options) {
options = {};
}
//////////////////////////
//Change the users current tokens straight away
var autoAuthenticate = true;
if (options.disableAutoAuthenticate) {
autoAuthenticate = false;
}
//////////////////////////
var promise = fluro.api.post(`/token/persona/${personaID}`)
promise.then(function(res) {
if (autoAuthenticate) {
fluro.cache.reset();
service.set(res.data);
}
}, function(err) {});
return promise;
}
///////////////////////////////////////////////////
/**
* Logs the user in to Fluro and returns a new user session
* @alias auth.login
* @param {Object} credentials
* @param {String} credentials.username The email address of the user to login as
* @param {String} credentials.password The password for the user
* @param {Object} options Extra options and configuration for the request
* @param {Object} options.disableAutoAuthenticate Disable automatic authentication, if true, will not set the current user session
* @param {Object} options.application Whether to attempt to login to the current application as a managed user persona, if not set will login as a global Fluro user
* @return {Promise} Returns a promise that either resolves with the logged in user session, or rejects with the responding error from the server
*/
service.login = function(credentials, options) {
if (!options) {
options = {};
}
//////////////////////////
//Change the users current tokens straight away
var autoAuthenticate = true;
if (options.disableAutoAuthenticate) {
autoAuthenticate = false;
}
//////////////////////////////////////
var promise = new Promise(loginCheck)
function loginCheck(resolve, reject) {
if (!credentials) {
return reject({
message: 'Missing credentials!',
})
}
if (!credentials.username || !credentials.username.length) {
return reject({
message: 'Username was not provided',
})
}
if (!credentials.password || !credentials.password.length) {
return reject({
message: 'Password was not provided',
})
}
/////////////////////////////////////////////
var postOptions = {
bypassInterceptor: true
}
/////////////////////////////////////////////
var url = fluro.apiURL + '/token/login';
/////////////////////////////////////////////
//If we are authenticating as an application
if (options.application) {
//The url is relative to the domain
url = `${fluro.domain || ''}/fluro/application/login`;
}
/////////////////////////////////////////////
//If we are logging in to a managed account use a different endpoint
if (options.managedAccount) {
url = fluro.apiURL + '/managed/' + options.managedAccount + '/login';
}
//If we have a specified url
if (options.url) {
url = options.url;
}
/////////////////////////////////////////////
fluro.api.post(url, credentials, postOptions).then(function(res) {
if (autoAuthenticate) {
store.user = res.data;
// console.log('Persist user', store.user)
dispatch();
// if (service.onChange) {
// service.onChange(store.user);
// }
}
resolve(res);
}, reject);
}
//////////////////////////////////////
return promise;
}
///////////////////////////////////////////////////
/**
* Signs up a new user to the current application, this will create a new managed user persona
* and automatically log in as that persona in the current application context. This function will
* only work when called in context of an application with the 'Application Token' authentication style.
* It will create a new user persona in the account of the application and return a session with all of the application's
* permissions and application's logged in user permissions
* @alias auth.signup
* @param {Object} credentials
* @param {String} credentials.firstName The first name for the new user persona
* @param {String} credentials.lastName The last name for the new user persona
* @param {String} credentials.username The email address for the new persona
* @param {String} credentials.password The password to set for the new persona
* @param {String} credentials.confirmPassword A double check to confirm the new password for the persona
* @param {Object} options Extra options and configuration for the request
* @return {Promise} Returns a promise that either resolves to the new authenticated session, or rejects with the responding error from the server
*/
service.signup = function(credentials, options) {
if (!options) {
options = {};
}
//////////////////////////
//Change the users current tokens straight away
var autoAuthenticate = true;
if (options.disableAutoAuthenticate) {
autoAuthenticate = false;
}
//////////////////////////////////////
var promise = new Promise(signupCheck)
function signupCheck(resolve, reject) {
if (!credentials) {
return reject({
message: 'No details provided',
})
}
if (!credentials.firstName || !credentials.firstName.length) {
return reject({
message: 'First Name was not provided',
})
}
if (!credentials.lastName || !credentials.lastName.length) {
return reject({
message: 'Last Name was not provided',
})
}
if (!credentials.username || !credentials.username.length) {
return reject({
message: 'Email/Username was not provided',
})
}
if (!credentials.password || !credentials.password.length) {
return reject({
message: 'Password was not provided',
})
}
if (!credentials.confirmPassword || !credentials.confirmPassword.length) {
return reject({
message: 'Confirm Password was not provided',
})
}
if (credentials.confirmPassword != credentials.password) {
return reject({
message: 'Your passwords do not match',
})
}
/////////////////////////////////////////////
var postOptions = {
bypassInterceptor: true
}
/////////////////////////////////////////////
var url = fluro.apiURL + '/user/signup';
/////////////////////////////////////////////
//If we are authenticating as an application
if (options.application) {
//The url is relative to the domain
url = `${fluro.domain || ''}/fluro/application/signup`;
}
//If we have a specified url
if (options.url) {
url = options.url;
}
/////////////////////////////////////////////
fluro.api.post(url, credentials, postOptions).then(function(res) {
if (autoAuthenticate) {
store.user = res.data;
dispatch();
}
resolve(res);
}, reject);
}
//////////////////////////////////////
return promise;
}
///////////////////////////////////////////////////
/**
* Retrieves a user's details by providing a password reset token
* @alias auth.retrieveUserFromResetToken
* @param {String} token The password reset token that was sent to the user's email address
* @param {Object} options other options for the request
* @param {Boolean} options.application If true will retrieve in the context of a managed persona in the same account as the current application.
* If not specified or false, will assume it's a Fluro global user that is resetting their password.
* @return {Promise} Returns a promise that resolves with the reset session details
*/
service.retrieveUserFromResetToken = function(token, options) {
if (!options) {
options = {};
}
//////////////////////////////////////
return new Promise(function(resolve, reject) {
var postOptions = {
bypassInterceptor: true
}
/////////////////////////////////////////////
//If a full fledged Fluro User
//then send directly to the API auth endpoint
var url = `${fluro.apiURL}/auth/token/${token}`;
/////////////////////////////////////////////
//If we are authenticating as an application
if (options.application) {
//The url is relative to the domain
url = `${fluro.domain || ''}/fluro/application/reset/${token}`;
}
//If we have a specified url
if (options.url) {
url = options.url;
}
/////////////////////////////////////////////
fluro.api.get(url, postOptions).then(function(res) {
return resolve(res.data);
}, reject);
});
}
///////////////////////////////////////////////////
/**
* Updates a user's details including password by providing a password reset token
* @alias auth.updateUserWithToken
* @param {String} token The password reset token that was sent to the user's email address
* @param {Object} body The details to change for the user
* @param {Object} options other options for the request
* @return {Promise} Returns a promise that resolves with the reset session details
*/
service.updateUserWithToken = function(token, body, options) {
if (!options) {
options = {};
}
//////////////////////////
//Change the users current tokens straight away
var autoAuthenticate = true;
if (options.disableAutoAuthenticate) {
autoAuthenticate = false;
}
//////////////////////////////////////
return new Promise(function(resolve, reject) {
var postOptions = {
bypassInterceptor: true
}
/////////////////////////////////////////////
//If a full fledged Fluro User
//then send directly to the API auth endpoint
var url = `${fluro.apiURL}/auth/token/${token}`;
/////////////////////////////////////////////
//If we are authenticating as an application
if (options.application) {
//The url is relative to the domain
url = `${fluro.domain || ''}/fluro/application/reset/${token}`;
}
//If we have a specified url
if (options.url) {
url = options.url;
}
/////////////////////////////////////////////
console.log('post request', url, body, postOptions)
fluro.api.post(url, body, postOptions).then(function(res) {
//If we should automatically authenticate
//once the request is successful
//Then clear caches and update the session
if (autoAuthenticate) {
fluro.cache.reset();
service.set(res.data);
}
return resolve(res.data);
}, reject);
});
}
///////////////////////////////////////////////////
/**
* Triggers a new Reset Password email request to the specified user.
* @alias auth.sendResetPasswordRequest
* @param {Object} body
* @param {String} body.username The email address of the user to reset the password for
* @param {String} body.redirect If the request is in the context of a managed user persona authenticated with an application, then you need to provide the url to direct the user to when they click the reset password link
* This is usually something like '/reset' for the current application, when the user clicks the link the reset token will be appended with ?token=RESET_TOKEN and your application should
* be ready on that url to handle the token and allow the user to use the token to reset their password
* @param {Object} options Extra options and configuration for the request
* @param {Boolean} options.application If true will send a reset email from the context of a managed persona in the same account as the current application.
* If not specified or false, will send a password reset request for a global Fluro user account.
* @return {Promise} Returns a promise that either resolves if the password request was sent, or rejects if an error occurred
*/
service.sendResetPasswordRequest = function(body, options) {
if (!options) {
options = {};
}
//////////////////////////////////////
var promise = new Promise(signupCheck)
function signupCheck(resolve, reject) {
if (!body) {
return reject({
message: 'No details provided',
})
}
if (!body.username || !body.username.length) {
return reject({
message: 'Email/Username was not provided',
})
}
//Set username as the email address
body.email = body.username;
/////////////////////////////////////////////
var postOptions = {
bypassInterceptor: true
}
/////////////////////////////////////////////
//If a full fledged Fluro User
//then send directly to the API
var url = fluro.apiURL + '/auth/resend';
/////////////////////////////////////////////
//If we are authenticating as an application
if (options.application) {
//The url is relative to the domain
url = `${fluro.domain || ''}/fluro/application/forgot`;
}
//If we have a specified url
if (options.url) {
url = options.url;
}
/////////////////////////////////////////////
fluro.api.post(url, body, postOptions).then(resolve, reject);
}
//////////////////////////////////////
return promise;
}
///////////////////////////////////////////////////
var nonAppRefreshContext = {};
var appRefreshContext = {};
/**
* Helper function to refresh an access token for an authenticated user session. This is usually handled automatically
* from the FluroAuth service itself
* @alias auth.refreshAccessToken
* @param {String} refreshToken The refresh token to reactivate
* @param {Boolean} isManagedSession Whether or not the refresh token is for a managed persona session or a global Fluro user session
* @return {Promise} A promise that either resolves with the refreshed token details or rejects with the responding error from the server
*/
service.refreshAccessToken = function(refreshToken, isManagedSession, appContext) {
var refreshContext = appContext ? appRefreshContext : nonAppRefreshContext;
// /////////////////////////////////////////////
// if (appContext) {
// console.log('refresh token in app context')
// } else {
// console.log('refresh token in normal context')
// }
// /////////////////////////////////////////////
//If there is already a request in progress
if (refreshContext.inflightRefreshRequest) {
log(`fluro.auth > use inflight request`);
return refreshContext.inflightRefreshRequest;
}
/////////////////////////////////////////////////////
//Create an refresh request
log(`fluro.auth > refresh token new request`);
refreshContext.inflightRefreshRequest = new Promise(function(resolve, reject) {
log(`fluro.auth > refresh token ${refreshToken}`);
//Bypass the interceptor on all token refresh calls
//Because we don't need to add the access token etc onto it
fluro.api.post('/token/refresh', {
refreshToken: refreshToken,
managed: isManagedSession,
}, {
bypassInterceptor: true,
application: appContext,
})
.then(function tokenRefreshComplete(res) {
//Update the user with any changes
//returned back from the refresh request
if (!res) {
log('fluro.auth > no res');
refreshContext.inflightRefreshRequest = null;
return reject();
} else {
if (fluro.GLOBAL_AUTH || appContext) {
if (fluro.app) {
if (fluro.app.user) {
_.assign(fluro.app.user, res.data);
fluro.app.user = fluro.app.user;
} else {
fluro.app.user = res.data;
}
}
} else {
if (store.user) {
Object.assign(store.user, res.data);
} else {
store.user = res.data;
}
}
log(`fluro.auth > token refreshed > ${res.data}`);
// if (service.onChange) {
// service.onChange(store.user);
// }
dispatch();
// }
}
//Resolve with the new token
resolve(res.data.token);
//Remove the inflight request
setTimeout(function() {
refreshContext.inflightRefreshRequest = null;
})
})
.catch(function(err) {
console.log('TOKEN REFRESH FAILED', err);
//TODO Check if invalid_refresh_token
//console.log('Refresh request Failed')
setTimeout(function() {
refreshContext.inflightRefreshRequest = null;
});
reject(err);
});
});
//Return the refresh request
return refreshContext.inflightRefreshRequest;
}
///////////////////////////////////////////////////
/**
* Helper function to resync the user's session from the server. This is often used when first loading a webpage or app
* just to see if the user's permissions have changed since the user first logged in
* from the FluroAuth service itself
* @alias auth.sync
* @return {Promise} A promise that either resolves with the user session
*/
var retryCount = 0;
service.sync = function() {
// console.log('Sync with server', store.user)
return fluro.api.get('/session')
.then(function(res) {
// console.log('sync response', res);
if (res.data) {
//Update the user with any changes
//returned back from the refresh request
if (fluro.GLOBAL_AUTH) {
if (fluro.app.user) {
fluro.app.user = Object.assign(fluro.app.user, res.data);
} else {
fluro.app.user = res.data;
}
} else {
if (store.user) {
Object.assign(store.user, res.data);
}
}
} else {
if (fluro.GLOBAL_AUTH) {
fluro.app.user = null;
} else {
store.user = null;
}
}
log('fluro.auth > server session refreshed');
retryCount = 0;
dispatch();
})
.catch(function(err) {
// if (retryCount > 2) {
console.log('auth sync not logged in');
if (fluro.GLOBAL_AUTH) {
fluro.app.user = null;
} else {
store.user = null;
}
retryCount = 0;
dispatch();
// } else {
// console.log('Retry sync')
// retryCount++;
// service.sync();
// }
});
}
/////////////////////////////////////////////////////
/**
* Returns the current user's access token
* @alias auth.getCurrentToken
* @return {String} The Fluro access token for the current user session
*/
service.getCurrentToken = function() {
var currentUser = service.getCurrentUser() || {};
return currentUser.token || fluro.applicationToken;
}
/////////////////////////////////////////////////////
/**
* Returns the current user's session data
* @alias auth.getCurrentUser
* @return {Object} The current user session
*/
service.getCurrentUser = function() {
return fluro.GLOBAL_AUTH ? fluro.app.user : _.get(store, 'user');
}
/////////////////////////////////////////////////////
fluro.api.interceptors.request.use(function(config) {
//If we want to bypass the interceptor
//then just return the request
if (config.bypassInterceptor) {
console.log('auth interceptor was bypassed');
return config;
}
//////////////////////////////
//////////////////////////////
//////////////////////////////
//////////////////////////////
//Get the original request
var originalRequest = config;
//If we aren't logged in or don't have a token
var token;
var refreshToken;
var applicationToken = fluro.applicationToken;
//////////////////////////////
//If we are running in an application context
if (config.application || fluro.GLOBAL_AUTH) {
token = _.get(fluro, 'app.user.token');
refreshToken = _.get(fluro, 'app.user.refreshToken');
} else {
//Get the token and refresh token
token = _.get(store, 'user.token');
refreshToken = _.get(store, 'user.refreshToken');
}
//////////////////////////////
//If there is a user token
if (token) {
//Set the token of the request as the user's access token
originalRequest.headers['Authorization'] = 'Bearer ' + token;
log('fluro.auth > using user token');
} else if (applicationToken && applicationToken.length) {
//If there is a static application token
//For example we have logged out from a website
//that has public content also
originalRequest.headers['Authorization'] = 'Bearer ' + applicationToken;
log('fluro.auth > using app token');
return originalRequest;
} else {
//Return the original request without a token
log('fluro.auth > no token');
return originalRequest;
}
/////////////////////////////////////////////////////
//If no refresh token
if (!refreshToken) {
log('fluro.auth > no refresh token');
//Continue with the original request
return originalRequest;
}
/////////////////////////////////////////////////////
//We have a refresh token so we need to check
//whether our access token is stale and needs to be refreshed
var now = new Date();
//Give us a bit of buffer incase some of our images
//are still loading
now.setSeconds(now.getSeconds() + 10);
var expiryDate;
if (config.application || fluro.GLOBAL_AUTH) {
expiryDate = _.get(fluro, 'app.user.expires');
} else {
expiryDate = _.get(store, 'user.expires');
}
var expires = new Date(expiryDate);
//If we are not debugging
if (service.debug) {
console.log('debug', now, expires)
} else {
//If the token is still fresh
if (now < expires) {
//Return the original request
return originalRequest;
}
}
/////////////////////////////////////////////////////
var isManagedUser = config.application || _.get(store, 'user.accountType') == 'managed';
if (fluro.GLOBAL_AUTH) {
isManagedUser = false;
}
//The token is stale by this point
log('fluro.auth > token expired');
return new Promise(function(resolve, reject) {
//Refresh the token
service.refreshAccessToken(refreshToken, isManagedUser, config.application)
.then(function(newToken) {
log('fluro.auth > token refreshed', isManagedUser);
//Update the original request with our new token
originalRequest.headers['Authorization'] = 'Bearer ' + newToken;
//And continue onward
return resolve(originalRequest);
})
.catch(function(err) {
console.log('ERRRRRR', err);
log('fluro.auth > token refresh rejected', err);
return reject(err);
});
});
}, function(error) {
return Promise.reject(error);
})
/////////////////////////////////////////////////////
fluro.api.interceptors.response.use(function(response) {
return response;
}, function(err) {
//////////////////////////////
//Get the response status
var status = _.get(err, 'response.status') || err.status;
log('fluro.auth > error', status);
switch (status) {
case 401:
//////////////////////////////
console.log('ERROR CAPTURE HERE', err.response, err.config);
//If we are running in an application context
if (_.get(err, 'config.application') || fluro.GLOBAL_AUTH) {
//Kill our app user store
if (fluro.app) {
fluro.app.user = null;
}
return Promise.reject(err);
}
// //////////////////////////////
// //If it's an invalid refresh token
// //In case it was a mismatch between tabs or sessions
// //we should try it a second time just in case
// var data = _.get(err, 'response.data');
// if (data == 'invalid_refresh_token') {
// //Try it again
// // console.log('Refresh failed but its ok')
// } else {
// //Logout and destroy the session
// }
console.log('logout from 401')
service.logout();
break;
default:
//Some other error
break;
}
/////////////////////////////////////////////////////
return Promise.reject(err);
})
/**
* @name auth.addEventListener
* @description Adds a callback that will be triggered whenever the specified event occurs
* @function
* @param {String} event The event to listen for
* @param {Function} callback The function to fire when this event is triggered
* @example
* //Listen for when the user session changes
* fluro.auth.addEventListener('change', function(userSession) {})
*/
/**
* @name auth.removeEventListener
* @description Removes all a callback from the listener list
* @function
* @param {String} event The event to stop listening for
* @param {Function} callback The function to remove from the listener list
* @example
* //Stop listening for the change event
* fluro.auth.removeEventListener('change', myFunction)
*/
/**
* @name auth.removeAllListeners
* @description Removes all listening callbacks for all events
* @function
* @example
* fluro.auth.removeAllListeners()
*/
return service;
}
export default FluroAuth;</code></pre>
</article>
</section>
</div>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.6.3</a> on Tue Jun 29 2021 08:38:17 GMT+1000 (Australian Eastern Standard Time) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
</footer>
<script>prettyPrint();</script>
<script src="scripts/polyfill.js"></script>
<script src="scripts/linenumber.js"></script>
</body>
</html>