From 499c28ac6300037e759bf84583564b1fc058d8e6 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Fri, 10 Nov 2017 16:24:55 +0330 Subject: [PATCH] feat: improve auth store BREAKING CHANGE: Some options changed and/or simplified --- README.md | 90 +++++++++++------------- lib/module.js | 44 +++++++----- lib/templates/auth.middleware.js | 6 +- lib/templates/auth.store.js | 117 ++++++++++++++----------------- 4 files changed, 123 insertions(+), 134 deletions(-) diff --git a/README.md b/README.md index 40f740222..6ef575f10 100644 --- a/README.md +++ b/README.md @@ -26,82 +26,76 @@ ], // Default Values auth: { - login: { - endpoint: 'auth/login', - propertyName: 'token' - }, - logout: { - endpoint: 'auth/logout', - method: 'GET', - paramTokenName: '', - appendToken: false - }, - user: { - endpoint: 'auth/user', - propertyName: 'user', - paramTokenName: '', - appendToken: false - }, - storageTokenName: 'nuxt-auth-token', - tokenType: 'Bearer', - notLoggedInRedirectTo: '/login', - loggedInRedirectTo: '/' - } + user: { + endpoint: 'auth/user', + propertyName: 'user', + }, + login: { + endpoint: 'auth/login', + }, + logout: { + endpoint: 'auth/logout', + method: 'GET', + }, + redirect: { + notLoggedIn: '/login', + loggedIn: '/' + }, + token: { + enabled: true, + name: 'token', + cookieName: 'token', + type: 'Bearer' + } } ``` ## Options +#### user +Sets the global settings for store **fetch** action. +* **endpoint** - Set the URL of the user data endpoint. It can be a relative or absolute path. +* **propertyName** - Set the name of the return object property that contains the user data. If you want the entire object returned, set an empty string. + #### login -Set the global settings for the login action. +Set the global settings for store **login** action. * **endpoint** - Set the URL of the login endpoint. It can be a relative or absolute path. -* **propertyName** - Set the name of the return object property that contains the access token. #### logout -Sets the global settings for the logout action. +Sets the global settings for store **logout** action. * **endpoint** - Set the URL of the logout endpoint. It can be a relative or absolute path. * **method** - Set the request to POST or GET. -* **paramTokenName** - Set the access token query string parameter name. -* **appendToken** - Set true if you want the access token to be inserted in the URL. - -#### user -Sets the global settings for the fetch action. -* **endpoint** - Set the URL of the user data endpoint. It can be a relative or absolute path. -* **propertyName** - Set the name of the return object property that contains the user data. If you want the entire object returned, set an empty string. -* **paramTokenName** - Set the access token query string parameter name. -* **appendToken** - Set true if you want the access token to be inserted in the URL. - -#### storageTokenName -Set the token name in the local storage and in the cookie. - -#### tokenType -Sets the token type of the authorization header. -#### notLoggedInRedirectTo -Sets the redirect URL default of the users not logged in. This is actived when 'auth' middeware is register. +#### Token +* **enabled** - Get and use tokens for authentication. +* **name** - Set the token name in the local storage. +* **cookieName** - Set the token name in Cookies. (Set to `null` to disable) +* **type** - Sets the token type of the authorization header. -#### loggedInRedirectTo -Sets the redirect URL default of the users logged in. This is actived when 'no-auth' middeware is register. +#### redirect +* **notLoggedInRedirectTo** - Sets the redirect URL default of the users not logged in. Only when `auth` middleware is added to a page. +* **loggedInRedirectTo** - Sets the redirect URL default of the users logged in. Only when `no-auth` middleware is added to a page. ## Example usage ```js // ... code ... +// Do a password based login store.dispatch('auth/login', { fields: { username: 'your_username', password: 'your_password' } -}) // run login +}) // ... code ... store.dispatch('auth/logout') // run logout // ... code ... -store.state['auth']['token'] // get access token +store.state.auth.token // get access token // ... code ... -store.state['auth']['user'] // get user data +store.state.auth.user // get user data // ... code ... store.getters['auth/loggedIn'] // get login status (true or false) @@ -113,8 +107,8 @@ store.getters['auth/loggedIn'] // get login status (true or false) // ... in nuxt.config.js ... router: { middleware: [ - 'auth', // If user not logged in, redirect to '/login' or to URL defined in notLoggedInRedirectTo property - 'no-auth' // If user is already logged in, redirect to '/' or to URL defined in loggedInRedirectTo property + 'auth', // If user not logged in, redirect to '/login' or to URL defined in redirect property + 'no-auth' // If user is already logged in, redirect to '/' or to URL defined in redirect property ] } ``` diff --git a/lib/module.js b/lib/module.js index 6819b30ed..9d51266d8 100644 --- a/lib/module.js +++ b/lib/module.js @@ -4,37 +4,49 @@ const merge = require('lodash/merge') module.exports = function (moduleOptions) { // Apply defaults const defaults = { + user: { + endpoint: 'auth/user', + propertyName: 'user', + }, login: { endpoint: 'auth/login', - propertyName: 'token', - session: false }, logout: { endpoint: 'auth/logout', method: 'GET', - paramTokenName: '', - appendToken: false }, - user: { - endpoint: 'auth/user', - propertyName: 'user', - paramTokenName: '', - appendToken: false + redirect: { + notLoggedIn: '/login', + loggedIn: '/' }, - storageTokenName: 'nuxt-auth-token', - tokenType: 'Bearer', - notLoggedInRedirectTo: '/login', - loggedInRedirectTo: '/' + token: { + enabled: true, + name: 'token', + cookieName: 'token', + type: 'Bearer' + } } const options = merge(defaults, moduleOptions, this.options.auth) // Plugin - this.addPlugin({ src: resolve(__dirname, './templates/auth.plugin.js'), fileName: 'auth.plugin.js' }) + this.addPlugin({ + src: resolve(__dirname, './templates/auth.plugin.js'), + fileName: 'auth.plugin.js', + options + }) // Middleware - this.addTemplate({ src: resolve(__dirname, './templates/auth.middleware.js'), fileName: 'auth.middleware.js', options }) + this.addTemplate({ + src: resolve(__dirname, './templates/auth.middleware.js'), + fileName: 'auth.middleware.js', + options + }) // Store - this.addTemplate({ src: resolve(__dirname, './templates/auth.store.js'), fileName: 'auth.store.js', options }) + this.addTemplate({ + src: resolve(__dirname, './templates/auth.store.js'), + fileName: 'auth.store.js', + options + }) } diff --git a/lib/templates/auth.middleware.js b/lib/templates/auth.middleware.js index 6400277fc..324f31a13 100644 --- a/lib/templates/auth.middleware.js +++ b/lib/templates/auth.middleware.js @@ -1,17 +1,15 @@ import middleware from './middleware' -const options = <%= serialize(options) %> - middleware.auth = function authMiddleware ({ store, redirect }) { // If user not logged in, redirect to /login if (!store.getters['auth/loggedIn']) { - return redirect(options.notLoggedInRedirectTo) + return redirect('<%= options.redirect.loggedIn %>') } } middleware['no-auth'] = function noAuthMiddleware ({ store, redirect }) { // If user is already logged in, redirect to / if (store.getters['auth/loggedIn']) { - return redirect(options.loggedInRedirectTo) + return redirect('<%= options.redirect.notLoggedIn %>') } } diff --git a/lib/templates/auth.store.js b/lib/templates/auth.store.js index 45f2850c1..c8645cc09 100644 --- a/lib/templates/auth.store.js +++ b/lib/templates/auth.store.js @@ -1,21 +1,19 @@ +<% if (options.token && options.token.enabled && options.token.cookieName) { %> import Cookie from 'cookie' import Cookies from 'js-cookie' -import kebabCase from 'lodash/kebabCase' - -const options = <%= serialize(options) %> -const storageTokenName = kebabCase(options.storageTokenName) +<% } %> export default { namespaced: true, state: () => ({ - token: null, + <% if (options.token && options.token.enabled) { %>token: null,<% } %> user: null }), getters: { loggedIn (state) { - return Boolean(state.user || state.token) + return Boolean(state.user<% if (options.token && options.token.enabled) { %> || state.token<% } %>) } }, @@ -25,33 +23,41 @@ export default { state.user = user }, + <% if (options.token && options.token.enabled) { %> // SET_TOKEN SET_TOKEN (state, token) { state.token = token } + <% } %> }, actions: { + <% if (options.token && options.token.enabled) { %> + // Update token updateToken ({ commit }, token) { - // Update state + // Update token in store's state commit('SET_TOKEN', token) + // Set Authorization token for all axios requests + this.$axios.setToken(token, '<%= options.token.type %>'); + // Update localStorage if (process.browser && localStorage) { if (token) { - localStorage.setItem(storageTokenName, token) + localStorage.setItem('<%= options.token.name %>', token) } else { - localStorage.removeItem(storageTokenName) + localStorage.removeItem('<%= options.token.name %>') } } + <% if (options.token.cookieName) { %> // Update cookies if (process.browser) { // ...Browser if (token) { - Cookies.set(storageTokenName, token) + Cookies.set('<%= options.token.cookieName %>', token) } else { - Cookies.remove(storageTokenName) + Cookies.remove('<%= options.token.cookieName %>') } } else { // ...Server @@ -64,111 +70,90 @@ export default { expires = date.setDate(date.getDate() - 1) params.expires = new Date(expires) } - this.$ctx.res.setHeader('Set-Cookie', Cookie.serialize(storageTokenName, token, params)) + this.$ctx.res.setHeader('Set-Cookie', Cookie.serialize('<%= options.token.cookieName %>', token, params)) } + <% } %> }, + <% } %> + <% if (options.token && options.token.enabled) { %> + // Fetch Token fetchToken ({ dispatch }) { let token - // First try localStorage + // Try to extract token from localStorage if (process.browser && localStorage) { - token = localStorage.getItem(storageTokenName) + token = localStorage.getItem('<%= options.token.name %>') } - // Then try to extract token from cookies + <% if (options.token.cookieName) { %> + // Try to extract token from cookies if (!token) { const cookieStr = process.browser ? document.cookie : this.$ctx.req.headers.cookie const cookies = Cookie.parse(cookieStr || '') || {} - token = cookies[storageTokenName] + token = cookies['<%= options.token.cookieName %>'] } + <% } %> if (token) { dispatch('updateToken', token) } }, + <% } %> - invalidate ({ dispatch, commit }) { + // Reset + reset ({ dispatch, commit }) { commit('SET_USER', null) - dispatch('updateToken', null) + <% if (options.token && options.token.enabled) { %>dispatch('updateToken', null)<% } %> }, - async fetch ({ state, commit, dispatch }) { - let {endpoint, propertyName, paramTokenName, appendToken} = options.user - + // Fetch + async fetch ({ getters, state, commit, dispatch }, { endpoint = '<%= options.user.endpoint %>' } = {}) { + <% if (options.token && options.token.enabled) { %> // Fetch and update latest token dispatch('fetchToken') + <% } %> - // Not loggedIn - if (!state.token) { + // Skip if not loggedIn + if (!getters.loggedIn) { return } - // Append token - if (appendToken) { - paramTokenName = (paramTokenName) ? ('?' + paramTokenName + '=') : '/' - endpoint = endpoint + paramTokenName + state.token - } - // Try to get user profile try { - // Set Authorization Token in request - this.$axios.setToken(state.token, options.tokenType) - - const userData = await this.$axios.$get(endpoint) - - if (propertyName) { - commit('SET_USER', userData[propertyName]) - } else { - commit('SET_USER', userData) - } + const data = await this.$axios.$get(endpoint) + commit('SET_USER', data<%= options.user.propertyName ? ('[\'' + options.user.propertyName + '\']') : '' %>) } catch (e) { - dispatch('invalidate') + // Reset store + dispatch('reset') } }, // Login - async login ({ dispatch }, { fields } = {}) { - let {endpoint, propertyName} = options.login - + async login ({ dispatch }, { fields, endpoint = '<%= options.login.endpoint %>' } = {}) { // Send credentials to API - let tokenData = await this.$axios.$post(endpoint, fields) - let token = tokenData[propertyName] + let data = await this.$axios.$post(endpoint, fields) - // Update new token - dispatch('updateToken', token) + <% if (options.token && options.token.enabled) { %> + dispatch('updateToken', data['<%= options.token.name %>']) + <% } %> // Fetch authenticated user await dispatch('fetch') }, // Logout - async logout ({ dispatch, state }) { - let {endpoint, method, paramTokenName, appendToken} = options.logout - - // Append token - if (appendToken) { - paramTokenName = (paramTokenName) ? ('?' + paramTokenName + '=') : '/' - endpoint = endpoint + paramTokenName + state.token - } - + async logout ({ dispatch, state }, { endpoint = '<%= options.logout.endpoint %>' } = {}) { // Server side logout try { - // Set Authorization Token in request - this.$axios.setToken(state.token, options.tokenType); - - if (method.toUpperCase() === 'POST') { - await this.$axios.$post(endpoint) - } else { - await this.$axios.$get(endpoint) - } + await this.$axios.$<%= options.logout.method.toLowerCase() %>(endpoint) } catch (e) { // eslint-disable-next-line no-console console.error('Error while logging out', e) } - // Unload user profile & token - dispatch('invalidate') + // Reset store + dispatch('reset') } } }