How to Refresh Token Using Interceptor In Dio for Flutter ??

5 min read
How to Refresh Token Using Interceptor In Dio for Flutter
How to Refresh Token Using Interceptor In Dio for Flutter

In order to request a new access token, you need to use the post method along with form data and required Dio’s options content-type and headers. So in this article, we will go through How to Refresh Token Using Interceptor In Dio for Flutter.

How to Refresh Token Using Interceptor In Dio for Flutter ??

After a successful request, if you get the response status code is 200, then you will get a new access token value along with a refresh token value and save them in any storage you prefer to use. For example, Shared preferences.

Once you have a new access token saved, you can use it to fetch data using the get method shown in the same code below.

onError(DioError error) async {
    if (error.response?.statusCode == 401) {
      Response response;
      var authToken = base64
          .encode(utf8.encode("username_value" + ":" + "password_value"));
      FormData formData = new FormData.from(
          {"grant_type": "refresh_token", "refresh_token": refresh_token_value});
      response = await dio.post(
        url,
        data: formData,
        options: new Options(
            contentType: ContentType.parse("application/x-www-form-urlencoded"),
            headers: {HttpHeaders.authorizationHeader: 'Basic $authToken'}),
      );
      if (response.statusCode == 200) {
        response = await dio.get(
          url,
          options: new Options(headers: {
            HttpHeaders.authorizationHeader: 'Bearer access_token_value'
          }),
        );
        return response;
      } else {
        print(response.data);
        return null;
      }
    }
    return error;
  }

I solved it using interceptors in the following way:-

Future<Dio> getApiClient() async {
  token = await storage.read(key: USER_TOKEN);
  _dio.interceptors.clear();
  _dio.interceptors
      .add(InterceptorsWrapper(onRequest: (RequestOptions options) {
    // Do something before request is sent
    options.headers["Authorization"] = "Bearer " + token;
    return options;
  },onResponse:(Response response) {
      // Do something with response data
      return response; // continue
  }, onError: (DioError error) async {
    // Do something with response error
    if (error.response?.statusCode == 403) {
      _dio.interceptors.requestLock.lock();
      _dio.interceptors.responseLock.lock();
      RequestOptions options = error.response.request;
      FirebaseUser user = await FirebaseAuth.instance.currentUser();
      token = await user.getIdToken(refresh: true);
      await writeAuthKey(token);
      options.headers["Authorization"] = "Bearer " + token;

      _dio.interceptors.requestLock.unlock();
      _dio.interceptors.responseLock.unlock();
      return _dio.request(options.path,options: options);
    } else {
      return error;
    }
  }));
  _dio.options.baseUrl = baseUrl;
  return _dio;
}

A Simple Solution that looks like the following:

this.api = Dio();

    this.api.interceptors.add(InterceptorsWrapper(
       onError: (error) async {
          if (error.response?.statusCode == 403 ||
              error.response?.statusCode == 401) {
              await refreshToken();
              return _retry(error.request);
            }
            return error.response;
        }));

Basically what is going on is it checks to see if the error is a 401 or 403, which are common-auth errors, and if so, it will refresh the token and retry the response.

My implementation of refreshToken() looks like the following, but this may vary based on your API:

Future<void> refreshToken() async {
    final refreshToken = await this._storage.read(key: 'refreshToken');
    final response =
        await this.api.post('/users/refresh', data: {'token': refreshToken});

    if (response.statusCode == 200) {
      this.accessToken = response.data['accessToken'];
    }
  }

We can also use flutter secure storage to store the accessToken. My retry method looks like the following:

If you want to easily allows add the access_token to the request.

The recommended thing is to, add the following function when you declare your dio router with the onError callback:

onRequest: (options) async {
          options.headers['Authorization'] = 'Bearer: $accessToken';
          return options;
        },

Consider a code snippet like the below:

RestClient client;
        
          static BaseOptions options = new BaseOptions(
            connectTimeout: 5000,
            receiveTimeout: 3000,
          );
          RemoteService() {
            // or new Dio with a BaseOptions instance.
            final dio = Dio(options);
        
            dio.interceptors
                .add(InterceptorsWrapper(onRequest: (RequestOptions options) async {
              // Do something before request is sent
              return options; //continue
            }, onResponse: (Response response) async {
              // Do something with response data
              return response; // continue
            }, onError: (DioError error) async {
              // Do something with response error
              if (error.response.statusCode == 401) {
                Response response =
                    await dio.post("http://addrees-server/oauth/token",
                        options: Options(
                          headers: {
                            'Authorization': ApiUtils.BASIC_TOKEN,
                            'Content-Type': ApiUtils.CONTENT_TYPE,
                          },
                        ),
                        queryParameters: {
                      "grant_type": ApiUtils.GRANT_TYPE,
                      "username": AppConstants.LOGIN,
                      "password": AppConstants.PASSWORD
                    });
        
                Sessions.access_token = response.data['access_token'];
        
                error.response.request.queryParameters
                    .update('access_token', (value) => Sessions.access_token);
                RequestOptions options = error.response.request;
        
                return dio.request(options.path, options: options); //continue
              } else {
                return error;
              }
            }));
            client = RestClient(dio);
          }

Conclusion:

Thanks for reading !!! Stay Connected 馃檪

In this article, we have been through how to refresh token using interceptor in Dio for flutter.

Keep Learning !!! Keep Fluttering !!!

Flutter Agency聽is our portal Platform dedicated to Flutter Technology and聽Flutter Developers. The portal is full of cool resources from Flutter like聽Flutter Widget聽Guide,聽Flutter Projects,聽Code libs聽and etc.

Flutter Agency聽is one of the most popular online portals dedicated to聽Flutter Technology and daily thousands of unique visitors come to this portal to enhance their knowledge of聽Flutter.

Leave a Reply