Nuxt.js: [ISSUE] Vuex + Axios + JWT Token Auth

Created on 24 Jan 2018  Â·  25Comments  Â·  Source: nuxt/nuxt.js

I have the following problem with Vuex and Axios auth tokens.

https://stackoverflow.com/questions/48402747/nuxt-js-vue-js-setting-axios-auth-token-in-vuex-store-resets-after-refresh

It works fine in client side, but after page refresh the token is not set anymore, tho I think I get the user fine. But the store seems to be filled at first, but when I press "Time travel to this state" it becomes empty and I get logged out.

On top of this I'm getting a bunch of PAGE NOT FOUND errors in console everytime i refresh the page.

Any clues on whats going on? I've added my stores index.js file in the stackoverflow question above.

Thanks for any input!

This question is available on Nuxt.js community (#c2329)
help-wanted

Most helpful comment

You need to update the axios authorization header when refreshing the page. You can put this code into the axios plugin.

// axios.js (Plugin)
export default ({ store }) => {
  axios.defaults.baseURL = {API endpoint}

  if (process.server) {
    return
  }

  axios.interceptors.request.use(request => {
    request.baseURL = {API endpoint}

    // Get token from auth.js store 
    const token = store.getters['auth/token']

    // Update token axios header
    if (token) {
      request.headers.common['Authorization'] = `Bearer ${token}`
    }
    return request
  })
})

All 25 comments

store won't be preserved after refreshing, you can combine store with cookie or session to save the JWT, for further more information you can take a look at https://github.com/nuxt-community/auth-module or https://github.com/nuxt/example-auth0

You didin't even bother opening my code before closing? Great support. I'm storing the JWT in cookies and loading the state back from them, User state loads, but token wouldn't set in axios.

Sorry, NuxtServerInit is right place to fetch token in server side, so when you refreshed pages, can fetchToken get the ccmsToken from cookie ?

It seems so.

Here's a screenshot of fetchToken action in store with console logs for token:
https://gyazo.com/89d7192c61e203e50b65e0536cdbb7d2

And here's the console log in nuxt console:

https://gyazo.com/67c9909e1f8eb5028a3457352d238e55

For some reason it gets called many times and alot of This page oculd not be found comes together with the refresh, maybe thats the root of the problem?

EDIT:

I think I found the problem.

its here:
https://gyazo.com/c7bb3d18553d7754e7fbec9ba6292db0

this.app.context.res.setHeader() , shouldn't we be setting the header on axios, not on context.res?

Perhaps it would help if I'd supply a demo site?

https://climatecms.herokuapp.com/login

demo, demo

and here's my update store/index.js

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
    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.$axios.defaults.headers.common['Authorization'] = token
      this.app.context.res.setHeader('Authorization', Cookie.serialize('ccmsToken', token, params))
      console.log('Axios: ', this.$axios.defaults.headers.common.Authorization)
    }
  },

  // 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)
    }
    if (process.browser) {
      console.log('Browser token: ', token)
    } else {
      console.log('Server token: ', token)
    }
  },

  // Reset
  async reset ({ dispatch, commit }) {
    commit('setUser', null)
    await dispatch('updateToken', null)
  },

  // Fetch
  async fetch ({ getters, state, commit, dispatch }, username = 'admin', { endpoint = 'https://climatecms-api.herokuapp.com/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 = 'https://climatecms-api.herokuapp.com/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)
    }
  }

}

Can you provide the middleware code or a demo repo ?

Can you try this.app.context.res.setHeader('Set-Cookie', Cookie.serialize('ccmsToken', token, params)) ?

Can I provide you a repo privately? We can post the solution here after ofc.

Sure, you can create a provaite repo on github or gitlab,then give me a readable access.

I think I have granted You access on gitlab repo.

About the PAGE NOT FOUND issue, it's a css source-map issue, you can add build.cssSourceMap: false to turn off it or add vuetify to vendor.

// nuxt.config.js

module.exports = {
  build: {
    cssSourceMap: false
  }
  // or
  vendor: [
    '~/plugins/vuetify.js',
    'vuetify'
  ]
}

Have you fixed the refreshing issue ? Because I don't have you api service, so I hard-code the data and cannot reproduce the issue.
Maybe you can check if axios in fetch and login can get the correct data, hence store.getters['loggedIn'] is true

You need to update the axios authorization header when refreshing the page. You can put this code into the axios plugin.

