Closed atmar closed 6 years ago
I am able to get it work with a (to me) hacky method and the help of this. But it doesn't feel right. At first, the middleware reloaded the user on every request. So I've added a seperate function for isServer(). Otherwise it would call "store.dispatch('auth/set_user', req)" on every route change in client and regrab the user. I've created a nuxtClientInit function that loads once with the app on client side (found that somewhere on the forum here a long time ago).
middleware/check-auth.js in nuxt.config.js
export default async function ({ isServer, isClient, store, req, app }) {
// If nuxt generate, pass this middleware
if (isServer && !req) return
if (isServer) {
await store.dispatch('auth/set_user', req)
}
}
plugins/nuxt-client-init.js
export default async (ctx) => {
await ctx.store.dispatch('nuxtClientInit', ctx)
}
store/index.js vuex store
import { initStorage } from '~/localstorage'
import { setGeoData } from '~/utils/geodata'
import { setAnonId } from '~/utils/axiosHeaders'
export const state = () => ({
imageUrl: process.env.IMAGE_URL
})
export const actions = {
async nuxtClientInit({ commit }, context) {
// First fill localstorage with values for new customers
await initStorage()
// Then fill Axios headers with anonid if set in local storage
await setAnonId(this)
// Set cart data
context.store.dispatch('cart/set_cart')
await context.store.dispatch('auth/load_token', null)
context.store.dispatch('auth/start_refresh_interval')
},
async nuxtServerInit({ commit }, context) {
await setGeoData(context)
console.log('nuxt server init')
}
}
store/auth.js vuex store:
import Cookies from 'js-cookie'
import Cookie from 'cookie'
const inBrowser = typeof window !== 'undefined'
export const state = () => ({
user: null,
token: null
})
export const mutations = {
SET_USER: function (state, user) {
state.user = user
},
SET_TOKEN: function (state, token) {
state.token = token
// Setup axios
this.$axios.setToken(token, 'Bearer')
// Store token in cookies
if (inBrowser) {
if (!token) {
return Cookies.remove('token')
}
Cookies.set('token', token)
}
}
}
export const getters = {
isAuthenticated(state) {
return !!state.user
}
}
export const actions = {
async load_token({ commit }, req) {
// Try to extract token from cookies
const cookieStr = inBrowser ? document.cookie : req.headers.cookie
const cookies = Cookie.parse(cookieStr || '') || {}
const token = cookies.token
commit('SET_TOKEN', token)
},
async register({ commit }, { email, password, confirmPassword }) {
try {
const { data } = await this.$axios.post('auth/register', { email, password, confirmPassword })
commit('SET_USER', data.user)
commit('SET_TOKEN', data.token)
} catch (error) {
throw error
}
},
async login({ commit }, { email, password }) {
try {
const { data } = await this.$axios.post('auth/login', { email, password })
commit('SET_USER', data.user)
commit('SET_TOKEN', data.token)
} catch (error) {
if (error.response && error.response.status === 401) {
throw new Error('Bad credentials')
}
throw error
}
},
async logout({ commit }) {
await this.$axios.post('auth/logout')
commit('SET_USER', null)
commit('SET_TOKEN', null)
},
async set_user({ commit, dispatch, state }, req) {
try {
await dispatch('load_token', req)
// No token
if (!state.token) {
return
}
await _refreshToken(this, commit)
const { data } = await this.$axios.get('auth/user')
commit('SET_USER', data.data)
} catch (error) {
// Token doesn't work anymore, throw it out
commit('SET_USER', null)
commit('SET_TOKEN', null)
}
},
start_refresh_interval({ getters }) {
let refreshInterval = setInterval(() => {
// If authenticated, refresh token every 60 min
// Get new token from header & replace token in cookies and axios headers
try {
if (getters.isAuthenticated) {
_refreshToken(this)
}
} catch (error) {
clearInterval(refreshInterval)
}
}, 1000 * 60 * 60)
}
}
async function _refreshToken(vm, commit) {
return vm.$axios.get('auth/refresh')
.then(async ({ headers }) => {
let token = headers.authorization.replace('Bearer ', '')
console.log('new token ' + token)
commit('SET_TOKEN', token)
})
.catch((error) => {
console.log(error)
})
}
Had to do some changes, Cookie.set and Cookie.remove doesn't work on server side. I called _refreshToken on serverside, changing the token. But it wasn't saved in the cookie. So now I load user data at server side and refresh the token on client side and set it in the cookie. I got to say, this authentication system is a whole lot more complicated than expected, perhaps there is a better way to do all of this. (backend is JWT Auth Laravel).
store/auth.js vuex store:
import Cookies from 'js-cookie'
import Cookie from 'cookie'
const inBrowser = typeof window !== 'undefined'
const opts = {}
export const state = () => ({
user: null,
token: null
})
export const mutations = {
SET_USER: function (state, user) {
state.user = user
},
SET_TOKEN: function (state, token) {
state.token = token
// Setup axios
this.$axios.setToken(token, 'Bearer')
// Store token in cookies
if (inBrowser) {
if (!token) {
return Cookies.remove('token', opts)
}
Cookies.set('token', token, opts)
}
}
}
export const getters = {
isAuthenticated(state) {
return !!state.user
}
}
export const actions = {
async load_token({ commit }, req) {
// Try to extract token from cookies
const cookieStr = inBrowser ? document.cookie : req.headers.cookie
const cookies = Cookie.parse(cookieStr || '') || {}
const token = cookies.token
commit('SET_TOKEN', token)
},
async register({ commit }, { email, password, confirmPassword }) {
try {
const { data } = await this.$axios.post('auth/register', { email, password, confirmPassword })
commit('SET_USER', data.user)
commit('SET_TOKEN', data.token)
} catch (error) {
throw error
}
},
async login({ commit }, { email, password }) {
try {
const { data } = await this.$axios.post('auth/login', { email, password })
commit('SET_USER', data.user)
commit('SET_TOKEN', data.token)
} catch (error) {
if (error.response && error.response.status === 401) {
throw new Error('Bad credentials')
}
throw error
}
},
async logout({ commit }) {
await this.$axios.post('auth/logout')
commit('SET_USER', null)
commit('SET_TOKEN', null)
},
async set_user({ commit, dispatch, state }, req) {
try {
await dispatch('load_token', req)
// No token
if (!state.token) {
return
}
if (inBrowser) {
await _refreshToken(this, commit)
} else {
const { data } = await this.$axios.get('auth/user')
commit('SET_USER', data.data)
}
} catch (error) {
// Token doesn't work anymore, throw it out
commit('SET_USER', null)
commit('SET_TOKEN', null)
}
},
start_refresh_interval({ getters }) {
let refreshInterval = setInterval(() => {
// If authenticated, refresh token every 60 min
// Get new token from header & replace token in storage and axios headers
try {
if (getters.isAuthenticated) {
_refreshToken(this)
}
} catch (error) {
clearInterval(refreshInterval)
}
}, 1000 * 60 * 60)
}
}
async function _refreshToken(vm, commit) {
return vm.$axios.get('auth/refresh')
.then(async ({ headers }) => {
let token = headers.authorization.replace('Bearer ', '')
commit('SET_TOKEN', token)
})
.catch(() => {
console.log('error')
})
}
Hey. With 5.0.0 release many things have been changed. Also, i highly suggest using auth module for Authentication handling.
Please open a new issue if you have more problems.
Hello,
I'd like to grab JWT out of cookie and set it to the axios instance as authorization, but this only works on server side. After initial load, subsequent AsyncData ajax requests throw an error that token is not given.
'check-auth.js' available in nuxt.config router as global middleware
Here I set the bearer token using the 'context.app' as axios instance.
Using this, the user loads perfectly and the initial data on the loaded page aswell, but when I navigate through the app, all asyncData axios requests fail because token is not set. My guess is that the axios instance is completely different on clientside and doen't have the bearer token. But how to solve this?
I used to set token in localstorage and load user after app has loaded, but this brought other issues. (For example if I load the account page (not navigate to it, but fresh refresh), I want to redirect to login page if not logged in using middleware. But if the token is in localstorage, the user is loaded after the middleware and always thinks a user is not logged in. Therefore always redirecting. So I've read that other people use JWT in cookie to grab user beforehand, but this brings me to the above issue