OAuth2.0-based token senseless refresh

Posted by thompsonsco on Wed, 15 Apr 2020 04:08:54 +0200

The vue project at hand now has a requirement for permissions, but the architect asked me to do it very early, but because the urgency is not very high, I was reminded of the project coming online recently, so I rushed to make up for it.This project is based on OAuth2.0 authentication and requires that access_token be carried over the head of each request. If this access_token expires, you need to retrieve an access_token using the existing refresh _token. If even this refresh_token expires, it is truly expired and you need to exit the login page.Refresh_token needs to be senseless, or painless refresh, to get a new access_token.

The code implementation here must be written in the axios interceptor, but there is a difference between writing in the request interceptor and the response interceptor:

1. Write in the request interceptor: Before each request, a check Token's interface is requested to confirm whether the access_token expires. If it does not expire, the original request is directly initiated. If it expires, the original request is re-acquired using the existing refresh_token, and then the original request is initiated.However, there is a disadvantage in writing this way, that is to request the interface of checkToken an extra time before each request. If the network speed is not good, it will result in a bad experience for users and a waste of performance for servers.

2. Write in response interceptor: Until access_token expires and 401 is returned unauthorized, an access_token will not be retrieved using the existing refresh_token.

Finally, after discussing with the back-end, I used the second method, putting checkToken in the back-end and writing the front-end senseless refresh in the response interceptor.

A response interceptor is written here:

import axios from 'axios'

//Create a axios Example
const service = axios.create({
  timeout: 5000, // Request Timeout
  withCredentials:true //Indicates whether credentials are required for cross-domain requests. Default to false
})
var loading;//mask

// Response Interceptor
service.interceptors.response.use( response => { //do what you like }, error => { loading.close(); if (error && error.response) { switch (error.response.status) { case 400: error.message = 'Request Error' break case 401: return doRequest(error); case 403: error.message = 'access denied' break case 404: error.message = `Error Requesting Address: ${error.response.config.url}` break case 408: error.message = 'request timeout' break case 500: error.message = 'Server internal error' break case 501: error.message = 'Service not implemented' break case 502: error.message = 'Bad Gateway' break case 503: error.message = 'Service unavailable' break case 504: error.message = 'gateway timeout' break case 505: error.message = 'HTTP Version not supported' break default: break } } errorLog(error) return Promise.reject(error) } )


  export default service
 

You can see that a method doRequest() is called in response to the 401 value in the interceptor's error callback function;

async function doRequest (error) {
  try {
    const data = await getNewToken();
    var token=data.data.token_type+' '+data.data.access_token;
    sessionStorage.setItem('RequestToken',token);
    const res = await service.request(error.config)
    return res;
  } catch(err) {
    Message({
      message: 'Logon session has expired, please log in again',
      type: 'error',
      duration: 5 * 1000
    })
    sessionStorage.clear();
    router.replace({
      path:"/login"
    });
    return err;
  }
}

The main point here is that these requests must be synchronous, synchronous, synchronous, important things said three times, and axios is asynchronous by default, so you must either use the ES6 async/await statement or the then callback function to remain synchronous.getNewToken() retrieves the access_token method using refresh_token.No, paste it out for reference only.

import qs from 'qs'

async function getNewToken() {
  var refreshToken=sessionStorage.getItem('refreshToken');
  return await axios({
    url: '/OAuth/oauth/token',
      method: 'post',
      headers: {
        'Authorization': 'Basic XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
        'Content-Type':'application/x-www-form-urlencoded'
      },
    data:qs.stringify({
      grant_type:'refresh_token',
      refresh_token : refreshToken
    })
  })
}

See the effect below.For effect, access_token valid time is set to 5s and refresh_token valid time to 10s.The motion picture is like this:

Step by step, get access_token and refresh_token when you log in.It is normal to then take access_token:f0a3******cb64 to access the menuQuery interface.

However, after waiting for more than 5 seconds (no more than 10 seconds, when access_token has expired and refresh_token has not expired), I sent a request for 0304 interface and returned 401 unauthorized indicating that access_token:f0a3******cb64 has expired.

Then use refresh_token to retrieve access_token.

You can see that a new access_token:8332******1c8a has been returned, and with this new access_token, you re-initiate the request for the 0304 interface, at which point you can return the data you need.

 

 

Then wait for more than 5 seconds, when access_token expires and refresh_token expires.The motion picture is like this:

 

The request returned 400 instead of 401, indicating that refresh _token:826b********17d1 is out of date.It's time to quit the login interface and log in again.

Finally, put a general effect map:

Topics: axios Vue network Session