3

I'm using laravel passport for API's and nuxt.js for frontend after a successful login if I refresh the page the user is not authenticated anymore and loggedIn returns false, its my first nuxt.js project so I have no idea how to deal with that, any advise is appreciated

login.vue

<script>
import { mapActions } from 'vuex'

export default {
  data() {
    return {
      email: "",
      password: ""
    }
  },
  methods:{
    async login(){
      const succesfulLogin = await this.$auth.loginWith('local', {
        data: {
          email: this.email,
          password: this.password
        },
      })
      this.$store.commit("saveUser",succesfulLogin.data)
      this.$store.commit("saveToken", succesfulLogin.data.token)

      if (succesfulLogin) {
        await this.$auth.setUser({
          email: this.email,
          password: this.password,
        })
        this.$router.push('/profile')
      }
    }
  }
}
</script>

store/index.js

export const state = () => ({
  user:{},
  token: ""
})

export const mutations = {
  saveUser(state, payload) {
    state.user=payload;
  },
  saveToken(state, token) {
    state.token= token
  }
 
}
export const actions = {
     saveUserAction({commit}, UserObject){
         commit('saveUser');
     },
     logoutUser({commit}){
        commit('logout_user')
     }
}
export const getters = {
  getUser: (state) => {
    return state.user
  },
  isAuthenticated(state) {
    return state.auth.loggedIn
  },

  loggedInUser(state) {
    return state.user.user
  }
}

after a successful login enter image description here

after refreshing the page enter image description here

kissu
  • 40,416
  • 14
  • 65
  • 133
Dr.Noob
  • 319
  • 6
  • 17

2 Answers2

3

We do use a global middleware right after my auth module authentication

/middleware/global.js

export default async ({ app, store }) => {
  if (store?.$auth?.$state?.loggedIn) {
    if (!app.$cookies.get('gql.me_query_expiration')) {
      // do some middleware logic if you wish

      await app.$cookies.set('gql.me_query_expiration', '5min', {
        // maxAge: 20,
        maxAge: 5 * 60,
        secure: true,
      })
    }
  }
}

nuxt.config.js

router: {
  middleware: ['auth', 'global'],
},

We're using cookie-universal-nuxt for handling secure cookies quickly, working great!

While accessing or refreshing the webapp (we do redirect to the /login page if not authenticated) and we use this basic GraphQL configuration where the cookie is needed.

/plugins/nuxt-apollo-config.js

export default ({ app }) => {
  const headersConfig = setContext(() => ({
    credentials: 'same-origin',
    headers: {
      Authorization: app.$cookies.get('auth._token.local'), // here
    },
  }))

  [...]
}

Checking gql.me_query_expiration allows us to see if the user has authenticated lately/is currently authenticated or if he needs to refresh his token.
And auth._token.local is our actual JWT token, provided by the auth module.

As told above, it is more secure to have a secure cookie than some localStorage, this is also why we are not using it

nuxt.config.js

auth: {
  localStorage: false, // REALLY not secure, so nah
  ...
}
kissu
  • 40,416
  • 14
  • 65
  • 133
  • thanks, is using cookies in this case irrelevent to the backend? because I'm using laravel passport for authentication in the backend? – Dr.Noob Jun 29 '21 at 08:35
  • 1
    @AIB you can use whatever you want (cookie or `localStorage`) on the client to my knowledge, since this one is generated by yourself and not your actual backend. – kissu Jun 29 '21 at 08:36
1

You can just use localStorage and implement it yourself e.g.:

  saveToken(state, token) {
    localStorage.setItem("authToken", token);
    state.token= token
  },
  saveUser(state, payload) {
    localStorage.setItem("authUser", payload);
    state.user=payload;
  },

And then retrieving the localStorage when initializing your store you need to do something like this:

export const state = () => {
  const localUser = localStorage.getItem("authToken")
  const localToken = localStorage.getItem("authUser")
  let user = {}
  let token = ""
  if (localUser) user = localUser
  if (localToken) token = localToken
  return {
    user: user,
    token: token
  }
}

As @mbuechmann pointed out, be aware of the security risk when storing sensitive information in localStorage. Better to use cookies for tokens, but localStorage is the 'simple' solution.

or use a package like nuxt-vuex-localstorage

Laurens
  • 2,596
  • 11
  • 21
  • Localstorage is not a proper storage for sensitive data. This answer explains it in detail: https://stackoverflow.com/questions/3718349/html5-localstorage-security – mbuechmann Jun 29 '21 at 08:04
  • @Laurens thanks, I just tried and unfortunately it didn't work, I just have now 'authToken' with a token in local storage but still with refreshing user is no more authenticated – Dr.Noob Jun 29 '21 at 08:07
  • @AIB this is not the entire solution, you also must retrieve the localStorage with `localStorage.getItem` when initializing the store, read up on how localStorage works – Laurens Jun 29 '21 at 08:10
  • @mbuechmann While you are correct that localstorage is not the safest way, if you have an xss vulnerability, you have bigger problems. Read https://pragmaticwebsecurity.com/articles/oauthoidc/localstorage-xss.html. But the best way would be to use cookies instead of localstorage. You can do this with the nuxt/auth module: https://auth.nuxtjs.org/schemes/cookie – Laurens Jun 29 '21 at 08:15
  • @ Laurens is using cookies in this case works with laravel passport or it is irrelevant to the backend? – Dr.Noob Jun 29 '21 at 08:20