// axios.js (Plugin)
export default ({ store }) => {
  axios.defaults.baseURL = {API endpoint}

  if (process.server) {
    return
  }

  axios.interceptors.request.use(request => {
    request.baseURL = {API endpoint}

    // Get token from auth.js store 
    const token = store.getters['auth/token']

    // Update token axios header
    if (token) {
      request.headers.common['Authorization'] = `Bearer ${token}`
    }
    return request
  })
})

@jay7793 Isin't this exactly what I'm doing on NuxtServerInit?
@clarkdo Hey, I will try this solution when I get back home. I can share the api with You as well, all it does on /login route is it checks if username and password match and then returns a JWT token along with the username, then I use username to fetch the user info from /users route. Thanks for the tip about source maps btw, It will probably solve the issue, didint think about this.

The problem persists. Everytime I go to Profile route which is "/" or "/admin" I'm getting 403. If I load these routes server side - everything works just fine. Any clues?

Heres both front-end and the api repos, if anyone has some time to check them out..

@clarkdo
@jay7793

https://github.com/Cogitoergo/ccms-front
https://github.com/Cogitoergo/ccms-api

Navigate to localhost:8000/setup to create a demo user with this info:

username: rytis
password: rytis

PS: the problem with the 404 not found still exists after disabling cssSourceMap or by adding vuetify to vendors (which is already added).

@Cogitoergo
The root cause of issue should be that in your nuxtServerInit, fetch didn't have a username, so every server side initialization will fetch user admin instead of rytis.

You can extract real userName from JWT and then fetch user info from api service.

Nah, changing it into just the token and getting the token decoded to get the username has the same effect, sadly. I noticed this, fixed it and still the same..

Sent from my iPhone

On 29 Jan 2018, at 04:23, Clark Du notifications@github.com wrote:

@Cogitoergo
The root cause of issue should be that in your nuxtServerInit, fetch didn't have a username, so every server side initialization will fetch user admin instead of rytis.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

For testing, I just set it to 'rytis' and disabled cssSourceMap, it works fine for me.

Many thanks for trying, but I will try to reproduce the error for You in
video format in an hour or so, as I'm still getting the same 403.. Its
really, really strange.. This store setup is a copy paste from @nuxtjs/auth
module,all I did is I modified the Cookie expiration to 1 day, as
previously it was 0, so I had no cookie to get my auth info from on SSR. Or
am I missing something? Perhaps cookie expiration is supposed to be only
session and I should get the auth data in SSR in some other way?

On 29 January 2018 at 08:08, Clark Du notifications@github.com wrote:

For testing, I just set it to 'rytis' and disabled cssSourceMap, it works
fine for me.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/nuxt/nuxt.js/issues/2680#issuecomment-361148962, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AMAWhjGp3koS8I_-UZcoNVmZ4vV4TmGTks5tPWBTgaJpZM4Rq7sJ
.

--
Pagarbiai,
Karolis StakÄ—nas
Skype:karolis.stakenas

@clarkdo

Buddy, I've added you to my gitlab repos for both front and back end. I updated them with the fixes You've mentioned.

I'm extracting the user from token now.. Still the same issue. Altho, it doesn't always come up.

Try reaching / or /admin routes. If I load them server side - it works perfectly. I can refresh the page, it all shows up. But when I navigate out to say /posts route, then coming back to / or /admin via profile dropdown in the upper right corner - it gives me 403..

EDIT: I narrowed the problem down. It seems, that it works fine with SSR, works fine with only Client-Side. Where it fails is to set the client token when rendered server-side. So if I load /admin directly, it works, but every other client-side request fails. If I load web with SSR, press logout, then log back in - everything works well up till first refresh. When I refresh say admin page, I still get the admin page data (list of users), but when I navigate to profile ('/'), it fails with the same 403.

The web is reachable here : https://climatecms.herokuapp.com

user: demo
password: demo

I'm so lost on this, damn.

@jay7793 Man, thank You very much! This actually solved my problem, I don't know why I turned a blind eye on this at first.

Hello, I am also the problem, how to solve

@Cogitoergo
how did you solved this issue will be more helpful if you explain in detail. im new to nuxtjs and facing same issue with token on refresh page.

I solved it exactly how I explained it in the stackoverflow post. Dont be lazy.

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bimohxh picture bimohxh  Â·  3Comments

vadimsg picture vadimsg  Â·  3Comments

danieloprado picture danieloprado  Â·  3Comments

o-alexandrov picture o-alexandrov  Â·  3Comments

VincentLoy picture VincentLoy  Â·  3Comments