UNPKG

fluro

Version:

Promise based HTTP Fluro client for the browser and node.js

1,238 lines (874 loc) 47.1 kB
<!DOCTYPE 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 &amp;&amp; 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 &amp;&amp; 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 &lt; 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>