Dio: Repeat a request after getting a fresh JWT token on 401

Created on 12 Aug 2018  路  9Comments  路  Source: flutterchina/dio

I'm considering to switch my app repositories to use dio for all http requests, it looks really promising!

What is the best approach to refresh a JWT token with dio? Is there an easy way to get a fresh token and repeat the request with the fresh token after it got a 401?

pinned

Most helpful comment

@ToniTornado @zinwalin Have you read this example?
https://github.com/flutterchina/dio/blob/flutter/example/interceptorLock.dart , Similarly, you can repeat the request with a new dio instance after you retrieved the new token in onError, like:

  String csrfToken; //top-level variable
  dio.interceptor.response.onError = (DioError error) {
    if(error.response?.statusCode==401){
      Options options=error.response.request;
      // If the token has been updated, repeat directly.
      if(csrfToken!=options.headers["csrfToken"]){
        options.headers["csrfToken"]=csrfToken;
        //repeat
       return  dio.request(options.path,options: options);
      }
      // update token and repeat
      // Lock to block the incoming request until the token updated
      dio.interceptor.request.lock();
      dio.interceptor.response.lock();
      return tokenDio.get("/token").then((d) {
        //update csrfToken
        options.headers["csrfToken"] = csrfToken = d.data['data']['token'];
      }).whenComplete((){
        dio.interceptor.request.unlock();
        dio.interceptor.response.unlock();
      }).then((e){
       //repeat
       return dio.request(options.path,options: options);
      });
    }
    return error;
  };

:octocat: From gitme Android

All 9 comments

same issue here.

Another idea is to decode the JWT token in an interceptor and get it's expiry date; if it is expired or about to expire, it could be refreshed and replaced before the request continues. Sounds like a clean solution, but I haven't tried it yet. As I said, I'm still evaluating which library to use.

And I was curious to hear about experiences from others how to deal with it.

@ToniTornado @zinwalin Have you read this example?
https://github.com/flutterchina/dio/blob/flutter/example/interceptorLock.dart , Similarly, you can repeat the request with a new dio instance after you retrieved the new token in onError, like:

  String csrfToken; //top-level variable
  dio.interceptor.response.onError = (DioError error) {
    if(error.response?.statusCode==401){
      Options options=error.response.request;
      // If the token has been updated, repeat directly.
      if(csrfToken!=options.headers["csrfToken"]){
        options.headers["csrfToken"]=csrfToken;
        //repeat
       return  dio.request(options.path,options: options);
      }
      // update token and repeat
      // Lock to block the incoming request until the token updated
      dio.interceptor.request.lock();
      dio.interceptor.response.lock();
      return tokenDio.get("/token").then((d) {
        //update csrfToken
        options.headers["csrfToken"] = csrfToken = d.data['data']['token'];
      }).whenComplete((){
        dio.interceptor.request.unlock();
        dio.interceptor.response.unlock();
      }).then((e){
       //repeat
       return dio.request(options.path,options: options);
      });
    }
    return error;
  };

:octocat: From gitme Android

@ToniTornado @zinwalin Have you read this example?
https://github.com/flutterchina/dio/blob/flutter/example/interceptorLock.dart , Similarly, you can repeat the request with a new dio instance after you retrieved the new token in onError, like:

  String csrfToken; //top-level variable
  dio.interceptor.response.onError = (DioError error) {
    if(error.response?.statusCode==401){
      Options options=error.response.request;
      // If the token has been updated, repeat directly.
      if(csrfToken!=options.headers["csrfToken"]){
        options.headers["csrfToken"]=csrfToken;
        //repeat
       return  dio.request(options.path,options: options);
      }
      // update token and repeat
      // Lock to block the incoming request until the token updated
      dio.interceptor.request.lock();
      dio.interceptor.response.lock();
      return tokenDio.get("/token").then((d) {
        //update csrfToken
        options.headers["csrfToken"] = csrfToken = d.data['data']['token'];
      }).whenComplete((){
        dio.interceptor.request.unlock();
        dio.interceptor.response.unlock();
      }).then((e){
       //repeat
       return dio.request(options.path,options: options);
      });
    }
    return error;
  };

:octocat: From gitme Android

such method wont working properly when multiple requests enqueued.
see #590

@ToniTornado @zinwalin Have you read this example?
https://github.com/flutterchina/dio/blob/flutter/example/interceptorLock.dart , Similarly, you can repeat the request with a new dio instance after you retrieved the new token in onError, like:

  String csrfToken; //top-level variable
  dio.interceptor.response.onError = (DioError error) {
    if(error.response?.statusCode==401){
      Options options=error.response.request;
      // If the token has been updated, repeat directly.
      if(csrfToken!=options.headers["csrfToken"]){
        options.headers["csrfToken"]=csrfToken;
        //repeat
       return  dio.request(options.path,options: options);
      }
      // update token and repeat
      // Lock to block the incoming request until the token updated
      dio.interceptor.request.lock();
      dio.interceptor.response.lock();
      return tokenDio.get("/token").then((d) {
        //update csrfToken
        options.headers["csrfToken"] = csrfToken = d.data['data']['token'];
      }).whenComplete((){
        dio.interceptor.request.unlock();
        dio.interceptor.response.unlock();
      }).then((e){
       //repeat
       return dio.request(options.path,options: options);
      });
    }
    return error;
  };

:octocat: From gitme Android

such method wont working properly when multiple requests enqueued.
see #590

I also met the same question . In my project , inside "onError" , dio.interceptor.errorLock.lock(); call multipletime.

Same problem, refresh code enter into loop of requests

The following solution/hack seems to work for me. Just keep track of a number (maybe a boolean would also work). When a request is successful or when there is an error while the counter is uneven (1) set the _repeatCounter to 0 (even number).
With the status code responses of the requests you want to repeat, in my case 401, you up the counter (to 1) before retrying the request.

The code is not battle tested (what happens i.e. when you make multiple requests simultaneously?).
But it seems to work well enough.

int _repeatCounter = 0;

...

dio.interceptors.add(InterceptorsWrapper(onError: (error) async {
    if (error.response.statusCode == 401 && _repeatCounter.isEven) {
        dio.interceptors.requestLock.lock();
        dio.interceptors.responseLock.lock();
        await refreshCredentials();
        dio.interceptors.requestLock.unlock();
        dio.interceptors.responseLock.unlock();
        _repeatCounter++;
        return await dio.request(
            error.request.path,
            queryParameters: error.request.queryParameters,
            data: error.request.data,
            options: Options(method: error.request.method),
        );
    }
    _repeatCounter = 0;
    return error;
}, onResponse: (response) async {
    if (response.statusCode == 200) {
        _repeatCounter = 0;
    }
}));

Any Update?

Was this page helpful?
0 / 5 - 0 ratings