简体   繁体   中英

React Native app logs out when jwt refresh token is not expired

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM