Skip to content

Instantly share code, notes, and snippets.

@johnwmcarneiro
Created October 15, 2019 20:51
Show Gist options
  • Save johnwmcarneiro/17bc7e9f8143e0d8c5a8e9775d4bdf9f to your computer and use it in GitHub Desktop.
Save johnwmcarneiro/17bc7e9f8143e0d8c5a8e9775d4bdf9f to your computer and use it in GitHub Desktop.
Vue + Axios + JWT + Middleware
// router/index.js
const routes = [];
const router = new Router({
mode: 'hash', // https://router.vuejs.org/api/#mode
linkActiveClass: 'open active',
scrollBehavior: () => ({ y: 0 }),
routes
});
router.beforeEach((to, from, next) => {
if (!to.meta.middleware) {
return next();
}
const metaMiddleware = to.meta.middleware;
const middleware = [
inspectToken,
...metaMiddleware
];
const context = {
to,
from,
next,
store
};
return middleware[0]({
...context,
next: middlewarePipeline(context, middleware, 1)
});
});
export default router;
// router/middleware.js
export function auth({ next, store }) {
if (!store.getters['auth/isLogged']) {
store.dispatch('auth/logout')
return next({
name: 'Login'
});
}
return next();
}
export function inspectToken({ next, store }) {
if (store.getters['auth/isLogged']) {
store.dispatch('auth/inspectToken');
}
return next();
}
// router/middlewarePipeline.js
function middlewarePipeline (context, middleware, index) {
const nextMiddleware = middleware[index]
if(!nextMiddleware){
return context.next;
}
return () => {
const nextPipeline = middlewarePipeline(
context, middleware, (index + 1)
);
nextMiddleware({ ...context, next: nextPipeline });
}
}
export default middlewarePipeline;
// store/auth.js
import api from '@/shared/api';
import jwt_decode from 'jwt-decode';
import router from '../router';
const state = {
token: localStorage.getItem('token') || null,
currentUser: null
}
const mutations = {
UPDATE_TOKEN (state, payload) {
state.token = payload.token
localStorage.setItem('token', state.token)
api.defaults.headers.common['Authorization'] = `JWT ${state.token}`
},
LOGOUT (state) {
state.token = null
state.currentUser = null
localStorage.removeItem('token')
api.defaults.headers.common = {}
},
CURRENT_USER (state, payload) {
state.currentUser = payload
}
}
const actions = {
obtainToken: (context, payload) => {
return api.post('auth/obtain-token/', payload).then(async response => {
context.commit('UPDATE_TOKEN', response.data)
});
},
refreshToken: (context) => {
let payload = {
token: context.state.token
};
return api.post('auth/refresh-token/', payload).then(async response => {
context.commit('UPDATE_TOKEN', response.data)
}).catch(() => {
context.dispatch('logout');
});
},
inspectToken: (context) => {
const token = context.state.token;
const lifetime = 60 * 60 * 24; // 24 hours; Expiration Delta
const lifespan = (60 * 60 * 24 * 7) - lifetime; // 7 days - lifetime; Refresh Expiration Delta
const now = (Date.now() / 1000);
if (token){
const decoded = jwt_decode(token);
const exp = decoded.exp
const orig_iat = decoded.orig_iat
if ((exp - now) < lifetime && (now - orig_iat) < lifespan){
context.dispatch('refreshToken');
} else if ((exp - now) < lifetime){
// DO NOTHING, DO NOT REFRESH
} else {
context.dispatch('logout');
}
}
},
logout: (context) => {
context.commit('LOGOUT');
router.push("/pages/login");
},
getMe: (context) => {
return api.get('/users/me/').then(async response => {
context.commit('CURRENT_USER', response.data)
})
}
}
const getters = {
isLogged: (state) => state.token !== null,
}
export default {
namespaced: true,
state,
mutations,
actions,
getters
}
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import createPersistedState from 'vuex-persistedstate'
// Modules vuex
import auth from './auth'
Vue.use(Vuex)
export default new Vuex.Store({
plugins: [createPersistedState({
key: 'SchefferCRM'
})],
modules: {
auth
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment