text: 'use strict';
  
  Object.defineProperty(exports, '__esModule', { value: true });
  
  const Vue = require('vue');
  const cookie2 = require('cookie');
  const jwtDecode = require('jwt-decode');
  const defu2 = require('defu');
  const requrl2 = require('requrl');
  
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
  
  const Vue__default = /*#__PURE__*/_interopDefaultLegacy(Vue);
  const cookie2__default = /*#__PURE__*/_interopDefaultLegacy(cookie2);
  const jwtDecode__default = /*#__PURE__*/_interopDefaultLegacy(jwtDecode);
  const defu2__default = /*#__PURE__*/_interopDefaultLegacy(defu2);
  const requrl2__default = /*#__PURE__*/_interopDefaultLegacy(requrl2);
  
  const isUnset = (o) => typeof o === "undefined" || o === null;
  const isSet = (o) => !isUnset(o);
  const isSameURL = (ctx, a, b) => normalizePath(a, ctx) === normalizePath(b, ctx);
  function isRelativeURL(u) {
    return u && u.length && /^\/([a-zA-Z0-9@\-%_~.][/a-zA-Z0-9@\-%_~.]*)?([?][^#]*)?(#[^#]*)?$/.test(u);
  }
  function parseQuery(queryString) {
    const query = {};
    const pairs = queryString.split("&");
    for (let i = 0; i < pairs.length; i++) {
      const pair = pairs[i].split("=");
      query[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || "");
    }
    return query;
  }
  function encodeQuery(queryObject) {
    return Object.entries(queryObject).filter(([_key, value]) => typeof value !== "undefined").map(([key, value]) => encodeURIComponent(key) + (value != null ? "=" + encodeURIComponent(value) : "")).join("&");
  }
  function routeOption(route, key, value) {
    return route.matched.some((m) => {
      if (process.client) {
        return Object.values(m.components).some((component) => component.options && component.options[key] === value);
      } else {
        return Object.values(m.components).some((component) => Object.values(component._Ctor).some((ctor) => ctor.options && ctor.options[key] === value));
      }
    });
  }
  function getMatchedComponents(route, matches = []) {
    return [].concat(...[], ...route.matched.map(function(m, index) {
      return Object.keys(m.components).map(function(key) {
        matches.push(index);
        return m.components[key];
      });
    }));
  }
  function normalizePath(path = "", ctx) {
    let result = path.split("?")[0];
    if (ctx && ctx.base) {
      result = result.replace(ctx.base, "/");
    }
    if (result.charAt(result.length - 1) === "/") {
      result = result.slice(0, -1);
    }
    result = result.replace(/\/+/g, "/");
    return result;
  }
  function encodeValue(val) {
    if (typeof val === "string") {
      return val;
    }
    return JSON.stringify(val);
  }
  function decodeValue(val) {
    if (typeof val === "string") {
      try {
        return JSON.parse(val);
      } catch (_) {
      }
    }
    return val;
  }
  function getProp(holder, propName) {
    if (!propName || !holder || typeof holder !== "object") {
      return holder;
    }
    if (propName in holder) {
      return holder[propName];
    }
    const propParts = Array.isArray(propName) ? propName : (propName + "").split(".");
    let result = holder;
    while (propParts.length && result) {
      result = result[propParts.shift()];
    }
    return result;
  }
  function addTokenPrefix(token, tokenType) {
    if (!token || !tokenType || typeof token !== "string" || token.startsWith(tokenType)) {
      return token;
    }
    return tokenType + " " + token;
  }
  function removeTokenPrefix(token, tokenType) {
    if (!token || !tokenType || typeof token !== "string") {
      return token;
    }
    return token.replace(tokenType + " ", "");
  }
  function urlJoin(...args) {
    return args.join("/").replace(/[/]+/g, "/").replace(/^(.+):\//, "$1://").replace(/^file:/, "file:/").replace(/\/(\?|&|#[^!])/g, "$1").replace(/\?/g, "&").replace("&", "?");
  }
  function cleanObj(obj) {
    for (const key in obj) {
      if (obj[key] === void 0) {
        delete obj[key];
      }
    }
    return obj;
  }
  const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  function randomString(length) {
    let result = "";
    const charactersLength = characters.length;
    for (let i = 0; i < length; i++) {
      result += characters.charAt(Math.floor(Math.random() * charactersLength));
    }
    return result;
  }
  
  class Storage {
    constructor(ctx, options) {
      this.ctx = ctx;
      this.options = options;
      this._initState();
    }
    setUniversal(key, value) {
      if (isUnset(value)) {
        return this.removeUniversal(key);
      }
      this.setCookie(key, value);
      this.setLocalStorage(key, value);
      this.setState(key, value);
      return value;
    }
    getUniversal(key) {
      let value;
      if (process.server) {
        value = this.getState(key);
      }
      if (isUnset(value)) {
        value = this.getCookie(key);
      }
      if (isUnset(value)) {
        value = this.getLocalStorage(key);
      }
      if (isUnset(value)) {
        value = this.getState(key);
      }
      return value;
    }
    syncUniversal(key, defaultValue) {
      let value = this.getUniversal(key);
      if (isUnset(value) && isSet(defaultValue)) {
        value = defaultValue;
      }
      if (isSet(value)) {
        this.setUniversal(key, value);
      }
      return value;
    }
    removeUniversal(key) {
      this.removeState(key);
      this.removeLocalStorage(key);
      this.removeCookie(key);
    }
    _initState() {
      Vue__default['default'].set(this, "_state", {});
      this._useVuex = this.options.vuex && !!this.ctx.store;
      if (this._useVuex) {
        const storeModule = {
          namespaced: true,
          state: () => this.options.initialState,
          mutations: {
            SET(state, payload) {
              Vue__default['default'].set(state, payload.key, payload.value);
            }
          }
        };
        this.ctx.store.registerModule(this.options.vuex.namespace, storeModule, {
          preserveState: Boolean(this.ctx.store.state[this.options.vuex.namespace])
        });
        this.state = this.ctx.store.state[this.options.vuex.namespace];
      } else {
        Vue__default['default'].set(this, "state", {});
      }
    }
    setState(key, value) {
      if (key[0] === "_") {
        Vue__default['default'].set(this._state, key, value);
      } else if (this._useVuex) {
        this.ctx.store.commit(this.options.vuex.namespace + "/SET", {
          key,
          value
        });
      } else {
        Vue__default['default'].set(this.state, key, value);
      }
      return value;
    }
    getState(key) {
      if (key[0] !== "_") {
        return this.state[key];
      } else {
        return this._state[key];
      }
    }
    watchState(key, fn) {
      if (this._useVuex) {
        return this.ctx.store.watch((state) => getProp(state[this.options.vuex.namespace], key), fn);
      }
    }
    removeState(key) {
      this.setState(key, void 0);
    }
    setLocalStorage(key, value) {
      if (isUnset(value)) {
        return this.removeLocalStorage(key);
      }
      if (typeof localStorage === "undefined" || !this.options.localStorage) {
        return;
      }
      const _key = this.options.localStorage.prefix + key;
      try {
        localStorage.setItem(_key, encodeValue(value));
      } catch (e) {
        if (!this.options.ignoreExceptions) {
          throw e;
        }
      }
      return value;
    }
    getLocalStorage(key) {
      if (typeof localStorage === "undefined" || !this.options.localStorage) {
        return;
      }
      const _key = this.options.localStorage.prefix + key;
      const value = localStorage.getItem(_key);
      return decodeValue(value);
    }
    removeLocalStorage(key) {
      if (typeof localStorage === "undefined" || !this.options.localStorage) {
        return;
      }
      const _key = this.options.localStorage.prefix + key;
      localStorage.removeItem(_key);
    }
    getCookies() {
      const cookieStr = process.client ? document.cookie : this.ctx.req.headers.cookie;
      return cookie2__default['default'].parse(cookieStr || "") || {};
    }
    setCookie(key, value, options = {}) {
      if (!this.options.cookie || process.server && !this.ctx.res) {
        return;
      }
      const _prefix = options.prefix !== void 0 ? options.prefix : this.options.cookie.prefix;
      const _key = _prefix + key;
      const _options = Object.assign({}, this.options.cookie.options, options);
      const _value = encodeValue(value);
      if (isUnset(value)) {
        _options.maxAge = -1;
      }
      if (typeof _options.expires === "number") {
        _options.expires = new Date(Date.now() + _options.expires * 864e5);
      }
      const serializedCookie = cookie2__default['default'].serialize(_key, _value, _options);
      if (process.client) {
        document.cookie = serializedCookie;
      } else if (process.server && this.ctx.res) {
        const cookies = this.ctx.res.getHeader("Set-Cookie") || [];
        cookies.unshift(serializedCookie);
        this.ctx.res.setHeader("Set-Cookie", cookies.filter((v, i, arr) => arr.findIndex((val) => val.startsWith(v.substr(0, v.indexOf("=")))) === i));
      }
      return value;
    }
    getCookie(key) {
      if (!this.options.cookie || process.server && !this.ctx.req) {
        return;
      }
      const _key = this.options.cookie.prefix + key;
      const cookies = this.getCookies();
      const value = cookies[_key] ? decodeURIComponent(cookies[_key]) : void 0;
      return decodeValue(value);
    }
    removeCookie(key, options) {
      this.setCookie(key, void 0, options);
    }
  }
  
  class Auth {
    constructor(ctx, options) {
      this.strategies = {};
      this._errorListeners = [];
      this._redirectListeners = [];
      this.ctx = ctx;
      this.options = options;
      const initialState = {user: null, loggedIn: false};
      const storage2 = new Storage(ctx, {...options, ...{initialState}});
      this.$storage = storage2;
      this.$state = storage2.state;
    }
    get state() {
      if (!this._stateWarnShown) {
        this._stateWarnShown = true;
        console.warn("[AUTH] $auth.state is deprecated. Please use $auth.$state or top level props like $auth.loggedIn");
      }
      return this.$state;
    }
    get strategy() {
      return this.getStrategy();
    }
    getStrategy(throwException = true) {
      if (throwException) {
        if (!this.$state.strategy) {
          throw new Error("No strategy is set!");
        }
        if (!this.strategies[this.$state.strategy]) {
          throw new Error("Strategy not supported: " + this.$state.strategy);
        }
      }
      return this.strategies[this.$state.strategy];
    }
    get user() {
      return this.$state.user;
    }
    get loggedIn() {
      return this.$state.loggedIn;
    }
    get busy() {
      return this.$storage.getState("busy");
    }
    async init() {
      if (this.options.resetOnError) {
        this.onError((...args) => {
          if (typeof this.options.resetOnError !== "function" || this.options.resetOnError(...args)) {
            this.reset();
          }
        });
      }
      this.$storage.syncUniversal("strategy", this.options.defaultStrategy);
      if (!this.getStrategy(false)) {
        this.$storage.setUniversal("strategy", this.options.defaultStrategy);
        if (!this.getStrategy(false)) {
          return Promise.resolve();
        }
      }
      try {
        await this.mounted();
      } catch (error) {
        this.callOnError(error);
      } finally {
        if (process.client && this.options.watchLoggedIn) {
          this.$storage.watchState("loggedIn", (loggedIn) => {
            if (!routeOption(this.ctx.route, "auth", false)) {
              this.redirect(loggedIn ? "home" : "logout");
            }
          });
        }
      }
    }
    getState(key) {
      if (!this._getStateWarnShown) {
        this._getStateWarnShown = true;
        console.warn("[AUTH] $auth.getState is deprecated. Please use $auth.$storage.getState() or top level props like $auth.loggedIn");
      }
      return this.$storage.getState(key);
    }
    registerStrategy(name, strategy) {
      this.strategies[name] = strategy;
    }
    setStrategy(name) {
      if (name === this.$storage.getUniversal("strategy")) {
        return Promise.resolve();
      }
      if (!this.strategies[name]) {
        throw new Error(`Strategy ${name} is not defined!`);
      }
      this.reset();
      this.$storage.setUniversal("strategy", name);
      return this.mounted();
    }
    mounted(...args) {
      if (!this.getStrategy().mounted) {
        return this.fetchUserOnce();
      }
      return Promise.resolve(this.getStrategy().mounted(...args)).catch((error) => {
        this.callOnError(error, {method: "mounted"});
        return Promise.reject(error);
      });
    }
    loginWith(name, ...args) {
      return this.setStrategy(name).then(() => this.login(...args));
    }
    login(...args) {
      if (!this.getStrategy().login) {
        return Promise.resolve();
      }
      return this.wrapLogin(this.getStrategy().login(...args)).catch((error) => {
        this.callOnError(error, {method: "login"});
        return Promise.reject(error);
      });
    }
    fetchUser(...args) {
      if (!this.getStrategy().fetchUser) {
        return Promise.resolve();
      }
      return Promise.resolve(this.getStrategy().fetchUser(...args)).catch((error) => {
        this.callOnError(error, {method: "fetchUser"});
        return Promise.reject(error);
      });
    }
    logout(...args) {
      if (!this.getStrategy().logout) {
        this.reset();
        return Promise.resolve();
      }
      return Promise.resolve(this.getStrategy().logout(...args)).catch((error) => {
        this.callOnError(error, {method: "logout"});
        return Promise.reject(error);
      });
    }
    setUserToken(token, refreshToken) {
      if (!this.getStrategy().setUserToken) {
        this.getStrategy().token.set(token);
        return Promise.resolve();
      }
      return Promise.resolve(this.getStrategy().setUserToken(token, refreshToken)).catch((error) => {
        this.callOnError(error, {method: "setUserToken"});
        return Promise.reject(error);
      });
    }
    reset(...args) {
      if (!this.getStrategy().reset) {
        this.setUser(false);
        this.getStrategy().token.reset();
        this.getStrategy().refreshToken.reset();
      }
      return this.getStrategy().reset(...args);
    }
    refreshTokens() {
      if (!this.getStrategy().refreshController) {
        return Promise.resolve();
      }
      return Promise.resolve(this.getStrategy().refreshController.handleRefresh()).catch((error) => {
        this.callOnError(error, {method: "refreshTokens"});
        return Promise.reject(error);
      });
    }
    check(...args) {
      if (!this.getStrategy().check) {
        return {valid: true};
      }
      return this.getStrategy().check(...args);
    }
    fetchUserOnce(...args) {
      if (!this.$state.user) {
        return this.fetchUser(...args);
      }
      return Promise.resolve();
    }
    setUser(user) {
      this.$storage.setState("user", user);
      let check = {valid: Boolean(user)};
      if (check.valid) {
        check = this.check();
      }
      this.$storage.setState("loggedIn", check.valid);
    }
    request(endpoint, defaults = {}) {
      const _endpoint = typeof defaults === "object" ? Object.assign({}, defaults, endpoint) : endpoint;
      if (!this.ctx.app.$axios) {
        console.error("[AUTH] add the @nuxtjs/axios module to nuxt.config file");
        return;
      }
      return this.ctx.app.$axios.request(_endpoint).catch((error) => {
        this.callOnError(error, {method: "request"});
        return Promise.reject(error);
      });
    }
    requestWith(strategy, endpoint, defaults) {
      const token = this.getStrategy().token.get();
      const _endpoint = Object.assign({}, defaults, endpoint);
      const tokenName = this.strategies[strategy].options.token.name || "Authorization";
      if (!_endpoint.headers) {
        _endpoint.headers = {};
      }
      if (!_endpoint.headers[tokenName] && isSet(token) && token && typeof token === "string") {
        _endpoint.headers[tokenName] = token;
      }
      return this.request(_endpoint);
    }
    wrapLogin(promise) {
      this.$storage.setState("busy", true);
      this.error = null;
      return Promise.resolve(promise).then((response) => {
        this.$storage.setState("busy", false);
        return response;
      }).catch((error) => {
        this.$storage.setState("busy", false);
        return Promise.reject(error);
      });
    }
    onError(listener) {
      this._errorListeners.push(listener);
    }
    callOnError(error, payload = {}) {
      this.error = error;
      for (const fn of this._errorListeners) {
        fn(error, payload);
      }
    }
    redirect(name, noRouter = false) {
      if (!this.options.redirect) {
        return;
      }
      const from = this.options.fullPathRedirect ? this.ctx.route.fullPath : this.ctx.route.path;
      let to = this.options.redirect[name];
      if (!to) {
        return;
      }
      if (this.options.rewriteRedirects) {
        if (name === "login" && isRelativeURL(from) && !isSameURL(this.ctx, to, from)) {
          this.$storage.setUniversal("redirect", from);
        }
        if (name === "home") {
          const redirect = this.$storage.getUniversal("redirect");
          this.$storage.setUniversal("redirect", null);
          if (isRelativeURL(redirect)) {
            to = redirect;
          }
        }
      }
      to = this.callOnRedirect(to, from) || to;
      if (isSameURL(this.ctx, to, from)) {
        return;
      }
      if (process.client) {
        if (noRouter) {
          if (isRelativeURL(to) && !to.includes(this.ctx.base)) {
            to = normalizePath("/" + this.ctx.base + "/" + to);
          }
          window.location.replace(to);
        } else {
          this.ctx.redirect(to, this.ctx.query);
        }
      } else {
        this.ctx.redirect(to, this.ctx.query);
      }
    }
    onRedirect(listener) {
      this._redirectListeners.push(listener);
    }
    callOnRedirect(to, from) {
      for (const fn of this._redirectListeners) {
        to = fn(to, from) || to;
      }
      return to;
    }
    hasScope(scope) {
      const userScopes = this.$state.user && getProp(this.$state.user, this.options.scopeKey);
      if (!userScopes) {
        return false;
      }
      if (Array.isArray(userScopes)) {
        return userScopes.includes(scope);
      }
      return Boolean(getProp(userScopes, scope));
    }
  }
  
  const authMiddleware = async (ctx) => {
    if (routeOption(ctx.route, "auth", false)) {
      return;
    }
    const matches = [];
    const Components = getMatchedComponents(ctx.route, matches);
    if (!Components.length) {
      return;
    }
    const {login, callback} = ctx.$auth.options.redirect;
    const pageIsInGuestMode = routeOption(ctx.route, "auth", "guest");
    const insidePage = (page) => normalizePath(ctx.route.path, ctx) === normalizePath(page, ctx);
    if (ctx.$auth.$state.loggedIn) {
      const {
        tokenExpired,
        refreshTokenExpired,
        isRefreshable
      } = ctx.$auth.check(true);
      if (!login || insidePage(login) || pageIsInGuestMode) {
        ctx.$auth.redirect("home");
      }
      if (refreshTokenExpired) {
        ctx.$auth.reset();
      } else if (tokenExpired) {
        if (isRefreshable) {
          try {
            await ctx.$auth.refreshTokens();
          } catch (error) {
            ctx.$auth.reset();
          }
        } else {
          ctx.$auth.reset();
        }
      }
    } else if (!pageIsInGuestMode && (!callback || !insidePage(callback))) {
      ctx.$auth.redirect("login");
    }
  };
  
  class ExpiredAuthSessionError extends Error {
    constructor() {
      super("Both token and refresh token have expired. Your request was aborted.");
      this.name = "ExpiredAuthSessionError";
    }
  }
  
  class RefreshController {
    constructor(scheme) {
      this.scheme = scheme;
      this._refreshPromise = null;
      this.$auth = scheme.$auth;
    }
    handleRefresh() {
      if (this._refreshPromise) {
        return this._refreshPromise;
      }
      return this._doRefresh();
    }
    _doRefresh() {
      this._refreshPromise = new Promise((resolve, reject) => {
        this.scheme.refreshTokens().then((response) => {
          this._refreshPromise = null;
          resolve(response);
        }).catch((error) => {
          this._refreshPromise = null;
          reject(error);
        });
      });
      return this._refreshPromise;
    }
  }
  
  exports.TokenStatusEnum = void 0;
  (function(TokenStatusEnum2) {
    TokenStatusEnum2["UNKNOWN"] = "UNKNOWN";
    TokenStatusEnum2["VALID"] = "VALID";
    TokenStatusEnum2["EXPIRED"] = "EXPIRED";
  })(exports.TokenStatusEnum || (exports.TokenStatusEnum = {}));
  class TokenStatus {
    constructor(token, tokenExpiresAt) {
      this._status = this._calculate(token, tokenExpiresAt);
    }
    unknown() {
      return exports.TokenStatusEnum.UNKNOWN === this._status;
    }
    valid() {
      return exports.TokenStatusEnum.VALID === this._status;
    }
    expired() {
      return exports.TokenStatusEnum.EXPIRED === this._status;
    }
    _calculate(token, tokenExpiresAt) {
      const now = Date.now();
      try {
        if (!token || !tokenExpiresAt) {
          return exports.TokenStatusEnum.UNKNOWN;
        }
      } catch (error) {
        return exports.TokenStatusEnum.UNKNOWN;
      }
      const timeSlackMillis = 500;
      tokenExpiresAt -= timeSlackMillis;
      if (now < tokenExpiresAt) {
        return exports.TokenStatusEnum.VALID;
      }
      return exports.TokenStatusEnum.EXPIRED;
    }
  }
  
  class RefreshToken {
    constructor(scheme, storage) {
      this.scheme = scheme;
      this.$storage = storage;
    }
    get() {
      const _key = this.scheme.options.refreshToken.prefix + this.scheme.name;
      return this.$storage.getUniversal(_key);
    }
    set(tokenValue) {
      const refreshToken = addTokenPrefix(tokenValue, this.scheme.options.refreshToken.type);
      this._setToken(refreshToken);
      this._updateExpiration(refreshToken);
      return refreshToken;
    }
    sync() {
      const refreshToken = this._syncToken();
      this._syncExpiration();
      return refreshToken;
    }
    reset() {
      this._setToken(false);
      this._setExpiration(false);
    }
    status() {
      return new TokenStatus(this.get(), this._getExpiration());
    }
    _getExpiration() {
      const _key = this.scheme.options.refreshToken.expirationPrefix + this.scheme.name;
      return this.$storage.getUniversal(_key);
    }
    _setExpiration(expiration) {
      const _key = this.scheme.options.refreshToken.expirationPrefix + this.scheme.name;
      return this.$storage.setUniversal(_key, expiration);
    }
    _syncExpiration() {
      const _key = this.scheme.options.refreshToken.expirationPrefix + this.scheme.name;
      return this.$storage.syncUniversal(_key);
    }
    _updateExpiration(refreshToken) {
      let refreshTokenExpiration;
      const _tokenIssuedAtMillis = Date.now();
      const _tokenTTLMillis = Number(this.scheme.options.refreshToken.maxAge) * 1e3;
      const _tokenExpiresAtMillis = _tokenTTLMillis ? _tokenIssuedAtMillis + _tokenTTLMillis : 0;
      try {
        refreshTokenExpiration = jwtDecode__default['default'](refreshToken + "").exp * 1e3 || _tokenExpiresAtMillis;
      } catch (error) {
        refreshTokenExpiration = _tokenExpiresAtMillis;
        if (!(error && error.name === "InvalidTokenError")) {
          throw error;
        }
      }
      return this._setExpiration(refreshTokenExpiration || false);
    }
    _setToken(refreshToken) {
      const _key = this.scheme.options.refreshToken.prefix + this.scheme.name;
      return this.$storage.setUniversal(_key, refreshToken);
    }
    _syncToken() {
      const _key = this.scheme.options.refreshToken.prefix + this.scheme.name;
      return this.$storage.syncUniversal(_key);
    }
  }
  
  class RequestHandler {
    constructor(scheme, axios) {
      this.scheme = scheme;
      this.axios = axios;
      this.interceptor = null;
    }
    setHeader(token) {
      if (this.scheme.options.token.global) {
        this.axios.setHeader(this.scheme.options.token.name, token);
      }
    }
    clearHeader() {
      if (this.scheme.options.token.global) {
        this.axios.setHeader(this.scheme.options.token.name, false);
      }
    }
    initializeRequestInterceptor(refreshEndpoint) {
      this.interceptor = this.axios.interceptors.request.use(async (config) => {
        if (!this._needToken(config) || config.url === refreshEndpoint) {
          return config;
        }
        const {
          valid,
          tokenExpired,
          refreshTokenExpired,
          isRefreshable
        } = this.scheme.check(true);
        let isValid = valid;
        if (refreshTokenExpired) {
          this.scheme.reset();
          throw new ExpiredAuthSessionError();
        }
        if (tokenExpired) {
          if (!isRefreshable) {
            this.scheme.reset();
            throw new ExpiredAuthSessionError();
          }
          isValid = await this.scheme.refreshTokens().then(() => true).catch(() => {
            this.scheme.reset();
            throw new ExpiredAuthSessionError();
          });
        }
        const token = this.scheme.token.get();
        if (!isValid) {
          if (!token && this._requestHasAuthorizationHeader(config)) {
            throw new ExpiredAuthSessionError();
          }
          return config;
        }
        return this._getUpdatedRequestConfig(config, token);
      });
    }
    reset() {
      this.axios.interceptors.request.eject(this.interceptor);
      this.interceptor = null;
    }
    _needToken(config) {
      const options = this.scheme.options;
      return options.token.global || Object.values(options.endpoints).some((endpoint) => typeof endpoint === "object" ? endpoint.url === config.url : endpoint === config.url);
    }
    _getUpdatedRequestConfig(config, token) {
      if (typeof token === "string") {
        config.headers[this.scheme.options.token.name] = token;
      }
      return config;
    }
    _requestHasAuthorizationHeader(config) {
      return !!config.headers.common[this.scheme.options.token.name];
    }
  }
  
  class Token {
    constructor(scheme, storage) {
      this.scheme = scheme;
      this.$storage = storage;
    }
    get() {
      const _key = this.scheme.options.token.prefix + this.scheme.name;
      return this.$storage.getUniversal(_key);
    }
    set(tokenValue) {
      const token = addTokenPrefix(tokenValue, this.scheme.options.token.type);
      this._setToken(token);
      this._updateExpiration(token);
      if (typeof token === "string") {
        this.scheme.requestHandler.setHeader(token);
      }
      return token;
    }
    sync() {
      const token = this._syncToken();
      this._syncExpiration();
      if (typeof token === "string") {
        this.scheme.requestHandler.setHeader(token);
      }
      return token;
    }
    reset() {
      this.scheme.requestHandler.clearHeader();
      this._setToken(false);
      this._setExpiration(false);
    }
    status() {
      return new TokenStatus(this.get(), this._getExpiration());
    }
    _getExpiration() {
      const _key = this.scheme.options.token.expirationPrefix + this.scheme.name;
      return this.$storage.getUniversal(_key);
    }
    _setExpiration(expiration) {
      const _key = this.scheme.options.token.expirationPrefix + this.scheme.name;
      return this.$storage.setUniversal(_key, expiration);
    }
    _syncExpiration() {
      const _key = this.scheme.options.token.expirationPrefix + this.scheme.name;
      return this.$storage.syncUniversal(_key);
    }
    _updateExpiration(token) {
      let tokenExpiration;
      const _tokenIssuedAtMillis = Date.now();
      const _tokenTTLMillis = Number(this.scheme.options.token.maxAge) * 1e3;
      const _tokenExpiresAtMillis = _tokenTTLMillis ? _tokenIssuedAtMillis + _tokenTTLMillis : 0;
      try {
        tokenExpiration = jwtDecode__default['default'](token + "").exp * 1e3 || _tokenExpiresAtMillis;
      } catch (error) {
        tokenExpiration = _tokenExpiresAtMillis;
        if (!(error && error.name === "InvalidTokenError")) {
          throw error;
        }
      }
      return this._setExpiration(tokenExpiration || false);
    }
    _setToken(token) {
      const _key = this.scheme.options.token.prefix + this.scheme.name;
      return this.$storage.setUniversal(_key, token);
    }
    _syncToken() {
      const _key = this.scheme.options.token.prefix + this.scheme.name;
      return this.$storage.syncUniversal(_key);
    }
  }
  
  class BaseScheme {
    constructor($auth, ...options) {
      this.$auth = $auth;
      this.options = options.reduce((p, c) => defu2__default['default'](p, c), {});
    }
    get name() {
      return this.options.name;
    }
  }
  
  const DEFAULTS$3 = {
    name: "local",
    endpoints: {
      login: {
        url: "/api/auth/login",
        method: "post"
      },
      logout: {
        url: "/api/auth/logout",
        method: "post"
      },
      user: {
        url: "/api/auth/user",
        method: "get"
      }
    },
    token: {
      property: "token",
      type: "Bearer",
      name: "Authorization",
      maxAge: 1800,
      global: true,
      required: true,
      prefix: "_token.",
      expirationPrefix: "_token_expiration."
    },
    user: {
      property: "user",
      autoFetch: true
    },
    clientId: false,
    grantType: false,
    scope: false
  };
  class LocalScheme extends BaseScheme {
    constructor($auth, options, ...defaults) {
      super($auth, options, ...defaults, DEFAULTS$3);
      this.token = new Token(this, this.$auth.$storage);
      this.requestHandler = new RequestHandler(this, this.$auth.ctx.$axios);
    }
    check(checkStatus = false) {
      const response = {
        valid: false,
        tokenExpired: false
      };
      const token = this.token.sync();
      if (!token) {
        return response;
      }
      if (!checkStatus) {
        response.valid = true;
        return response;
      }
      const tokenStatus = this.token.status();
      if (tokenStatus.expired()) {
        response.tokenExpired = true;
        return response;
      }
      response.valid = true;
      return response;
    }
    mounted({
      tokenCallback = () => this.$auth.reset(),
      refreshTokenCallback = void 0
    } = {}) {
      const {tokenExpired, refreshTokenExpired} = this.check(true);
      if (refreshTokenExpired && typeof refreshTokenCallback === "function") {
        refreshTokenCallback();
      } else if (tokenExpired && typeof tokenCallback === "function") {
        tokenCallback();
      }
      this.initializeRequestInterceptor();
      return this.$auth.fetchUserOnce();
    }
    async login(endpoint, {reset = true} = {}) {
      if (!this.options.endpoints.login) {
        return;
      }
      if (reset) {
        this.$auth.reset({resetInterceptor: false});
      }
      if (this.options.clientId) {
        endpoint.data.client_id = this.options.clientId;
      }
      if (this.options.grantType) {
        endpoint.data.grant_type = this.options.grantType;
      }
      if (this.options.scope) {
        endpoint.data.scope = this.options.scope;
      }
      const response = await this.$auth.request(endpoint, this.options.endpoints.login);
      this.updateTokens(response);
      if (!this.requestHandler.interceptor) {
        this.initializeRequestInterceptor();
      }
      if (this.options.user.autoFetch) {
        await this.fetchUser();
      }
      return response;
    }
    setUserToken(token) {
      this.token.set(token);
      return this.fetchUser();
    }
    fetchUser(endpoint) {
      if (!this.check().valid) {
        return Promise.resolve();
      }
      if (!this.options.endpoints.user) {
        this.$auth.setUser({});
        return Promise.resolve();
      }
      return this.$auth.requestWith(this.name, endpoint, this.options.endpoints.user).then((response) => {
        const userData = getProp(response.data, this.options.user.property);
        if (!userData) {
          const error = new Error(`User Data response does not contain field ${this.options.user.property}`);
          return Promise.reject(error);
        }
        this.$auth.setUser(userData);
        return response;
      }).catch((error) => {
        this.$auth.callOnError(error, {method: "fetchUser"});
        return Promise.reject(error);
      });
    }
    async logout(endpoint = {}) {
      if (this.options.endpoints.logout) {
        await this.$auth.requestWith(this.name, endpoint, this.options.endpoints.logout).catch(() => {
        });
      }
      return this.$auth.reset();
    }
    reset({resetInterceptor = true} = {}) {
      this.$auth.setUser(false);
      this.token.reset();
      if (resetInterceptor) {
        this.requestHandler.reset();
      }
    }
    updateTokens(response) {
      const token = this.options.token.required ? getProp(response.data, this.options.token.property) : true;
      this.token.set(token);
    }
    initializeRequestInterceptor() {
      this.requestHandler.initializeRequestInterceptor();
    }
  }
  
  const DEFAULTS$2 = {
    name: "cookie",
    cookie: {
      name: null
    },
    token: {
      type: "",
      property: "",
      maxAge: false,
      global: false,
      required: false
    },
    endpoints: {
      csrf: null
    }
  };
  class CookieScheme extends LocalScheme {
    constructor($auth, options) {
      super($auth, options, DEFAULTS$2);
    }
    mounted() {
      if (process.server) {
        this.$auth.ctx.$axios.setHeader("referer", this.$auth.ctx.req.headers.host);
      }
      return super.mounted();
    }
    check() {
      const response = {valid: false};
      if (!super.check().valid) {
        return response;
      }
      if (this.options.cookie.name) {
        const cookies = this.$auth.$storage.getCookies();
        response.valid = Boolean(cookies[this.options.cookie.name]);
        return response;
      }
      response.valid = true;
      return response;
    }
    async login(endpoint) {
      this.$auth.reset();
      if (this.options.endpoints.csrf) {
        await this.$auth.request(this.options.endpoints.csrf, {
          maxRedirects: 0
        });
      }
      return super.login(endpoint, {reset: false});
    }
    reset() {
      if (this.options.cookie.name) {
        this.$auth.$storage.setCookie(this.options.cookie.name, null, {
          prefix: ""
        });
      }
      return super.reset();
    }
  }
  
  const DEFAULTS$1 = {
    name: "oauth2",
    accessType: null,
    redirectUri: null,
    logoutRedirectUri: null,
    clientId: null,
    audience: null,
    grantType: null,
    responseMode: null,
    acrValues: null,
    autoLogout: false,
    endpoints: {
      logout: "",
      authorization: "",
      token: "",
      userInfo: ""
    },
    scope: [],
    token: {
      property: "access_token",
      type: "Bearer",
      name: "Authorization",
      maxAge: 1800,
      global: true,
      prefix: "_token.",
      expirationPrefix: "_token_expiration."
    },
    refreshToken: {
      property: "refresh_token",
      maxAge: 60 * 60 * 24 * 30,
      prefix: "_refresh_token.",
      expirationPrefix: "_refresh_token_expiration."
    },
    user: {
      property: false
    },
    responseType: "token",
    codeChallengeMethod: "implicit"
  };
  class Oauth2Scheme extends BaseScheme {
    constructor($auth, options, ...defaults) {
      super($auth, options, ...defaults, DEFAULTS$1);
      this.req = $auth.ctx.req;
      this.token = new Token(this, this.$auth.$storage);
      this.refreshToken = new RefreshToken(this, this.$auth.$storage);
      this.refreshController = new RefreshController(this);
      this.requestHandler = new RequestHandler(this, this.$auth.ctx.$axios);
    }
    get scope() {
      return Array.isArray(this.options.scope) ? this.options.scope.join(" ") : this.options.scope;
    }
    get redirectURI() {
      const basePath = this.$auth.ctx.base || "";
      const path = basePath + this.$auth.options.redirect.callback;
      return this.options.redirectUri || urlJoin(requrl2__default['default'](this.req), path);
    }
    get logoutRedirectURI() {
      const basePath = this.$auth.ctx.base || "";
      const path = basePath + this.$auth.options.redirect.logout;
      return this.options.logoutRedirectUri || urlJoin(requrl2__default['default'](this.req), path);
    }
    check(checkStatus = false) {
      const response = {
        valid: false,
        tokenExpired: false,
        refreshTokenExpired: false,
        isRefreshable: true
      };
      const token = this.token.sync();
      this.refreshToken.sync();
      if (!token) {
        return response;
      }
      if (!checkStatus) {
        response.valid = true;
        return response;
      }
      const tokenStatus = this.token.status();
      const refreshTokenStatus = this.refreshToken.status();
      if (refreshTokenStatus.expired()) {
        response.refreshTokenExpired = true;
        return response;
      }
      if (tokenStatus.expired()) {
        response.tokenExpired = true;
        return response;
      }
      response.valid = true;
      return response;
    }
    async mounted() {
      const {tokenExpired, refreshTokenExpired} = this.check(true);
      if (refreshTokenExpired || tokenExpired && this.options.autoLogout) {
        this.$auth.reset();
      }
      this.requestHandler.initializeRequestInterceptor(this.options.endpoints.token);
      const redirected = await this._handleCallback();
      if (!redirected) {
        return this.$auth.fetchUserOnce();
      }
    }
    reset() {
      this.$auth.setUser(false);
      this.token.reset();
      this.refreshToken.reset();
      this.requestHandler.reset();
    }
    async login(_opts = {}) {
      const opts = {
        protocol: "oauth2",
        response_type: this.options.responseType,
        access_type: this.options.accessType,
        client_id: this.options.clientId,
        redirect_uri: this.redirectURI,
        scope: this.scope,
        state: _opts.state || randomString(10),
        code_challenge_method: this.options.codeChallengeMethod,
        ..._opts.params
      };
      if (this.options.audience) {
        opts.audience = this.options.audience;
      }
      if (opts.response_type.includes("token")) {
        opts.nonce = _opts.nonce || randomString(10);
      }
      if (opts.code_challenge_method) {
        switch (opts.code_challenge_method) {
          case "plain":
          case "S256":
            {
              const state = this.generateRandomString();
              this.$auth.$storage.setUniversal(this.name + ".pkce_state", state);
              const codeVerifier = this.generateRandomString();
              this.$auth.$storage.setUniversal(this.name + ".pkce_code_verifier", codeVerifier);
              const codeChallenge = await this.pkceChallengeFromVerifier(codeVerifier, opts.code_challenge_method === "S256");
              opts.code_challenge = window.encodeURIComponent(codeChallenge);
            }
            break;
        }
      }
      if (this.options.responseMode) {
        opts.response_mode = this.options.responseMode;
      }
      if (this.options.acrValues) {
        opts.acr_values = this.options.acrValues;
      }
      this.$auth.$storage.setUniversal(this.name + ".state", opts.state);
      const url = this.options.endpoints.authorization + "?" + encodeQuery(opts);
      window.location.replace(url);
    }
    logout() {
      if (this.options.endpoints.logout) {
        const opts = {
          client_id: this.options.clientId + "",
          post_logout_redirect_uri: this.logoutRedirectURI
        };
        const url = this.options.endpoints.logout + "?" + encodeQuery(opts);
        window.location.replace(url);
      }
      return this.$auth.reset();
    }
    async fetchUser() {
      if (!this.check().valid) {
        return;
      }
      if (!this.options.endpoints.userInfo) {
        this.$auth.setUser({});
        return;
      }
      const response = await this.$auth.requestWith(this.name, {
        url: this.options.endpoints.userInfo
      });
      this.$auth.setUser(getProp(response.data, this.options.user.property));
    }
    async _handleCallback() {
      if (this.$auth.options.redirect && normalizePath(this.$auth.ctx.route.path, this.$auth.ctx) !== normalizePath(this.$auth.options.redirect.callback, this.$auth.ctx)) {
        return;
      }
      if (process.server) {
        return;
      }
      const hash = parseQuery(this.$auth.ctx.route.hash.substr(1));
      const parsedQuery = Object.assign({}, this.$auth.ctx.route.query, hash);
      let token = parsedQuery[this.options.token.property];
      let refreshToken;
      if (this.options.refreshToken.property) {
        refreshToken = parsedQuery[this.options.refreshToken.property];
      }
      const state = this.$auth.$storage.getUniversal(this.name + ".state");
      this.$auth.$storage.setUniversal(this.name + ".state", null);
      if (state && parsedQuery.state !== state) {
        return;
      }
      if (this.options.responseType === "code" && parsedQuery.code) {
        let codeVerifier;
        if (this.options.codeChallengeMethod && this.options.codeChallengeMethod !== "implicit") {
          codeVerifier = this.$auth.$storage.getUniversal(this.name + ".pkce_code_verifier");
          this.$auth.$storage.setUniversal(this.name + ".pkce_code_verifier", null);
        }
        const response = await this.$auth.request({
          method: "post",
          url: this.options.endpoints.token,
          baseURL: "",
          data: encodeQuery({
            code: parsedQuery.code,
            client_id: this.options.clientId + "",
            redirect_uri: this.redirectURI,
            response_type: this.options.responseType,
            audience: this.options.audience,
            grant_type: this.options.grantType,
            code_verifier: codeVerifier
          })
        });
        token = getProp(response.data, this.options.token.property) || token;
        refreshToken = getProp(response.data, this.options.refreshToken.property) || refreshToken;
      }
      if (!token || !token.length) {
        return;
      }
      this.token.set(token);
      if (refreshToken && refreshToken.length) {
        this.refreshToken.set(refreshToken);
      }
      this.$auth.redirect("home", true);
      return true;
    }
    async refreshTokens() {
      const refreshToken = this.refreshToken.get();
      if (!refreshToken) {
        return;
      }
      const refreshTokenStatus = this.refreshToken.status();
      if (refreshTokenStatus.expired()) {
        this.$auth.reset();
        throw new ExpiredAuthSessionError();
      }
      this.requestHandler.clearHeader();
      const response = await this.$auth.request({
        method: "post",
        url: this.options.endpoints.token,
        baseURL: "",
        headers: {
          "Content-Type": "application/x-www-form-urlencoded"
        },
        data: encodeQuery({
          refresh_token: removeTokenPrefix(refreshToken, this.options.token.type),
          client_id: this.options.clientId + "",
          grant_type: "refresh_token"
        })
      }).catch((error) => {
        this.$auth.callOnError(error, {method: "refreshToken"});
        return Promise.reject(error);
      });
      this.updateTokens(response);
      return response;
    }
    updateTokens(response) {
      const token = getProp(response.data, this.options.token.property);
      const refreshToken = getProp(response.data, this.options.refreshToken.property);
      this.token.set(token);
      if (refreshToken) {
        this.refreshToken.set(refreshToken);
      }
    }
    async pkceChallengeFromVerifier(v, hashValue) {
      if (hashValue) {
        const hashed = await this._sha256(v);
        return this._base64UrlEncode(hashed);
      }
      return v;
    }
    generateRandomString() {
      const array = new Uint32Array(28);
      window.crypto.getRandomValues(array);
      return Array.from(array, (dec) => ("0" + dec.toString(16)).substr(-2)).join("");
    }
    _sha256(plain) {
      const encoder = new TextEncoder();
      const data = encoder.encode(plain);
      return window.crypto.subtle.digest("SHA-256", data);
    }
    _base64UrlEncode(str) {
      return btoa(String.fromCharCode.apply(null, new Uint8Array(str))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
    }
  }
  
  const DEFAULTS = {
    name: "refresh",
    endpoints: {
      refresh: {
        url: "/api/auth/refresh",
        method: "post"
      }
    },
    refreshToken: {
      property: "refresh_token",
      data: "refresh_token",
      maxAge: 60 * 60 * 24 * 30,
      required: true,
      tokenRequired: false,
      prefix: "_refresh_token.",
      expirationPrefix: "_refresh_token_expiration."
    },
    autoLogout: false
  };
  class RefreshScheme extends LocalScheme {
    constructor($auth, options) {
      super($auth, options, DEFAULTS);
      this.refreshToken = new RefreshToken(this, this.$auth.$storage);
      this.refreshController = new RefreshController(this);
    }
    check(checkStatus = false) {
      const response = {
        valid: false,
        tokenExpired: false,
        refreshTokenExpired: false,
        isRefreshable: true
      };
      const token = this.token.sync();
      const refreshToken = this.refreshToken.sync();
      if (!token || !refreshToken) {
        return response;
      }
      if (!checkStatus) {
        response.valid = true;
        return response;
      }
      const tokenStatus = this.token.status();
      const refreshTokenStatus = this.refreshToken.status();
      if (refreshTokenStatus.expired()) {
        response.refreshTokenExpired = true;
        return response;
      }
      if (tokenStatus.expired()) {
        response.tokenExpired = true;
        return response;
      }
      response.valid = true;
      return response;
    }
    mounted() {
      return super.mounted({
        tokenCallback: () => {
          if (this.options.autoLogout) {
            this.$auth.reset();
          }
        },
        refreshTokenCallback: () => {
          this.$auth.reset();
        }
      });
    }
    refreshTokens() {
      if (!this.options.endpoints.refresh) {
        return Promise.resolve();
      }
      if (!this.check().valid) {
        return Promise.resolve();
      }
      const refreshTokenStatus = this.refreshToken.status();
      if (refreshTokenStatus.expired()) {
        this.$auth.reset();
        throw new ExpiredAuthSessionError();
      }
      if (!this.options.refreshToken.tokenRequired) {
        this.requestHandler.clearHeader();
      }
      const endpoint = {
        data: {
          client_id: void 0,
          grant_type: void 0
        }
      };
      if (this.options.refreshToken.required && this.options.refreshToken.data) {
        endpoint.data[this.options.refreshToken.data] = this.refreshToken.get();
      }
      if (this.options.clientId) {
        endpoint.data.client_id = this.options.clientId;
      }
      if (this.options.grantType) {
        endpoint.data.grant_type = "refresh_token";
      }
      cleanObj(endpoint.data);
      return this.$auth.request(endpoint, this.options.endpoints.refresh).then((response) => {
        this.updateTokens(response, {isRefreshing: true});
        return response;
      }).catch((error) => {
        this.$auth.callOnError(error, {method: "refreshToken"});
        return Promise.reject(error);
      });
    }
    setUserToken(token, refreshToken) {
      this.token.set(token);
      if (refreshToken) {
        this.refreshToken.set(refreshToken);
      }
      return this.fetchUser();
    }
    reset({resetInterceptor = true} = {}) {
      this.$auth.setUser(false);
      this.token.reset();
      this.refreshToken.reset();
      if (resetInterceptor) {
        this.requestHandler.reset();
      }
    }
    updateTokens(response, {isRefreshing = false, updateOnRefresh = true} = {}) {
      const token = this.options.token.required ? getProp(response.data, this.options.token.property) : true;
      const refreshToken = this.options.refreshToken.required ? getProp(response.data, this.options.refreshToken.property) : true;
      this.token.set(token);
      if (refreshToken && (!isRefreshing || isRefreshing && updateOnRefresh)) {
        this.refreshToken.set(refreshToken);
      }
    }
    initializeRequestInterceptor() {
      this.requestHandler.initializeRequestInterceptor(this.options.endpoints.refresh.url);
    }
  }
  
  class Auth0Scheme extends Oauth2Scheme {
    logout() {
      this.$auth.reset();
      const opts = {
        client_id: this.options.clientId + "",
        returnTo: this.logoutRedirectURI
      };
      const url = this.options.endpoints.logout + "?" + encodeQuery(opts);
      window.location.replace(url);
    }
  }
  
  class LaravelJWTScheme extends RefreshScheme {
    updateTokens(response, {isRefreshing = false, updateOnRefresh = false} = {}) {
      super.updateTokens(response, {isRefreshing, updateOnRefresh});
    }
  }
  
  exports.Auth = Auth;
  exports.Auth0Scheme = Auth0Scheme;
  exports.BaseScheme = BaseScheme;
  exports.CookieScheme = CookieScheme;
  exports.ExpiredAuthSessionError = ExpiredAuthSessionError;
  exports.LaravelJWTScheme = LaravelJWTScheme;
  exports.LocalScheme = LocalScheme;
  exports.Oauth2Scheme = Oauth2Scheme;
  exports.RefreshController = RefreshController;
  exports.RefreshScheme = RefreshScheme;
  exports.RefreshToken = RefreshToken;
  exports.RequestHandler = RequestHandler;
  exports.Storage = Storage;
  exports.Token = Token;
  exports.TokenStatus = TokenStatus;
  exports.authMiddleware = authMiddleware;
  