What is the best way to incorporate auto refresh in a client-side application using vue.js?

Disclaimer: I have separated my client application (Vue.js) from the server side (DjangoRest). I am utilizing JWT for validating each request sent from the client to the server. Here is how it works - The client forwards user credentials to the server, and if the credentials are valid, the server responds with a refresh token and an access token. The client then stores these tokens. I have defined the refresh token expiry as 1 week and the access token expiry as 30 minutes. Now, I aim to ensure that the access token gets automatically refreshed 15 minutes prior to its expiration. This involves sending the stored refresh token from the client to the server, which will issue new access and refresh tokens in return. How can I incorporate this into the Vuex store? As someone new to web development and Vue.js, any code snippet or detailed explanation would be greatly appreciated.

I have successfully implemented functions like loginUser, logoutUser, registerUser in the store, and they work fine. However, I am facing difficulty in implementing the auto-refresh logic. My assumption is that the client needs to periodically check the remaining expiry time of the access token. When there's around 15 minutes left, the autorefresh function should kick in. Can someone guide me through this process?

Below is a snippet of my Vuex store:

import Vue from 'vue'
import Vuex from 'vuex'
import axiosBase from './api/axios-base'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
     accessToken: '' || null,
     refreshToken: '' || null
  },
  getters: {
    loggedIn (state) {
      return state.accessToken != null
    }
  },
  mutations: {
    loginUser (state) {
      state.accessToken = localStorage.getItem('access_token')
      state.refreshToken = localStorage.getItem('refresh_token')
    },
    destroyToken (state) {
      state.accessToken = null
      state.refreshToken = null
    }
  },
  actions: {
    registerUser (context, data) {
      return new Promise((resolve, reject) => {
        this.axios.post('/register', {
          name: data.name,
          email: data.email,
          username: data.username,
          password: data.password,
          confirm: data.confirm
        })
          .then(response => {
            resolve(response)
          })
          .catch(error => {
            reject(error)
          })
      })
    },
    
    // More action functions can be added here...

  }
})

Any assistance provided would be highly valued. Thank you!

Answer №1

This question presents a variety of solutions to consider, emphasizing the importance of avoiding polling whenever possible.

An effective approach involves leveraging the time-based nature of JWT tokens. By decoding the access token using a library like jwt-decode, you can extract vital information such as the expiration time.

  • Regularly check the token before executing requests to determine if a refresh is needed
  • Utilize `setTimeout` to automatically refresh the token a set number of seconds before it expires

The implementation below provides a guide on how this can be achieved:

export default new Vuex.Store({
  ...
  actions: {
    refreshTokens (context, credentials) {
      // Implementation for exchanging refresh token with access token
      ...
      // Initiate autoRefresh to establish the new timeout
      dispatch('autoRefresh', credentials)
    },
    autoRefresh (context, credentials) {
      const { state, commit, dispatch } = context
      const { accessToken } = state
      const { exp } = jwt_decode(accessToken)
      const now = Date.now() / 1000 
      let timeUntilRefresh = exp - now
      timeUntilRefresh -= (15 * 60) // Refresh 15 minutes prior to expiry
      const refreshTask = setTimeout(() => dispatch('refreshTokens', credentials), timeUntilRefresh * 1000)
      commit('refreshTask', refreshTask) // To potentially cancel task upon logout
    }
  }
})

Answer №2

It's always more reliable to depend on your server response code rather than the expiration time of data. If you encounter a 401 error when trying to access a protected route, prompt for a new access token and retry the request. Should the refresh route also return a 401 error, it is best to request the user to log in again.

An axios interceptor can be incredibly useful in handling these scenarios.

I personally implemented an axios interceptor for one of my projects:

import axios from 'axios'
import router from './router'
import store from './store'
import Promise from 'es6-promise'

const customAxios = axios.create({
  baseURL: 'http://localhost:3000',
  headers: {
    'Content-type': 'application/json'
  }
})

customAxios.interceptors.response.use(
  (response) => {
    return response
  },
  (err) => {
    console.log(err.response.config.url)
    // handle other errors
    if (err.response.status !== 401) {
      return new Promise((resolve, reject) => {
        reject(err)
      })
    }
    // handle login error
    if (err.response.config.url === '/auth/login') {
      return new Proimise((resolve, reject) => {
        reject(err)
      })
    }
    // handle refresh error
    if (err.response.config.url === '/auth/refresh') {
      console.log('ERROR IN REFRESH')
      router.push('/logout')
      return new Promise((resolve, reject) => {
        reject(err)
      })
    }
    // implement token refresh
    return customAxios.get('/auth/refresh', { withCredentials: true }).then(
      success => {
        const config = err.response.config
        config.headers.Authorization = 'Bearer ' + success.data.access_token
        store.commit('setToken', success.data.access_token)
        return customAxios(config)
      }
    )
  }
)

export default customAxios

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Fix issue with nested form in Rails 3.0.9 where remove_fields and add field link functionalities are not functioning properly

