Hi all!
I am trying to solve the problem of updating the access token without using third-party extensions.
All HTTP requests in the application are processed centrally, using a separate action.
If the TokenExpired error came in response to the request, then I send a token update request (access & refresh) and repeat the request again.
The problem is that the application components start sending requests almost simultaneously. The first request sends a refresh token, after which the old tokens on the server are deleted and when a request comes from another component, the server does not find the received tokens and sends a TokenError in response. The application, seeing a message about an incorrect token, redirects the user to the authorization page [application after restarting and not finding the token in localStorage, push to the authorization screen].
Help solve the problems, please. I will be glad of any good advice. Thanks in advance for the answers!
App HTTP request
async readUsers({rootState, state, getters, dispatch}, payload) {
const token = rootState.token.access if(token) { state.usersProcess = true let params = '' if(payload) { for( let param in payload) { params.length ? params += '&' + param + '=' + payload[param] : params = '?' + param + '=' + payload[param] } } const request = await dispatch('fetchRequest', { address: `${rootState.config.httpServer}/users${params}`, method: 'GET', token: true }) if(!request.error) { state.users = request state.usersMsg = { success: true } } else { state.usersMsg = { success: false } state.usersMsg.text = getters.getTranslate[request.error] || request.error } state.usersProcess = false } else { state.usersMsg = { success: false, text: getters.getTranslate.EUnauthRequest } }
}
Action for processing app HTTP requests
async fetchRequest({rootState, dispatch}, payload) {
let parameters = { method: payload.method, cache: 'no-cache', referrerPolicy: 'origin-when-cross-origin', headers: new Headers() } if(payload.token) { parameters.headers.append('Authorization', 'Bearer ' + rootState.token.access) } if(payload.data) { if(payload.type) { payload.type == 'json' ? parameters.body = JSON.stringify(payload.data) : parameters.body = payload.data } else { parameters.body = JSON.stringify(payload.data) } } try { const request = await fetch(payload.address, parameters) const response = await request.text() const result = JSON.parse( response ) if(request.ok) { return result } else if(result.details) { if(result.details == 'TokenExpired') { dispatch('refreshToken').then(() => { if(rootState.token.access) { dispatch('fetchRequest', payload) } }) } else if(result.details == 'TokenError') { localStorage.clear() document.location.reload() } return { error: result.details } } else if(result.message) { return { error: result.message } } else { return { error: 'EUnknown' } } } catch { return { error: 'EConnection' } }
}
Refresh token action
async refreshToken({rootState, state, dispatch}) {
const request = await dispatch('fetchRequest', { address: `${rootState.config.httpServer}/refresh_tokens`, method: 'POST', data: `"${rootState.token.refresh}"`, type: 'string' }) if(request.tokens) { state.access = request.tokens.access state.refresh = request.tokens.refresh localStorage.setItem('TOKEN_ACCESS', state.access) localStorage.setItem('TOKEN_REFRESH', state.refresh) } else { state.access = null state.refresh = null localStorage.removeItem('TOKEN_ACCESS') localStorage.removeItem('TOKEN_REFRESH') }
}
Completely confused with asynchronous requests