I am using the JWT token to verify my API requests. Access token expires in 1 minute, and refresh token expires in 1 year. After the access token expires, an API request is sent with a refresh token to get a new set of tokens. A new set of tokens are only sent if the refresh token is valid, and exists in the database. I am using Axios interceptor to achieve this. Everytyhing seems to work fine for some time. However, it logs me out even when the refresh token is valid and does exist in DB. I am assuming I am missing something in Axios interceptor or has to do with async functions.
Error logs "code does not match" in server-side at verifyRefreshToken function, and "Error here" in client-side updateToken function.
CLIENT SIDE CODE API.js
// Response interceptor for API calls
API.interceptors.response.use((response) => {
return response
}, async (error) => {
// reject promise if network error
if (!error.response) {
console.log("Network Error");
return Promise.reject(error);
}
const originalRequest = error.config;
console.log(store.getState().auth)
// if access token is expired
if (error.response.status === 403 && error.response.data.message == "token expired") {
// var refreshToken = await getRefreshToken() // get refresh token from local storage
var refreshToken = await store.getState().auth.refreshToken
// restore tokens using refresh token
await store.dispatch(await updateToken(refreshToken)) // get new set of tokens from server and store tokens in redux state
// var newAccessToken = await getToken() // get token from local storage
var newAccessToken = await store.getState().auth.accessToken
if(newAccessToken != null){
originalRequest.headers["Authorization"] = `Bearer ${newAccessToken}`;
return API(originalRequest)
}
return Promise.reject(error);
}
// if refresh token is expired or does not match
if (error.response.status === 403 && error.response.data.message == "false token") {
socketDisconnect() // disconnect socket connection
signOut() // remove tokens from local storage
store.dispatch(logOut()) // set tokens in redux as null
return Promise.reject(error);
}
return Promise.reject(error);
});
updateTokenFunction
export const updateToken = (rt) => {
return async (dispatch) => {
const data = await API.post('/auth/refreshToken', {
token: rt
})
.then(async res => {
var accessToken = res.data.accessToken
var refreshToken = res.data.refreshToken
await storeToken(accessToken) // store access token in local storage
await storeRefreshToken(refreshToken) // store refresh token in local storage
dispatch(restoreToken({accessToken, refreshToken})) // store token in redux state
})
.catch(err => {
console.log("err here" + err) // LOG SHOWS ERROR HERE
})
}
}
SERVER SIDE CODE // /auth/refreshToken
// POST: /api/auth/refreshToken
router.post('/', (req, res) => {
var { token } = req.body
if(!token) res.status(403).send({"status":false, "message": "false token", "result": ""})
verifyRefreshToken(token)
.then(async data => {
var userName = data.userName
// get new tokens
var accessToken = await getAccessToken(userName)
var refreshToken = await getRefreshToken(userName)
res.json({"status":true, "message": "token verified", "accessToken": accessToken, "refreshToken": refreshToken})
})
.catch(err => {
console.log(err);
res.status(403).send({"status":false, "message": "false token", "result": ""})
})
});
To generate new refresh token
// generate refresh token
const getRefreshToken = (userName) => {
return new Promise((resolve, reject) => {
var secret = process.env.REFRESH_TOKEN_SECRET
var options = { expiresIn: '1y' }
jwt.sign({userName},secret , options, (err, token) => {
if(err) reject("error")
var data = {"userName": userName, "token": token}
// delete all expired token from database
dbQueries.deleteRefreshToken(data, result => {
})
// add refresh token to database
dbQueries.addRefreshToken(data, result => {
if(result == "success"){
console.log("added token " + token);
resolve(token)
}else{
reject("failure")
}
})
});
})
}
Verifying refresh token
// verify access token
const verifyRefreshToken = (token) => {
return new Promise((resolve, reject) => {
var secret = process.env.REFRESH_TOKEN_SECRET
if(!token) return reject("no token")
jwt.verify(token, secret, (err, user) => {
if(err){
return reject(err)
}
// check if the verified token and token from database matches
var data = {"userName": user.userName}
dbQueries.getRefreshToken(data, result => {
if(result.length == 0){
return reject("no data")
}
if(token === result[0].token){
resolve(user)
} else{
reject("code does not match") // LOGS THIS ERROR
}
})
})
})
}
UPDATE The error was due to multiple API calls at the same time, and all had requested for a new access token, with old refresh tokens. I solved the issue using code in this link .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.