I've been watching Ryan Bates' nested_forms episodes 1 & 2 on RailsCasts, successfully implementing the functionality in one project without any issues. However, in a new project using the same reference, the remove and add field functionalit ...

Apply a border to the input field when the user enters or leaves the field, only if the value is

I am managing a few input fields and storing their information in an object. My goal is to click on an input field to focus on it, and if the field is empty or has a length greater than or equal to 0, I want it to display a red border. If I type somethin ...

What is the best way to retrieve the fields from the JSON object generated by the API?

I'm having trouble displaying a specific field from the API response. While I can display the entire API response, I am struggling to show just one particular field of the object. Here is the API being used: The response received is as follows: { ...

Locate the row just before the last one in the table

I have a table that dynamically creates rows, and I need to locate the second to last row in the table. HTML Code <table class="table"> <tr><td>1</td></tr> <tr><td>2</td>< ...

What is the best approach to dynamically bind a style property in Vue.js 3?

I am currently working with vue3 and I am trying to apply dynamic styles. This is how my vue3 template looks: <ul> <li v-for="(question, q_index) in questions" :key="q_index" v-show="question.visible" :style="{ ...

What is the best way to refresh a page after rotating the web page?

Struggling with a challenge in Next JS - can't seem to figure out how to automatically refresh the page when it rotates const app () => { useEffect(()=>{ window.addEventListener("orientationchange", function() { window.locati ...

Combine several objects into one consolidated object

Is there a way to combine multiple Json objects into one single object? When parsing an array from AJAX, I noticed that it logs like this: 0:{id: "24", user: "Joe", pass: "pass", name: "Joe Bloggs", role: "Technical Support", ...} 1:{id: "25", user: "Jim ...

Issues with routing in Node.js using Express

This is my first attempt at programming. I am currently in the process of setting up routing in the following way: var indexRouter = require('./routes/index'); var loginRouter = require('./routes/login'); app.use('/', indexRo ...

An easy guide to dynamically assigning a property using jQuery

I am currently utilizing the toastr plugin and I would like to dynamically set the options using a JSON object that is retrieved from an AJAX call. I am encountering some difficulties in setting the options property and value programmatically. Below is a s ...

The automated Login Pop Up button appears on its own and does not immediately redirect to the login form

Hey guys, I'm struggling with modifying the jquery and html to ensure that when the login button is clicked, the login form pops up instead of displaying another login button. Another issue I am facing is that the login button seems to pop up automati ...

Creating unique page styles using global styles in Next.js

I am facing a dilemma in my NextJS app. On the /home page, I need to hide the x and y overflow, while on the /books page, I want the user to be able to scroll freely. The issue is that Next only allows for one global stylesheet and using global CSS selec ...

Guide to verifying the update of user information with sweet Alert on ASP MVC

I'm currently working on an ASP MVC web application that involves using stored procedures with SQL Server. My goal is to implement a confirmation alert (Sweet Alert) to confirm the data update for a logged-in user. The code for editing works fine wi ...

Error occurred while making a request in React using Axios: TypeError - Unable to retrieve the 'token' property as it is undefined

After successfully receiving a token from logging in with React Redux, I attempted to authorize it using the token. However, an error occurred stating Axios request failed: TypeError: Cannot read property 'token' of undefined. The token is stored ...

Guide on merging non-modular JavaScript files into a single file with webpack

I am trying to bundle a non-modular JS file that uses jQuery and registers a method on $.fn. This JS must be placed behind jQuery after bundling. Here is an example of the structure of this JS file: (function($){ $.fn.splitPane = ... }(JQuery) If y ...

What could be causing my Angular JS application to malfunction?

Presenting myFirstAngularApp.html <html ng-app="store"><!-- The "ng-app" attribute initializes an Angular app. Everything inside this tag is considered part of the Angular app due to ng-app --> <head> <link rel="stylesheet" type= ...

Angular HttpClient does not support cross-domain POST requests, unlike jQuery which does

I am transitioning to Angular 13 and I want to switch from using jQuery.ajax to HttpClient. The jquery code below is currently functional: function asyncAjax(url: any){ return new Promise(function(resolve, reject) { $.ajax({ type: ...

What could be causing the error when attempting to retrieve an index using the window variable?

I'm facing a strange issue where I am trying to utilize a variable that I define as follows: window.params['variable'] = { ... }; Within the function that I am using, the code looks like this: function q() { ... // for example return n ...

analyzing strings by comparing their characters to another string

In a scenario where I have two strings; let's call them string a="a-b-c" and string b="a-b". I am looking to determine whether every letter in string b is present within string a. ...

Using percentages for sizing and margins in CSS

Looking to create a visually appealing page that always fills the entire screen? Check out this code snippet that does just that: #posts{ width:100%; height:auto; background:#666; } .entry{ float:left; margin-left: 4%; margin-top:10 ...

When using EventBus in Vue.js with dynamic elements, the $off method removes event listeners for all reused components at once, rather

I am encountering an issue with a dynamically generated component that can be mounted multiple times on a page simultaneously. When there are 10 food items returned from a feed, there are 10 components created. Each FoodItem component has its own event bus ...