CodingAleCR / http_interceptor

A lightweight, simple plugin that allows you to intercept request and response objects and modify them if desired.
MIT License
134 stars 67 forks source link

How to use updated token in the repeating request? #42

Closed logblythe closed 3 years ago

logblythe commented 4 years ago

I have been using http and http_interceptor for networking in the flutter application. In the request, I am adding token header from the SecureStorage. Now once the token expires(401 error), I refresh the token, update the token in SecureStorage. But when the request gets repeated old token is used instead of the new one

issue-label-bot[bot] commented 4 years ago

Issue-Label Bot is automatically applying the label question to this issue, with a confidence of 0.67. Please mark this comment with :thumbsup: or :thumbsdown: to give our bot feedback!

Links: app homepage, dashboard and code for this bot.

CodingAleCR commented 4 years ago

Sorry it took me so long to reply, are you trying to retry onResponse or onError? Also how does your interceptor look like? It should work in the way you are trying to do it, so it might be a bug depending on how it's implemented.

logblythe commented 4 years ago

@CodingAleCR , this is my implementation of the retry interceptor. I am making this post request in which I get the token from FlutterSecureStorage package.

Future<dynamic> post(String url, {Map<String, dynamic> params}) async {
    var storage = FlutterSecureStorage();
    var _headers = {"token": await storage.read(key: KEY_TOKEN)};
    print('the params $params');
    var responseJson;
    try {
      final response = await client.post(
        _baseUrl + url,
        headers: _headers,
        body: params,
      );
      responseJson = _returnResponse(response);
    } on SocketException {
      throw FetchDataException({"message": 'No Internet connection'});
    }
    return responseJson;
  }

In my RetryPolicyClass

class ExpiredTokenRetryPolicy extends RetryPolicy {

  @override
  Future<bool> shouldAttemptRetryOnResponse(ResponseData response) async {
    if (response.statusCode == 401) {
      //perform token refresh,get the new token and update it in the secure storage
      var storage = FlutterSecureStorage();
      await storage.write(key: KEY_TOKEN, value: updatedToken);
      return true;
    }
    return false;
  }
}

Now, when the post request is retried after updating the token in the secured storage, same old token is being used. How can I use the updated token when the request is retried.

CodingAleCR commented 4 years ago

And how is your interceptor looking? Are you using Client with Interceptor o just Http with Interceptor?

logblythe commented 3 years ago

@CodingAleCR Apologies for the late reply. I am just logging with the interceptor.


class LoggingInterceptor implements InterceptorContract {
  @override
  Future<RequestData> interceptRequest({RequestData data}) async {
    print(data);
    return data;
  }

  @override
  Future<ResponseData> interceptResponse({ResponseData data}) async {
    print(data);
    return data;
  }
}

and using it like client

final client = HttpClientWithInterceptor.build(
    retryPolicy: ExpiredTokenRetryPolicy(),
    interceptors: [LoggingInterceptor()],
  );
CodingAleCR commented 3 years ago

No worries @logblythe ! In order for the request to be properly updated I would suggest you create an additional interceptor to include the token in your headers.

Here's what's happening right now:

  1. You make the request with manually set header.
  2. Request fails because the token has expired.
  3. Retry policy is checked and updates the token in storage.
  4. Request is retried.
  5. Request fails.

Notice that between steps 3 and 4, at no point the request is updated since the header in the request is static.

To solve this you could use an interceptor, it would make the token in header respond better to changes in storage, for example something like this:

class AuthorizationInterceptor implements InterceptorContract {
  @override
  Future<RequestData> interceptRequest({RequestData data}) async {
    data.headers["token"] = await storage.read(key: YOUR_KEY);
    return data;
  }

  @override
  Future<ResponseData> interceptResponse({ResponseData data}) async {
    return data;
  }
}
logblythe commented 3 years ago

When the retry happens, the updated token doesn't get used. If I make the request again, then only the updated token is being used.

CodingAleCR commented 3 years ago

Yes, thst is because the retry policy updates the storage but the request is still the same as before. The solution would be to use an interceptor to attach the header you need like described above in my previous message.

logblythe commented 3 years ago

I tried using another interceptor as you suggested, but even that interceptor doesn't use the updated value.

CodingAleCR commented 3 years ago

Do you have an example repository that maybe I could take a look, I ask this because I'm doing the exact same thing as you are in my projects but it's working fine for some reason, so I want to figure out what's happening and help you in a better way. I'm also down for a Gitduck session or something like that if you feel more comfortable with it.

logblythe commented 3 years ago

I am not very familiar with the pair programming tools. I tried sharing with Gitduck but it didn't go well. However we can still go with CodeTogether. Let me know what time works for you.

logblythe commented 3 years ago

No worries @logblythe ! In order for the request to be properly updated I would suggest you create an additional interceptor to include the token in your headers.

Here's what's happening right now:

  1. You make the request with manually set header.
  2. Request fails because the token has expired.
  3. Retry policy is checked and updates the token in storage.
  4. Request is retried.
  5. Request fails.

Notice that between steps 3 and 4, at no point the request is updated since the header in the request is static.

To solve this you could use an interceptor, it would make the token in header respond better to changes in storage, for example something like this:

class AuthorizationInterceptor implements InterceptorContract {
  @override
  Future<RequestData> interceptRequest({RequestData data}) async {
    data.headers["token"] = await storage.read(key: YOUR_KEY);
    return data;
  }

  @override
  Future<ResponseData> interceptResponse({ResponseData data}) async {
    return data;
  }
}

I was finally able to use the updated token, via another interceptor. Thanks a lot for helping me out.

CodingAleCR commented 3 years ago

That sounds great, I was just about to reply with a time window for the call. I'm glad to hear it worked out.

renntbenrennt commented 3 years ago

@logblythe hey, how did you solve this problem eventually?