[Vuejs]-Vuejs Laravel Passport – what should I do if access token is expired?

1πŸ‘

βœ…

This is what I do. Axios will throw error if the response code is 4xx or 5xx, and then I add an if to check if response status is 401, then redirect to login page.

export default {
    methods: {
        loadData () {
            axios
                .request({
                    method: 'get',
                    url: 'https://mysite/api/route',
                })
                .then(response => {
                    // assign response.data to a variable
                })
                .catch(error => {
                    if (error.response.status === 401) {
                        this.$router.replace({name: 'login'})
                    }
                })
        }
    }
}

But if you do it like this, you have to copy paste the catch on all axios call inside your programs.
The way I did it is to put the code above to a javascript files api.js, import the class to main.js, and assign it to Vue.prototype.$api

import api from './api'
Object.defineProperty(Vue.prototype, '$api', { value: api })

So that in my component, I just call the axios like this.

this.$api.GET(url, params)
    .then(response => {
        // do something
    })

The error is handled on api.js.
This is my full api.js

import Vue from 'vue'
import axios from 'axios'
import router from '@/router'

let config = {
  baseURL : process.env.VUE_APP_BASE_API,
  timeout : 30000,
  headers : {
    Accept         : 'application/json',
    'Content-Type' : 'application/json',
  },
}

const GET = (url, params) => REQUEST({ method: 'get', url, params })
const POST = (url, data) => REQUEST({ method: 'post', url, data })
const PUT = (url, data) => REQUEST({ method: 'put', url, data })
const PATCH = (url, data) => REQUEST({ method: 'patch', url, data })
const DELETE = url => REQUEST({ method: 'delete', url })

const REQUEST = conf => {
  conf = { ...conf, ...config }
  conf = setAccessTokenHeader(conf)

  return new Promise((resolve, reject) => {
    axios
      .request(conf)
      .then(response => {
        resolve(response.data)
      })
      .catch(error => {
        outputError(error)
        reject(error)
      })
  })
}

function setAccessTokenHeader (config) {
  const access_token = Vue.cookie.get('access_token')
  if (access_token) {
    config.headers.Authorization = 'Bearer ' + access_token
  }

  return config
}

/* https://github.com/axios/axios#handling-errors */
function outputError (error) {
  if (error.response) {
    /**
     * The request was made and the server responded with a
     * status code that falls out of the range of 2xx
     */
    if (error.response.status === 401) {
      router.replace({ name: 'login' })
      return
    }
    else {
      /* other response status such as 403, 404, 422, etc */
    }
  }
  else if (error.request) {
    /**
     * The request was made but no response was received
     * `error.request` is an instance of XMLHttpRequest in the browser
     * and an instance of http.ClientRequest in node.js
     */
  }
  else {
    /* Something happened in setting up the request that triggered an Error */
  }
}

export default {
  GET,
  POST,
  DELETE,
  PUT,
  PATCH,
  REQUEST,
}

2πŸ‘

Be as user friendly as possible. Rather than waiting until the token expires, receiving a 401 error response, and then redirecting, set up a token verification check on the mounted hook of your main SPA instance and have it make a ajax call to e.g. /validatePersonalToken on the server, then do something like this in your routes or controller.

Route::get('/validatePersonalToken', function () {
      return ['message' => 'is valid'];
})->middleware('auth:api');

This should return "error": "Unauthenticated" if the token is not valid. This way the user will be directed to authenticate before continuing to use the app and submitting data and then potentially losing work (like submitting a form) which is not very user friendly.

You could potentially do this on a component by component basis rather than the main instance by using a Vue Mixin. This would work better for very short lived tokens that might expire while the app is being used. Put the check in the mounted() hook of the mixin and then use that mixin in any component that makes api calls so that the check is run when that component is mounted. https://v2.vuejs.org/v2/guide/mixins.html

πŸ‘€skribe

0πŸ‘

You could use an interceptor with axios. Catch the 401s and clear the local storage when you do then redirect user to appropriate page.

πŸ‘€LDUBBS

Leave a comment