fluro.auth.js

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;