Mystery problem with refresh token in react native

Viewed 142

I would try to explain as much as I can about my issue.

Background: I have a react native app which talks to a .Net framewrok backend (Web API) using microsoft token based authentication. The token expires in 30 minutes and then when the next call is made the server responds with 401 and then refresh token api is being called and then the new refresh token is created and app uses this. While we are waiting for refresh token the other calls are qued up on the client and once the token has arrived they are called with new token.

Problem (mystery): The problem is that sometimes the status from the server is 0. We have tried to find out why, but with no luck. The pattern we are also seeing is that when the calls for refresh token so the backend deletes the old token and generates a new one. But when the app calls new apis it still uses the same old token and not the new one. This doesn't happen always. Could it be that the qued up calls are not being qued?? And something is wrong there?

Tools: We are using four dedicated servers running IIS 10. We have a load balancer to balance the load. On front-end we are using React native 0.64.2. We are using react-native-ssl-pinning to make the calls.

The Code: So here is the code

async request( route: string, action: string, params: any = null, isJson: boolean = true, ) {

var options = await this.handleOption(action, params, isJson)

route = Config.API_BASE_URI + route

return new Promise(async resolve => {
  try {
    var response = await fetchSSLPinning(route, options)
    return resolve(response)
  } catch (e) {
    // // console.log(e)
    var originalRequest = {
      route,
      options,
    }
    var p = this.handleError(e, originalRequest)
    return resolve(p)
  }
})
},

Code to handle error

async handleError(error: any, originalRequest: any): Promise { const errorResponse = error

if (this.isTokenExpiredError(errorResponse)) {
  return this.resetTokenAndReattemptRequest(error, originalRequest)
}
this.dispatchError(true, errorResponse.status)
return Promise.reject(error)
},

Code to get new refresh token

  async resetTokenAndReattemptRequest(error: any, originalRequest: any) {
    try {
      
      const resetToken = await TokenStorage.getToken() 
      if (!resetToken) {
        return Promise.reject(error)
      }

     
      const retryOriginalRequest = new Promise(resolve => {
        this.addSubscriber((accessToken: string) => {
          var options = {
            ...originalRequest.options,
            headers: {
              ...originalRequest.options.headers,
              authorization: 'Bearer ' + accessToken,
            },
          }
         
          resolve(fetchSSLPinning(originalRequest.route, options))
        })
      })
      if (!this.isAlreadyFetchingAccessToken) {
        this.isAlreadyFetchingAccessToken = true
        const refreshToken = await TokenStorage.getRefreshToken()
        const userName = await TokenStorage.getUsername()

        const response = await AuthenticationService.refreshToken(
          refreshToken,
          userName,
        )

        if (!response) {
         
          this.isAlreadyFetchingAccessToken = false
          this.dispatchError(true, '404')
          return Promise.reject(error)
        }
        
        await TokenStorage.storeTokenInfo(
          response.access_token,
          response.refresh_token,
          response.userName,
        ) // save the newly refreshed token for other requests to use

        const newToken = response.access_token
        this.isAlreadyFetchingAccessToken = false
        this.onAccessTokenFetched(newToken)
      }
      return retryOriginalRequest
    } catch (err) {
      
      this.isAlreadyFetchingAccessToken = false
      this.dispatchError(true, err.status)
      return Promise.reject(err)
    }
  },

Code to run all in subscriber

onAccessTokenFetched(accessToken: string) {
    // When the refresh is successful, we start retrying the requests one by one and empty the queue
    this.subscribers.forEach((callback: any) => {
      callback(accessToken)
    })
    this.subscribers = []
  },

I do understand that it is hard to come up with a solution but putting it out there as we have been stuck with this for very loog.

0 Answers
Related