@nuxtjs/auth
Advanced tools
+336
| import getProp from 'dotprop' | ||
| import Storage from './storage' | ||
| import { routeOption, isRelativeURL, isSet, isSameURL } from './utilities' | ||
| export default class Auth { | ||
| constructor (ctx, options) { | ||
| this.ctx = ctx | ||
| this.options = options | ||
| // Strategies | ||
| this.strategies = {} | ||
| // Error listeners | ||
| this._errorListeners = [] | ||
| // Storage & State | ||
| options.initialState = { user: null, loggedIn: false } | ||
| const storage = new Storage(ctx, options) | ||
| this.$storage = storage | ||
| this.$state = storage.state | ||
| } | ||
| init () { | ||
| // Watch for loggedIn changes only in client side | ||
| if (process.browser) { | ||
| this.$storage.watchState('loggedIn', loggedIn => { | ||
| if (!routeOption(this.ctx.route, 'auth', false)) { | ||
| this.redirect(loggedIn ? 'home' : 'logout') | ||
| } | ||
| }) | ||
| } | ||
| // Restore strategy | ||
| this.$storage.syncUniversal('strategy', this.options.defaultStrategy) | ||
| // Set default strategy if current one is invalid | ||
| if (!this.strategy) { | ||
| this.$storage.setUniversal('strategy', this.options.defaultStrategy) | ||
| // Give up if still invalid | ||
| if (!this.strategy) { | ||
| return Promise.resolve() | ||
| } | ||
| } | ||
| // Call mounted for active strategy on initial load | ||
| return this.mounted() | ||
| } | ||
| // Backward compatibility | ||
| get state () { | ||
| if (!this._state_warn_shown) { | ||
| this._state_warn_shown = true | ||
| // eslint-disable-next-line no-console | ||
| console.warn('[AUTH] $auth.state is deprecated. Please use $auth.$state or top level props like $auth.loggedIn') | ||
| } | ||
| return this.$state | ||
| } | ||
| getState (key) { | ||
| if (!this._get_state_warn_shown) { | ||
| this._get_state_warn_shown = true | ||
| // eslint-disable-next-line no-console | ||
| console.warn('[AUTH] $auth.getState is deprecated. Please use $auth.$storage.getState() or top level props like $auth.loggedIn') | ||
| } | ||
| return this.$storage.getState(key) | ||
| } | ||
| // --------------------------------------------------------------- | ||
| // Strategy and Scheme | ||
| // --------------------------------------------------------------- | ||
| get strategy () { | ||
| return this.strategies[this.$state.strategy] | ||
| } | ||
| registerStrategy (name, strategy) { | ||
| this.strategies[name] = strategy | ||
| } | ||
| setStrategy (name) { | ||
| if (name === this.$storage.getUniversal('strategy')) { | ||
| return Promise.resolve() | ||
| } | ||
| // Set strategy | ||
| this.$storage.setUniversal('strategy', name) | ||
| // Call mounted hook on active strategy | ||
| return this.mounted() | ||
| } | ||
| mounted () { | ||
| if (!this.strategy.mounted) { | ||
| return this.fetchUserOnce() | ||
| } | ||
| return Promise.resolve(this.strategy.mounted(...arguments)).catch(error => { | ||
| this.callOnError(error, { method: 'mounted' }) | ||
| return Promise.reject(error) | ||
| }) | ||
| } | ||
| loginWith (name, ...args) { | ||
| return this.setStrategy(name).then(() => this.login(...args)) | ||
| } | ||
| login () { | ||
| if (!this.strategy.login) { | ||
| return Promise.resolve() | ||
| } | ||
| return this.wrapLogin(this.strategy.login(...arguments)).catch(error => { | ||
| this.callOnError(error, { method: 'login' }) | ||
| return Promise.reject(error) | ||
| }) | ||
| } | ||
| fetchUser () { | ||
| if (!this.strategy.fetchUser) { | ||
| return Promise.resolve() | ||
| } | ||
| return Promise.resolve(this.strategy.fetchUser(...arguments)).catch(error => { | ||
| this.callOnError(error, { method: 'fetchUser' }) | ||
| return Promise.reject(error) | ||
| }) | ||
| } | ||
| logout () { | ||
| if (!this.strategy.logout) { | ||
| this.reset() | ||
| return Promise.resolve() | ||
| } | ||
| return Promise.resolve(this.strategy.logout(...arguments)).catch(error => { | ||
| this.callOnError(error, { method: 'logout' }) | ||
| return Promise.reject(error) | ||
| }) | ||
| } | ||
| reset () { | ||
| if (!this.strategy.reset) { | ||
| this.setUser(null) | ||
| this.setToken(this.$state.strategy, null) | ||
| return Promise.resolve() | ||
| } | ||
| return Promise.resolve(this.strategy.reset(...arguments)).catch(error => { | ||
| this.callOnError(error, { method: 'reset' }) | ||
| return Promise.reject(error) | ||
| }) | ||
| } | ||
| // --------------------------------------------------------------- | ||
| // Token helpers | ||
| // --------------------------------------------------------------- | ||
| getToken (strategy) { | ||
| const _key = this.options.token.prefix + strategy | ||
| return this.$storage.getUniversal(_key) | ||
| } | ||
| setToken (strategy, token) { | ||
| const _key = this.options.token.prefix + strategy | ||
| return this.$storage.setUniversal(_key, token) | ||
| } | ||
| syncToken (strategy) { | ||
| const _key = this.options.token.prefix + strategy | ||
| return this.$storage.syncUniversal(_key) | ||
| } | ||
| // --------------------------------------------------------------- | ||
| // User helpers | ||
| // --------------------------------------------------------------- | ||
| get user () { | ||
| return this.$state.user | ||
| } | ||
| get loggedIn () { | ||
| return this.$state.loggedIn | ||
| } | ||
| fetchUserOnce () { | ||
| if (!this.$state.user) { | ||
| return this.fetchUser(...arguments) | ||
| } | ||
| return Promise.resolve() | ||
| } | ||
| setUser (user) { | ||
| this.$storage.setState('loggedIn', Boolean(user)) | ||
| this.$storage.setState('user', user) | ||
| } | ||
| // --------------------------------------------------------------- | ||
| // Utils | ||
| // --------------------------------------------------------------- | ||
| get busy () { | ||
| return this.$storage.getState('busy') | ||
| } | ||
| request (endpoint, defaults) { | ||
| const _endpoint = | ||
| typeof defaults === 'object' | ||
| ? Object.assign({}, defaults, endpoint) | ||
| : endpoint | ||
| return this.ctx.app.$axios | ||
| .request(_endpoint) | ||
| .then(response => { | ||
| if (_endpoint.propertyName) { | ||
| return getProp(response.data, _endpoint.propertyName) | ||
| } else { | ||
| return response.data | ||
| } | ||
| }) | ||
| .catch(error => { | ||
| // Call all error handlers | ||
| this.callOnError(error, { method: 'request' }) | ||
| // Throw error | ||
| return Promise.reject(error) | ||
| }) | ||
| } | ||
| requestWith (strategy, endpoint, defaults) { | ||
| const token = this.getToken(strategy) | ||
| const _endpoint = Object.assign({}, defaults, endpoint) | ||
| if (!_endpoint.headers) { | ||
| _endpoint.headers = {} | ||
| } | ||
| if (!_endpoint.headers['Authorization'] && isSet(token)) { | ||
| _endpoint.headers['Authorization'] = token | ||
| } | ||
| return this.request(_endpoint) | ||
| } | ||
| wrapLogin (promise) { | ||
| this.$storage.setState('busy', true) | ||
| this.error = null | ||
| return Promise.resolve(promise) | ||
| .then(() => { | ||
| this.$storage.setState('busy', false) | ||
| }) | ||
| .catch(error => { | ||
| this.$storage.setState('busy', false) | ||
| return Promise.reject(error) | ||
| }) | ||
| } | ||
| onError (listener) { | ||
| this._errorListeners.push(listener) | ||
| } | ||
| callOnError (error, payload = {}) { | ||
| this.error = error | ||
| for (let fn of this._errorListeners) { | ||
| fn(error, payload) | ||
| } | ||
| } | ||
| redirect (name, noRouter = false) { | ||
| if (!this.options.redirect) { | ||
| return | ||
| } | ||
| const from = this.options.fullPathRedirect ? this.ctx.route.path : this.ctx.route.fullPath | ||
| let to = this.options.redirect[name] | ||
| if (!to) { | ||
| return | ||
| } | ||
| // Apply rewrites | ||
| if (this.options.rewriteRedirects) { | ||
| if (name === 'login' && isRelativeURL(from) && !isSameURL(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 | ||
| } | ||
| } | ||
| } | ||
| // Prevent infinity redirects | ||
| if (isSameURL(to, from)) { | ||
| return | ||
| } | ||
| if (process.browser) { | ||
| if (noRouter) { | ||
| window.location.replace(to) | ||
| } else { | ||
| this.ctx.redirect(to) | ||
| } | ||
| } else { | ||
| this.ctx.redirect(to) | ||
| } | ||
| } | ||
| hasScope (scope) { | ||
| const userScopes = this.$state.user && getProp(this.$state.user, this.options.scopeKey) | ||
| if (!userScopes) { | ||
| return undefined | ||
| } | ||
| if (Array.isArray(userScopes)) { | ||
| return userScopes.includes(scope) | ||
| } | ||
| return Boolean(getProp(userScopes, scope)) | ||
| } | ||
| } |
| import Middleware from '../middleware' | ||
| import { routeOption } from './utilities' | ||
| Middleware.auth = function (ctx) { | ||
| // Disable middleware if options: { auth: false } is set on the route | ||
| if (routeOption(ctx.route, 'auth', false)) { | ||
| return | ||
| } | ||
| const { login } = ctx.app.$auth.options.redirect | ||
| if (ctx.app.$auth.$state.loggedIn) { | ||
| // -- Authorized -- | ||
| // Redirect to home page if inside login page | ||
| if (login && ctx.route.path === login.split('?')[0]) { | ||
| ctx.app.$auth.redirect('home') | ||
| } | ||
| } else { | ||
| // -- Guest -- | ||
| // Redirect to login path if not authorized | ||
| ctx.app.$auth.redirect('login') | ||
| } | ||
| } |
| import Vue from 'vue' | ||
| import Cookies from 'js-cookie' | ||
| import getProp from 'dotprop' | ||
| import { parse as parseCookie } from 'cookie' | ||
| import { isUnset, isSet } from './utilities' | ||
| export default class Storage { | ||
| constructor (ctx, options) { | ||
| this.ctx = ctx | ||
| this.options = options | ||
| this._initState() | ||
| } | ||
| // ------------------------------------ | ||
| // Universal | ||
| // ------------------------------------ | ||
| setUniversal (key, value, isJson) { | ||
| // Local state | ||
| this.setState(key, value) | ||
| // Cookies | ||
| this.setCookie(key, value) | ||
| // Local Storage | ||
| this.setLocalStorage(key, value, isJson) | ||
| return value | ||
| } | ||
| getUniversal (key, isJson) { | ||
| // Local state | ||
| let value = this.getState(key) | ||
| // Cookies | ||
| if (isUnset(value)) { | ||
| value = this.getCookie(key, isJson) | ||
| } | ||
| // Local Storage | ||
| if (isUnset(value)) { | ||
| value = this.getLocalStorage(key, isJson) | ||
| } | ||
| return value | ||
| } | ||
| syncUniversal (key, defaultValue, isJson) { | ||
| let value = this.getUniversal(key, isJson) | ||
| if (isUnset(value) && isSet(defaultValue)) { | ||
| value = defaultValue | ||
| } | ||
| if (isSet(value)) { | ||
| this.setUniversal(key, value) | ||
| } | ||
| return value | ||
| } | ||
| // ------------------------------------ | ||
| // Local state (reactive) | ||
| // ------------------------------------ | ||
| _initState () { | ||
| // Private state is suitable to keep information not being exposed to Vuex store | ||
| // This helps prevent stealing token from SSR response HTML | ||
| Vue.set(this, '_state', {}) | ||
| // Use vuex for local state's if possible | ||
| this._useVuex = this.options.vuex && this.ctx.store | ||
| if (this._useVuex) { | ||
| const storeModule = { | ||
| namespaced: true, | ||
| state: () => this.options.initialState, | ||
| mutations: { | ||
| SET (state, payload) { | ||
| Vue.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.set(this, 'state', {}) | ||
| } | ||
| } | ||
| setState (key, value) { | ||
| if (key[0] === '_') { | ||
| Vue.set(this._state, key, value) | ||
| } else { | ||
| if (this._useVuex) { | ||
| this.ctx.store.commit(this.options.vuex.namespace + '/SET', { | ||
| key, | ||
| value | ||
| }) | ||
| } else { | ||
| Vue.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 | ||
| ) | ||
| } | ||
| } | ||
| // ------------------------------------ | ||
| // Local storage | ||
| // ------------------------------------ | ||
| setLocalStorage (key, value, isJson) { | ||
| if (typeof localStorage === 'undefined' || !this.options.localStorage) { | ||
| return | ||
| } | ||
| const _key = this.options.localStorage.prefix + key | ||
| if (isUnset(value)) { | ||
| localStorage.removeItem(_key) | ||
| } else { | ||
| localStorage.setItem(_key, isJson ? JSON.stringify(value) : value) | ||
| } | ||
| return value | ||
| } | ||
| getLocalStorage (key, isJson) { | ||
| if (typeof localStorage === 'undefined' || !this.options.localStorage) { | ||
| return | ||
| } | ||
| const _key = this.options.localStorage.prefix + key | ||
| const value = localStorage.getItem(_key) | ||
| return isJson ? JSON.parse(value) : value | ||
| } | ||
| // ------------------------------------ | ||
| // Cookies | ||
| // ------------------------------------ | ||
| setCookie (key, value, options = {}) { | ||
| if (process.server || !this.options.cookie) { | ||
| return | ||
| } | ||
| const _key = this.options.cookie.prefix + key | ||
| const _options = Object.assign({}, this.options.cookie.options, options) | ||
| if (isUnset(value)) { | ||
| Cookies.remove(_key, _options) | ||
| } else { | ||
| Cookies.set(_key, value, _options) | ||
| } | ||
| return value | ||
| } | ||
| getCookie (key, isJson) { | ||
| if (!this.options.cookie) { | ||
| return | ||
| } | ||
| const _key = this.options.cookie.prefix + key | ||
| const cookieStr = process.browser | ||
| ? document.cookie | ||
| : this.ctx.req.headers.cookie | ||
| const cookies = parseCookie(cookieStr || '') || {} | ||
| const value = cookies[_key] | ||
| return isJson ? JSON.parse(value) : value | ||
| } | ||
| } |
| export const isUnset = o => typeof o === 'undefined' || o === null | ||
| export const isSet = o => !isUnset(o) | ||
| export const isSameURL = (a, b) => a.split('?')[0] === b.split('?')[0] | ||
| export const isRelativeURL = u => | ||
| u && u.length && /^\/[a-zA-Z0-9@\-%_~][/a-zA-Z0-9@\-%_~]{1,200}$/.test(u) | ||
| export const 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 | ||
| } | ||
| export const encodeQuery = queryObject => { | ||
| return Object.keys(queryObject) | ||
| .map( | ||
| key => | ||
| encodeURIComponent(key) + '=' + encodeURIComponent(queryObject[key]) | ||
| ) | ||
| .join('&') | ||
| } | ||
| export const randomString = () => btoa(Math.random() + '').replace('==', '') | ||
| export const routeOption = (route, key, value) => { | ||
| return route.matched.some(m => { | ||
| if (process.browser) { | ||
| // Browser | ||
| return Object.values(m.components).some( | ||
| component => component.options[key] === value | ||
| ) | ||
| } else { | ||
| // SSR | ||
| return Object.values(m.components).some(component => | ||
| Object.values(component._Ctor).some( | ||
| ctor => ctor.options && ctor.options[key] === value | ||
| ) | ||
| ) | ||
| } | ||
| }) | ||
| } |
+13
-0
@@ -5,2 +5,15 @@ # Change Log | ||
| <a name="4.0.1"></a> | ||
| ## [4.0.1](https://github.com/nuxt-community/auth-module/compare/v4.0.0...v4.0.1) (2018-04-03) | ||
| ### Bug Fixes | ||
| * **local-scheme-token:** avoid token type duplicata on Axios requests ([3908563](https://github.com/nuxt-community/auth-module/commit/3908563)) | ||
| * **local-scheme-token:** removed token type from axios setToken ([c64e7f1](https://github.com/nuxt-community/auth-module/commit/c64e7f1)), closes [#113](https://github.com/nuxt-community/auth-module/issues/113) | ||
| * **scheme-resolution:** fix problem with backslashes in path to schemes on windows ([77161b8](https://github.com/nuxt-community/auth-module/commit/77161b8)) | ||
| * no token exception when tokenRequired is set to false ([#118](https://github.com/nuxt-community/auth-module/issues/118)) ([56265a7](https://github.com/nuxt-community/auth-module/commit/56265a7)) | ||
| <a name="4.0.0"></a> | ||
@@ -7,0 +20,0 @@ # [4.0.0](https://github.com/nuxt-community/auth-module/compare/v4.0.0-rc.3...v4.0.0) (2018-04-02) |
@@ -63,3 +63,3 @@ const { resolve, join, basename } = require('path') | ||
| function copyCore (options) { | ||
| const coreRoot = resolve(libRoot, 'auth') | ||
| const coreRoot = resolve(libRoot, 'core') | ||
@@ -66,0 +66,0 @@ for (const file of readdirSync(coreRoot)) { |
@@ -6,3 +6,3 @@ import Auth from './auth' | ||
| // Active chemes | ||
| <%= options.uniqueSchemes.map(path =>`import ${'scheme_' + hash(path)} from '${path}'`).join('\n') %> | ||
| <%= options.uniqueSchemes.map(path =>`import ${'scheme_' + hash(path)} from '${path.replace(/\\/g,'/')}'`).join('\n') %> | ||
@@ -9,0 +9,0 @@ export default function (ctx, inject) { |
@@ -12,3 +12,3 @@ export default class LocalScheme { | ||
| // Set Authorization token for all axios requests | ||
| this.$auth.ctx.app.$axios.setToken(token, this.options.tokenType) | ||
| this.$auth.ctx.app.$axios.setToken(token) | ||
| } | ||
@@ -15,0 +15,0 @@ } |
+1
-1
| { | ||
| "name": "@nuxtjs/auth", | ||
| "version": "4.0.0", | ||
| "version": "4.0.1", | ||
| "description": "Authentication module for Nuxt.js", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
+10
-7
@@ -1,6 +0,4 @@ | ||
| # 🔑 Auth Module | ||
| <h1 align="center" >🔑 Auth Module</h1> | ||
| <p align="center">Authentication module for Nuxt.js</p> | ||
| > Authentication module for Nuxt.js | ||
| <p align="center"> | ||
@@ -28,6 +26,11 @@ <a href="https://david-dm.org/nuxt-community/auth-module"> | ||
| 📖 [**Read Documentation**](https://auth.nuxtjs.org) | ||
| <p align="center"> | ||
| <a href="https://auth.nuxtjs.org">Read Documentation</a> | ||
| </p> | ||
| **Running demo for development:** | ||
| ## Development | ||
| Running demo for development: | ||
| ```bash | ||
@@ -38,4 +41,4 @@ $ yarn install | ||
| ## 📑 License | ||
| ## License | ||
| [MIT License](./LICENSE) - Copyright (c) Nuxt Community |
-340
| import getProp from 'dotprop' | ||
| import Storage from './storage' | ||
| import { routeOption, isRelativeURL, isUnset, isSameURL } from './utilities' | ||
| export default class Auth { | ||
| constructor (ctx, options) { | ||
| this.ctx = ctx | ||
| this.options = options | ||
| // Strategies | ||
| this.strategies = {} | ||
| // Error listeners | ||
| this._errorListeners = [] | ||
| // Storage & State | ||
| options.initialState = { user: null, loggedIn: false } | ||
| const storage = new Storage(ctx, options) | ||
| this.$storage = storage | ||
| this.$state = storage.state | ||
| } | ||
| init () { | ||
| // Watch for loggedIn changes only in client side | ||
| if (process.browser) { | ||
| this.$storage.watchState('loggedIn', loggedIn => { | ||
| if (!routeOption(this.ctx.route, 'auth', false)) { | ||
| this.redirect(loggedIn ? 'home' : 'logout') | ||
| } | ||
| }) | ||
| } | ||
| // Restore strategy | ||
| this.$storage.syncUniversal('strategy', this.options.defaultStrategy) | ||
| // Set default strategy if current one is invalid | ||
| if (!this.strategy) { | ||
| this.$storage.setUniversal('strategy', this.options.defaultStrategy) | ||
| // Give up if still invalid | ||
| if (!this.strategy) { | ||
| return Promise.resolve() | ||
| } | ||
| } | ||
| // Call mounted for active strategy on initial load | ||
| return this.mounted() | ||
| } | ||
| // Backward compatibility | ||
| get state () { | ||
| if (!this._state_warn_shown) { | ||
| this._state_warn_shown = true | ||
| // eslint-disable-next-line no-console | ||
| console.warn('[AUTH] $auth.state is deprecated. Please use $auth.$state or top level props like $auth.loggedIn') | ||
| } | ||
| return this.$state | ||
| } | ||
| getState (key) { | ||
| if (!this._get_state_warn_shown) { | ||
| this._get_state_warn_shown = true | ||
| // eslint-disable-next-line no-console | ||
| console.warn('[AUTH] $auth.getState is deprecated. Please use $auth.$storage.getState() or top level props like $auth.loggedIn') | ||
| } | ||
| return this.$storage.getState(key) | ||
| } | ||
| // --------------------------------------------------------------- | ||
| // Strategy and Scheme | ||
| // --------------------------------------------------------------- | ||
| get strategy () { | ||
| return this.strategies[this.$state.strategy] | ||
| } | ||
| registerStrategy (name, strategy) { | ||
| this.strategies[name] = strategy | ||
| } | ||
| setStrategy (name) { | ||
| if (name === this.$storage.getUniversal('strategy')) { | ||
| return Promise.resolve() | ||
| } | ||
| // Set strategy | ||
| this.$storage.setUniversal('strategy', name) | ||
| // Call mounted hook on active strategy | ||
| return this.mounted() | ||
| } | ||
| mounted () { | ||
| if (!this.strategy.mounted) { | ||
| return this.fetchUserOnce() | ||
| } | ||
| return Promise.resolve(this.strategy.mounted(...arguments)).catch(error => { | ||
| this.callOnError(error, { method: 'mounted' }) | ||
| return Promise.reject(error) | ||
| }) | ||
| } | ||
| loginWith (name, ...args) { | ||
| return this.setStrategy(name).then(() => this.login(...args)) | ||
| } | ||
| login () { | ||
| if (!this.strategy.login) { | ||
| return Promise.resolve() | ||
| } | ||
| return this.wrapLogin(this.strategy.login(...arguments)).catch(error => { | ||
| this.callOnError(error, { method: 'login' }) | ||
| return Promise.reject(error) | ||
| }) | ||
| } | ||
| fetchUser () { | ||
| if (!this.strategy.fetchUser) { | ||
| return Promise.resolve() | ||
| } | ||
| return Promise.resolve(this.strategy.fetchUser(...arguments)).catch(error => { | ||
| this.callOnError(error, { method: 'fetchUser' }) | ||
| return Promise.reject(error) | ||
| }) | ||
| } | ||
| logout () { | ||
| if (!this.strategy.logout) { | ||
| this.reset() | ||
| return Promise.resolve() | ||
| } | ||
| return Promise.resolve(this.strategy.logout(...arguments)).catch(error => { | ||
| this.callOnError(error, { method: 'logout' }) | ||
| return Promise.reject(error) | ||
| }) | ||
| } | ||
| reset () { | ||
| if (!this.strategy.reset) { | ||
| this.setUser(null) | ||
| this.setToken(this.$state.strategy, null) | ||
| return Promise.resolve() | ||
| } | ||
| return Promise.resolve(this.strategy.reset(...arguments)).catch(error => { | ||
| this.callOnError(error, { method: 'reset' }) | ||
| return Promise.reject(error) | ||
| }) | ||
| } | ||
| // --------------------------------------------------------------- | ||
| // Token helpers | ||
| // --------------------------------------------------------------- | ||
| getToken (strategy) { | ||
| const _key = this.options.token.prefix + strategy | ||
| return this.$storage.getUniversal(_key) | ||
| } | ||
| setToken (strategy, token) { | ||
| const _key = this.options.token.prefix + strategy | ||
| return this.$storage.setUniversal(_key, token) | ||
| } | ||
| syncToken (strategy) { | ||
| const _key = this.options.token.prefix + strategy | ||
| return this.$storage.syncUniversal(_key) | ||
| } | ||
| // --------------------------------------------------------------- | ||
| // User helpers | ||
| // --------------------------------------------------------------- | ||
| get user () { | ||
| return this.$state.user | ||
| } | ||
| get loggedIn () { | ||
| return this.$state.loggedIn | ||
| } | ||
| fetchUserOnce () { | ||
| if (!this.$state.user) { | ||
| return this.fetchUser(...arguments) | ||
| } | ||
| return Promise.resolve() | ||
| } | ||
| setUser (user) { | ||
| this.$storage.setState('loggedIn', Boolean(user)) | ||
| this.$storage.setState('user', user) | ||
| } | ||
| // --------------------------------------------------------------- | ||
| // Utils | ||
| // --------------------------------------------------------------- | ||
| get busy () { | ||
| return this.$storage.getState('busy') | ||
| } | ||
| request (endpoint, defaults) { | ||
| const _endpoint = | ||
| typeof defaults === 'object' | ||
| ? Object.assign({}, defaults, endpoint) | ||
| : endpoint | ||
| return this.ctx.app.$axios | ||
| .request(_endpoint) | ||
| .then(response => { | ||
| if (_endpoint.propertyName) { | ||
| return getProp(response.data, _endpoint.propertyName) | ||
| } else { | ||
| return response.data | ||
| } | ||
| }) | ||
| .catch(error => { | ||
| // Call all error handlers | ||
| this.callOnError(error, { method: 'request' }) | ||
| // Throw error | ||
| return Promise.reject(error) | ||
| }) | ||
| } | ||
| requestWith (strategy, endpoint, defaults) { | ||
| const token = this.getToken(strategy) | ||
| if (isUnset(token)) { | ||
| return Promise.reject(new Error('No Token')) | ||
| } | ||
| const _endpoint = Object.assign({}, defaults, endpoint) | ||
| if (!_endpoint.headers) { | ||
| _endpoint.headers = {} | ||
| } | ||
| if (!_endpoint.headers['Authorization']) { | ||
| _endpoint.headers['Authorization'] = token | ||
| } | ||
| return this.request(_endpoint) | ||
| } | ||
| wrapLogin (promise) { | ||
| this.$storage.setState('busy', true) | ||
| this.error = null | ||
| return Promise.resolve(promise) | ||
| .then(() => { | ||
| this.$storage.setState('busy', false) | ||
| }) | ||
| .catch(error => { | ||
| this.$storage.setState('busy', false) | ||
| return Promise.reject(error) | ||
| }) | ||
| } | ||
| onError (listener) { | ||
| this._errorListeners.push(listener) | ||
| } | ||
| callOnError (error, payload = {}) { | ||
| this.error = error | ||
| for (let fn of this._errorListeners) { | ||
| fn(error, payload) | ||
| } | ||
| } | ||
| redirect (name, noRouter = false) { | ||
| if (!this.options.redirect) { | ||
| return | ||
| } | ||
| const from = this.options.fullPathRedirect ? this.ctx.route.path : this.ctx.route.fullPath | ||
| let to = this.options.redirect[name] | ||
| if (!to) { | ||
| return | ||
| } | ||
| // Apply rewrites | ||
| if (this.options.rewriteRedirects) { | ||
| if (name === 'login' && isRelativeURL(from) && !isSameURL(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 | ||
| } | ||
| } | ||
| } | ||
| // Prevent infinity redirects | ||
| if (isSameURL(to, from)) { | ||
| return | ||
| } | ||
| if (process.browser) { | ||
| if (noRouter) { | ||
| window.location.replace(to) | ||
| } else { | ||
| this.ctx.redirect(to) | ||
| } | ||
| } else { | ||
| this.ctx.redirect(to) | ||
| } | ||
| } | ||
| hasScope (scope) { | ||
| const userScopes = this.$state.user && getProp(this.$state.user, this.options.scopeKey) | ||
| if (!userScopes) { | ||
| return undefined | ||
| } | ||
| if (Array.isArray(userScopes)) { | ||
| return userScopes.includes(scope) | ||
| } | ||
| return Boolean(getProp(userScopes, scope)) | ||
| } | ||
| } |
| import Middleware from '../middleware' | ||
| import { routeOption } from './utilities' | ||
| Middleware.auth = function (ctx) { | ||
| // Disable middleware if options: { auth: false } is set on the route | ||
| if (routeOption(ctx.route, 'auth', false)) { | ||
| return | ||
| } | ||
| const { login } = ctx.app.$auth.options.redirect | ||
| if (ctx.app.$auth.$state.loggedIn) { | ||
| // -- Authorized -- | ||
| // Redirect to home page if inside login page | ||
| if (login && ctx.route.path === login.split('?')[0]) { | ||
| ctx.app.$auth.redirect('home') | ||
| } | ||
| } else { | ||
| // -- Guest -- | ||
| // Redirect to login path if not authorized | ||
| ctx.app.$auth.redirect('login') | ||
| } | ||
| } |
| import Vue from 'vue' | ||
| import Cookies from 'js-cookie' | ||
| import getProp from 'dotprop' | ||
| import { parse as parseCookie } from 'cookie' | ||
| import { isUnset, isSet } from './utilities' | ||
| export default class Storage { | ||
| constructor (ctx, options) { | ||
| this.ctx = ctx | ||
| this.options = options | ||
| this._initState() | ||
| } | ||
| // ------------------------------------ | ||
| // Universal | ||
| // ------------------------------------ | ||
| setUniversal (key, value, isJson) { | ||
| // Local state | ||
| this.setState(key, value) | ||
| // Cookies | ||
| this.setCookie(key, value) | ||
| // Local Storage | ||
| this.setLocalStorage(key, value, isJson) | ||
| return value | ||
| } | ||
| getUniversal (key, isJson) { | ||
| // Local state | ||
| let value = this.getState(key) | ||
| // Cookies | ||
| if (isUnset(value)) { | ||
| value = this.getCookie(key, isJson) | ||
| } | ||
| // Local Storage | ||
| if (isUnset(value)) { | ||
| value = this.getLocalStorage(key, isJson) | ||
| } | ||
| return value | ||
| } | ||
| syncUniversal (key, defaultValue, isJson) { | ||
| let value = this.getUniversal(key, isJson) | ||
| if (isUnset(value) && isSet(defaultValue)) { | ||
| value = defaultValue | ||
| } | ||
| if (isSet(value)) { | ||
| this.setUniversal(key, value) | ||
| } | ||
| return value | ||
| } | ||
| // ------------------------------------ | ||
| // Local state (reactive) | ||
| // ------------------------------------ | ||
| _initState () { | ||
| // Private state is suitable to keep information not being exposed to Vuex store | ||
| // This helps prevent stealing token from SSR response HTML | ||
| Vue.set(this, '_state', {}) | ||
| // Use vuex for local state's if possible | ||
| this._useVuex = this.options.vuex && this.ctx.store | ||
| if (this._useVuex) { | ||
| const storeModule = { | ||
| namespaced: true, | ||
| state: () => this.options.initialState, | ||
| mutations: { | ||
| SET (state, payload) { | ||
| Vue.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.set(this, 'state', {}) | ||
| } | ||
| } | ||
| setState (key, value) { | ||
| if (key[0] === '_') { | ||
| Vue.set(this._state, key, value) | ||
| } else { | ||
| if (this._useVuex) { | ||
| this.ctx.store.commit(this.options.vuex.namespace + '/SET', { | ||
| key, | ||
| value | ||
| }) | ||
| } else { | ||
| Vue.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 | ||
| ) | ||
| } | ||
| } | ||
| // ------------------------------------ | ||
| // Local storage | ||
| // ------------------------------------ | ||
| setLocalStorage (key, value, isJson) { | ||
| if (typeof localStorage === 'undefined' || !this.options.localStorage) { | ||
| return | ||
| } | ||
| const _key = this.options.localStorage.prefix + key | ||
| if (isUnset(value)) { | ||
| localStorage.removeItem(_key) | ||
| } else { | ||
| localStorage.setItem(_key, isJson ? JSON.stringify(value) : value) | ||
| } | ||
| return value | ||
| } | ||
| getLocalStorage (key, isJson) { | ||
| if (typeof localStorage === 'undefined' || !this.options.localStorage) { | ||
| return | ||
| } | ||
| const _key = this.options.localStorage.prefix + key | ||
| const value = localStorage.getItem(_key) | ||
| return isJson ? JSON.parse(value) : value | ||
| } | ||
| // ------------------------------------ | ||
| // Cookies | ||
| // ------------------------------------ | ||
| setCookie (key, value, options = {}) { | ||
| if (process.server || !this.options.cookie) { | ||
| return | ||
| } | ||
| const _key = this.options.cookie.prefix + key | ||
| const _options = Object.assign({}, this.options.cookie.options, options) | ||
| if (isUnset(value)) { | ||
| Cookies.remove(_key, _options) | ||
| } else { | ||
| Cookies.set(_key, value, _options) | ||
| } | ||
| return value | ||
| } | ||
| getCookie (key, isJson) { | ||
| if (!this.options.cookie) { | ||
| return | ||
| } | ||
| const _key = this.options.cookie.prefix + key | ||
| const cookieStr = process.browser | ||
| ? document.cookie | ||
| : this.ctx.req.headers.cookie | ||
| const cookies = parseCookie(cookieStr || '') || {} | ||
| const value = cookies[_key] | ||
| return isJson ? JSON.parse(value) : value | ||
| } | ||
| } |
| export const isUnset = o => typeof o === 'undefined' || o === null | ||
| export const isSet = o => !isUnset(o) | ||
| export const isSameURL = (a, b) => a.split('?')[0] === b.split('?')[0] | ||
| export const isRelativeURL = u => | ||
| u && u.length && /^\/[a-zA-Z0-9@\-%_~][/a-zA-Z0-9@\-%_~]{1,200}$/.test(u) | ||
| export const 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 | ||
| } | ||
| export const encodeQuery = queryObject => { | ||
| return Object.keys(queryObject) | ||
| .map( | ||
| key => | ||
| encodeURIComponent(key) + '=' + encodeURIComponent(queryObject[key]) | ||
| ) | ||
| .join('&') | ||
| } | ||
| export const randomString = () => btoa(Math.random() + '').replace('==', '') | ||
| export const routeOption = (route, key, value) => { | ||
| return route.matched.some(m => { | ||
| if (process.browser) { | ||
| // Browser | ||
| return Object.values(m.components).some( | ||
| component => component.options[key] === value | ||
| ) | ||
| } else { | ||
| // SSR | ||
| return Object.values(m.components).some(component => | ||
| Object.values(component._Ctor).some( | ||
| ctor => ctor.options && ctor.options[key] === value | ||
| ) | ||
| ) | ||
| } | ||
| }) | ||
| } |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
45292
1.91%43
7.5%1
-50%1002
-0.3%