I have the following store code to handle login, logout, to fetch the user and to set the token to all axios requests as auth header.
It works great with client side render, say I go to login page, login, get the token, store it in cookies.. But when I refresh the page, it seems that no token is being set anymore.. I'm calling the fetch action on NuxtServerInit, still no luck.. Any ideas where my code is failing?
Heres my store/index.js file :
https://jsfiddle.net/3dc07yv4/
import Cookie from 'cookie'
import Cookies from 'js-cookie'
export const state = () => ({
sidebar: true,
token: null,
user: null
})
export const mutations = {
// SET SIDEBAR
toggleSidebar (state) {
state.sidebar = !state.sidebar
},
// SET USER
setUser (state, user) {
state.user = user
},
// SET TOKEN
setToken (state, token) {
state.token = token
}
}
export const getters = {
loggedIn (state) {
return Boolean(state.user && state.token)
}
}
export const actions = {
async nuxtServerInit ({ dispatch }, { req }) {
await dispatch('fetch')
},
// Update token
async updateToken ({ commit }, token) {
// Update token in store's state
commit('setToken', token)
// Set Authorization token for all axios requests
console.log('Setting axios token to: ', token)
this.$axios.setToken(token, '')
// Update cookies
if (process.browser) {
// ...Browser
if (token) {
Cookies.set('ccmsToken', token, { expires: 1 })
} else {
Cookies.remove('ccmsToken')
}
} else {
// ...Server
let params = {
domain: '/'
}
if (!token) {
let expires
let date = new Date()
expires = date.setDate(date.getDate() + 1)
params.expires = new Date(expires)
}
this.app.context.res.setHeader('Set-Cookie', Cookie.serialize('ccmsToken', token, params))
}
},
// Fetch Token
async fetchToken ({ dispatch }) {
let token
// Try to extract token from cookies
if (!token) {
const cookieStr = process.browser ? document.cookie : this.app.context.req.headers.cookie
const cookies = Cookie.parse(cookieStr || '') || {}
token = cookies['ccmsToken']
}
if (token) {
await dispatch('updateToken', token)
}
},
// Reset
async reset ({ dispatch, commit }) {
commit('setUser', null)
await dispatch('updateToken', null)
},
// Fetch
async fetch ({ getters, state, commit, dispatch }, username = 'admin', { endpoint = 'http://localhost:8000/api/user' } = {}) {
// Fetch and update latest token
await dispatch('fetchToken')
// Skip if there is no token set
if (!state.token) {
return
}
// Try to get user profile
try {
const data = await this.$axios.$get(endpoint + '?username=' + username)
commit('setUser', data)
} catch (e) {
// Reset store
await dispatch('reset')
}
},
// Login
async login ({ dispatch }, { fields, endpoint = 'http://localhost:8000/api/login' } = {}) {
try {
// Send credentials to API
let data = await this.$axios.$post(endpoint, fields)
if (data.success) {
await dispatch('updateToken', data['token'])
// Fetch authenticated user
await dispatch('fetch', data.user.username)
} else {
throw new Error(data.message)
}
} catch (error) {
if (error.response && error.response.status === 401) {
throw new Error('Bad credentials')
}
throw error
}
},
// Logout
async logout ({ dispatch, state }) {
try {
await dispatch('reset')
} catch (e) {
console.error('Error while logging out', e)
}
}
}
I have solved this problem by implementing an interceptor that would inject the token into headers on every axios request. It looks like this:
export default ({ $axios, store }) => {
$axios.defaults.baseURL = 'https://api.com/api/'
if (process.server) {
return
}
$axios.interceptors.request.use(request => {
request.baseURL = 'https://api.com/api/'
// Get token from auth.js store
const token = store.state.token
// Update token axios header
if (token) {
request.headers.common['Authorization'] = token
}
return request
})
}
You use it as a nuxt plugin.