Skip to content

Instantly share code, notes, and snippets.

@Darkzarich
Last active April 25, 2021 23:54
Show Gist options
  • Save Darkzarich/5f1bd8b7fe6ad83b4c5fce7a5b05b75a to your computer and use it in GitHub Desktop.
Save Darkzarich/5f1bd8b7fe6ad83b4c5fce7a5b05b75a to your computer and use it in GitHub Desktop.
Frontend Token-Based Authentication in Vue

Frontend Token-Based Authentication in Vue

User registration:

RegisterUser.vue:

methods: {
  register() {
    this.$store.dispatch('register', {
      name: this.name,
      email: this.email,
      password: this.password
    })
    .then(() => {
      this.$router.push({name: 'dashboard'}) // redirecting the user to a protected route
    })
    .catch(err => {
      this.error = err.response.data.error
    })
  }
}

User login:

LoginUser.vue:

methods: {
  login() {
    this.$store.dispatch('login', {
      email: this.email,
      password: this.password
    })
    .then(() => {
      this.$router.push({name: 'dashboard'}) // redirecting the user to a protected route
    })
    .catch(err => {
      this.error = err.response.data.error
    })
  }
}

Vuex state

export default new Vuex.Store({
  state: {
    user: null
  },
  mutations: {
    SET_USER_DATA (state, userData) {
      state.user = userData
      localStorage.setItem('user', JSON.stringify(userData)) // userData contains 'token'
      axios.defaults.headers.common['Authorization'] = 
        `Bearer ${userData.token}` // authorization header for the backend
    },
    CLEAR_USER_DATA () {
      state.user = null
      localStore.removeItem('user') // clear our token from the browser
      axios.defaults.headers.common['Authorization'] = null // clear axios auth header
      
      // or
      
      localStore.removeItem('user')
      location.reload() // this approach will let not bother with the other state as everything gets cleared on a reload
    }
  },
  actions: {
    register({ commit }, credentials) {
      return axios.post('//localhost:3000/register', credentials)
        .then({data} => {
          commit('SET_USER_DATA', data);
        })
    },
    login({ commit }, credentials) {
      return axios.post('//localhost:3000/login', credentials)
        .then({data} => {
          commit('SET_USER_DATA', data);
        })
    },
    logout({ commit }) { // called on logout button click
      commit('CLEAR_USER_DATA')
    }
  }
})

Router

guarded route:

{
  path: '/dashboard',
  name: 'dashboard',
  component: Dashboard,
  meta: {needAuth: true}
}

route guard:

router.beforeEach((to, from, next) => {
  const loggedIn = localStorage.getItem('user')
  
  // check if any of the routes have `meta.needAuth` truthy
  if (to.matched.some(record => record.meta.needAuth) && !loggedIn) {
    next('/') // redirecting to home
  }
  next() // moving further
})

main.js

new Vue({
  router,
  store,
  created () {
    // automatic login logic  
    const userString = localStorage.getItem('user')
    if (userString) {
      const userData = JSON.parse(userString)
      this.$store.commit('SET_USER_DATA', userData);
    }
    
    // intercept all 401 status errors forcing logout
    axios.interceptors.response.use( 
      response => response,
      error => {
        if (error.response.status === 401) {
          this.$store.dispatch('logout')
        }
        return Promise.reject(error)
      }
    )
  },
  render: h => h(App)
}).$mount('#app')

TIP:

Easy error with reason handling in catch block:

.catch((error) => {
  const reason = error.response 
    ? error.response.data.reason 
    : undefined
  console.error(reason || error.message)
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment