@cortezaproject/corteza-vue 2021.9.9 → 2021.9.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.es.js CHANGED
@@ -1934,7 +1934,7 @@ const storeKeyFinalState = 'auth.state.final';
1934
1934
  const storeKeyRefreshToken = 'auth.refresh-token';
1935
1935
  const maxStartAttempts = 5;
1936
1936
  class Auth {
1937
- constructor({ app, verbose, cortezaAuthURL, callbackURL, entrypointURL, location, sessionStorage, refreshFactor }) {
1937
+ constructor({ app, verbose, cortezaAuthURL, callbackURL, entrypointURL, location, sessionStorage, refreshFactor, registerEventListener }) {
1938
1938
  if (refreshFactor >= 1 || refreshFactor <= 0) {
1939
1939
  throw new Error('refreshFactor should be between 0 and 1');
1940
1940
  }
@@ -1944,6 +1944,7 @@ class Auth {
1944
1944
  this.callbackURL = callbackURL;
1945
1945
  this.location = location;
1946
1946
  this.sessionStorage = sessionStorage;
1947
+ this.registerEventListener = registerEventListener;
1947
1948
  this.refreshFactor = refreshFactor;
1948
1949
  this.entrypointURL = entrypointURL;
1949
1950
  this.log.debug('initialized auth plugin', {
@@ -1998,38 +1999,26 @@ class Auth {
1998
1999
  handle(req = new URL(this.entrypointURL)) {
1999
2000
  return __awaiter(this, void 0, void 0, function* () {
2000
2001
  this.log.info('handling authentication');
2001
- if (this.isCallback(req.pathname)) {
2002
- this.log.info('handling authentication callback');
2003
- const params = new URLSearchParams(req.search);
2002
+ // State management
2003
+ // - duplication checking
2004
+ // - management handlers
2005
+ const dup = this.handleStateManagement();
2006
+ if (dup) {
2007
+ this.log.debug('duplicate tab: unauthorized');
2008
+ throw new Error('Unauthenticated');
2009
+ }
2010
+ // Handle auth callback requests
2011
+ const params = new URLSearchParams(req.search);
2012
+ if (this.isCallback(req.pathname) && (params.has('error') || params.has('code'))) {
2004
2013
  if (params.has('error')) {
2005
2014
  throw new Error(params.get('error') || 'authentication flow failed with error');
2006
2015
  }
2007
- if (params.has('code')) {
2008
- let finalLocation = this.entrypointURL;
2009
- if (params.has('state')) {
2010
- const state = params.get('state') || '';
2011
- const storeKeyStateLocation = `auth.state.${state}.location`;
2012
- const tmp = this.sessionStorage.getItem(storeKeyStateLocation);
2013
- if (tmp === null) {
2014
- console.warn('state does not match, restarting authentication flow');
2015
- this.startAuthenticationFlow();
2016
- return null;
2017
- }
2018
- if (!this.isCallback(tmp)) {
2019
- // if by some coincidence we got callback URL to finalLocation
2020
- // we'll silently ignore it and redirect user back to entrypoint
2021
- finalLocation = tmp;
2022
- }
2023
- this.sessionStorage.removeItem(storeKeyStateLocation);
2024
- }
2025
- const code = params.get('code') || '';
2026
- this.log.info('authorization code received', code);
2027
- const rsp = yield this.exchangeCode(code);
2028
- this.startFinalState(finalLocation);
2029
- return rsp;
2030
- }
2016
+ this.log.info('handling authentication callback');
2017
+ return this.handleCallbackRoute(params.get('state'), (params.has('code') ? params.get('code') : ''));
2031
2018
  }
2032
- return this.check();
2019
+ // Handle auth from the current system state
2020
+ this.log.info('handling authentication from state');
2021
+ return this.handleState();
2033
2022
  });
2034
2023
  }
2035
2024
  /**
@@ -2046,10 +2035,16 @@ class Auth {
2046
2035
  * know, after the redirection to the final location, if this is a final stage or not and if the refresh token
2047
2036
  * belongs to this session or not.
2048
2037
  */
2049
- startFinalState(dst) {
2050
- this.log.info('redirecting back to final destination', dst);
2038
+ handleStateManagement() {
2039
+ // See if this is a duplicate
2040
+ const dup = this.sessionStorage.getItem(storeKeyFinalState) !== null;
2041
+ // Bind listeners to cleanup
2042
+ this.registerEventListener('beforeunload', function () {
2043
+ this.sessionStorage.removeItem(storeKeyFinalState);
2044
+ });
2045
+ // Set state
2051
2046
  this.sessionStorage.setItem(storeKeyFinalState, Date.now().toString());
2052
- this.location.assign(dst);
2047
+ return dup;
2053
2048
  }
2054
2049
  /**
2055
2050
  * Called when refresh token is re-fetched.
@@ -2057,7 +2052,6 @@ class Auth {
2057
2052
  * Cleanup aux items in the session store
2058
2053
  */
2059
2054
  completeFinalState() {
2060
- this.sessionStorage.removeItem(storeKeyFinalState);
2061
2055
  this.sessionStorage.removeItem(storeKeyFlowStarted);
2062
2056
  const stateKey = /^auth\.state\.\w+\.location$/;
2063
2057
  for (let i = 0; i < this.sessionStorage.length; i++) {
@@ -2068,12 +2062,38 @@ class Auth {
2068
2062
  }
2069
2063
  }
2070
2064
  /**
2071
- * Are we in a final state or not?
2065
+ * Exchanges the auth parameters for access & refresh token.
2072
2066
  *
2073
- * See startFinalState function for more details
2067
+ * If the parameters are correct and exchange is successful, the refresh token
2068
+ * gets stored in localStorage for further use, the access token and current user get stored
2069
+ * in-memory.
2070
+ *
2071
+ * Function will throw null when user is unauthenticated
2074
2072
  */
2075
- inFinalState() {
2076
- return this.sessionStorage.getItem(storeKeyFinalState) !== null;
2073
+ handleCallbackRoute(state, code) {
2074
+ return __awaiter(this, void 0, void 0, function* () {
2075
+ let finalLocation = this.entrypointURL;
2076
+ if (state) {
2077
+ const storeKeyStateLocation = `auth.state.${state}.location`;
2078
+ const tmp = this.sessionStorage.getItem(storeKeyStateLocation);
2079
+ if (tmp === null) {
2080
+ console.warn('state does not match, restarting authentication flow');
2081
+ this.startAuthenticationFlow();
2082
+ return null;
2083
+ }
2084
+ if (!this.isCallback(tmp)) {
2085
+ // if by some coincidence we got callback URL to finalLocation
2086
+ // we'll silently ignore it and redirect user back to entrypoint
2087
+ finalLocation = tmp;
2088
+ }
2089
+ this.sessionStorage.removeItem(storeKeyStateLocation);
2090
+ }
2091
+ this.log.info('authorization code received', code);
2092
+ const rsp = yield this.exchangeCode(code);
2093
+ this.log.info('redirecting back to final destination', finalLocation);
2094
+ this.location.assign(finalLocation);
2095
+ return rsp;
2096
+ });
2077
2097
  }
2078
2098
  /**
2079
2099
  * Checks current auth state; is access token loaded OR do we have a refresh token we can use
@@ -2087,7 +2107,7 @@ class Auth {
2087
2107
  *
2088
2108
  * Function will throw null when user is unauthenticated
2089
2109
  */
2090
- check() {
2110
+ handleState() {
2091
2111
  return __awaiter(this, void 0, void 0, function* () {
2092
2112
  this.log.info('checking authentication');
2093
2113
  if (this[accessToken]) {
@@ -2123,14 +2143,12 @@ class Auth {
2123
2143
  * If this is a duplicated-session, an error will be thrown and authentication will be (probably)
2124
2144
  * restarted by the caller.
2125
2145
  */
2126
- if (this.inFinalState()) {
2127
- this.log.info('refreshing token', refreshToken);
2128
- /**
2129
- * Refresh token found in the storage,
2130
- * let's use it to get new access token
2131
- */
2132
- return this.exchangeRefresh(refreshToken);
2133
- }
2146
+ this.log.info('refreshing token', refreshToken);
2147
+ /**
2148
+ * Refresh token found in the storage,
2149
+ * let's use it to get new access token
2150
+ */
2151
+ return this.exchangeRefresh(refreshToken);
2134
2152
  }
2135
2153
  throw new Error('Unauthenticated');
2136
2154
  });
@@ -2321,7 +2339,7 @@ class Auth {
2321
2339
  }
2322
2340
  function auth () {
2323
2341
  return function (Vue, opts) {
2324
- let { app = '', rootApp = false, cortezaAuthURL = '', callbackURL = '', verbose = undefined, refreshFactor = 0.75, entrypointURL = window.location.toString(), location = window.location, sessionStorage = window.sessionStorage, } = (opts || {});
2342
+ let { app = '', rootApp = false, cortezaAuthURL = '', callbackURL = '', verbose = undefined, refreshFactor = 0.75, entrypointURL = window.location.toString(), location = window.location, sessionStorage = window.sessionStorage, registerEventListener = window.addEventListener.bind(window), } = (opts || {});
2325
2343
  if (!cortezaAuthURL) {
2326
2344
  /**
2327
2345
  * cortezaAuthURL not explicitly set, try to auto-configure from properties set on window variable
@@ -2415,6 +2433,7 @@ function auth () {
2415
2433
  sessionStorage,
2416
2434
  entrypointURL,
2417
2435
  refreshFactor,
2436
+ registerEventListener,
2418
2437
  });
2419
2438
  };
2420
2439
  }