简体   繁体   English

当 jwt 刷新令牌未过期时,React Native 应用程序注销

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

I am using the JWT token to verify my API requests.我正在使用 JWT 令牌来验证我的 API 请求。 Access token expires in 1 minute, and refresh token expires in 1 year.访问令牌在 1 分钟后过期,刷新令牌在 1 年后过期。 After the access token expires, an API request is sent with a refresh token to get a new set of tokens.访问令牌过期后,会发送带有刷新令牌的 API 请求以获取一组新令牌。 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.我正在使用 Axios 拦截器来实现这一点。 Everytyhing seems to work fine for some time. Everytyhing 似乎工作正常一段时间。 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.我假设我在 Axios 拦截器中遗漏了一些东西,或者与异步函数有关。

Error logs "code does not match" in server-side at verifyRefreshToken function, and "Error here" in client-side updateToken function.错误在服务器端的 verifyRefreshToken 函数中记录“代码不匹配”,在客户端的 updateToken 函数中记录“此处出错”。

CLIENT SIDE CODE API.js客户端代码 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服务器端代码 // /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.更新 该错误是由于同时调用多个 API 造成的,并且所有请求都使用旧的刷新令牌请求新的访问令牌。 I solved the issue using code in this link .我使用此链接中的代码解决了该问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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