dio_refresh 1.0.5 copy "dio_refresh: ^1.0.5" to clipboard
dio_refresh: ^1.0.5 copied to clipboard

A flutter package for intercepting requests and automatically fetching refresh tokens on API failures

Dio Refresh Interceptor #

pub package license

A Dart package that provides an interceptor for handling automatic token refresh in Dio HTTP client requests. It simplifies the process of managing access and refresh tokens, ensuring that your API requests stay authenticated, even when the access token expires.

Features #

  • Automatic Token Refresh: Automatically refreshes the access token when it expires using a custom refresh callback.
  • Customizable: Define custom logic for determining when a token refresh is needed and how headers are generated.
  • Singleton Token Manager: A singleton TokenManager class for easy token storage and retrieval.
  • Seamless Integration: Designed for use with the Dio HTTP client package.

Installation #

Add the following dependency to your pubspec.yaml file:

dependencies:
  dio: ^5.0.0
  dio_refresh: ^1.0.0
  flutter:
    sdk: flutter

Then, run:

flutter pub get

Getting Started #

Setup DioRefreshInterceptor #

To use the DioRefreshInterceptor, you'll need to define the following callbacks:

  • OnRefreshCallback: Handles the logic for refreshing the access token.
  • ShouldRefreshCallback: Determines whether a response requires a token refresh.
  • TokenHeaderCallback: Generates headers with the access token.

Example #

import 'package:dio/dio.dart';
import 'package:dio_refresh/dio_refresh.dart';

void main() {
  final dio = Dio();

  // Define the TokenManager instance.
  final tokenManager = TokenManager.instance;
  tokenManager.setToken(
    TokenStore(
      accessToken: authToken,
      refreshToken: refreshToken,
    ),
  );

  // Add the DioRefreshInterceptor.
  dio.interceptors.add(DioRefreshInterceptor(
    tokenManager: tokenManager,
    authHeader: (tokenStore) {
      if (tokenStore.accessToken == null) {
        return {};
      }
      return {
        'Authorization': 'Bearer ${tokenStore.accessToken}',
      };
    },
    shouldRefresh: (response) =>
        response?.statusCode == 401 || response?.statusCode == 403,
    onRefresh: (dio, tokenStore) async {
      final response = await dio.post('/refresh', data: {
        'refresh_token': tokenStore.refreshToken,
      });
      return TokenStore(
        accessToken: response.data['accessToken'],
        refreshToken: response.data['refreshToken'],
      );
    },
  ));
}

TokenManager Usage #

The TokenManager class is used to manage your access and refresh tokens.

// Retrieve the singleton instance of TokenManager.
final tokenManager = TokenManager.instance;

// Set new tokens after refreshing.
tokenManager.setToken(TokenStore(
  accessToken: 'newAccessToken',
  refreshToken: 'newRefreshToken',
));

// Access the current access token.
print(tokenManager.accessToken);

API Reference #

DioRefreshInterceptor #

  • DioRefreshInterceptor: A custom interceptor for handling token refresh logic.
    • tokenManager: Instance of TokenManager to manage tokens.
    • authHeader: Callback to generate authorization headers.
    • shouldRefresh: Callback to determine if a refresh is needed.
    • onRefresh: Callback to handle the refresh logic and return a new TokenStore.
    • isTokenValid: Optional callback to validate if a token is still valid.
    • retryInterceptors: Optional list of interceptors to be added to the Dio instance used for retrying requests. This is useful for adding logging or other custom interceptors to the retry mechanism. Note: Do not add another DioRefreshInterceptor to this list, as it may cause an infinite loop.

TokenManager #

  • TokenManager: A singleton class to manage tokens.
    • setToken(TokenStore tokenStore): Updates the stored access and refresh tokens.
    • accessToken: Returns the current access token.
    • refreshToken: Returns the current refresh token.
    • isRefreshing: A ValueNotifier that indicates whether a refresh is in progress.

typedef Callbacks #

  • OnRefreshCallback: Future<TokenStore> Function(Dio, TokenStore)
    • Handles the token refresh logic.
  • ShouldRefreshCallback: bool Function(Response?)
    • Determines if a token refresh is required.
  • TokenHeaderCallback: Map<String, String> Function(TokenStore)
    • Generates authorization headers for requests.
  • IsTokenValidCallback: bool Function(String)
    • Validates if a token is still valid.
    • Default implementation checks if the JWT token is not expired.
    • Called if [shouldRefresh] returns true.
    • Can be customized to implement different token validation strategies.

Example with Custom Token Validation #

final dio = Dio();
dio.interceptors.add(DioRefreshInterceptor(
  tokenManager: tokenManager,
  onRefresh: onRefresh,
  shouldRefresh: shouldRefresh,
  authHeader: authHeader,
  isTokenValid: (token) {
    // Implement custom token validation logic
    try {
      final decodedToken = JwtDecoder.decode(token);
      // Add additional validation checks here
      return !JwtDecoder.isExpired(token) && 
             decodedToken['custom_claim'] == 'expected_value';
    } catch (_) {
      return false;
    }
  },
));

Example with retryInterceptors #

You can provide a list of custom interceptors that will be added to the Dio instance responsible for retrying the request after a successful token refresh. This is particularly useful for logging the retry attempts.

Important: Do not add an instance of DioRefreshInterceptor itself to the retryInterceptors list. Doing so may create an infinite loop if the token refresh request also fails. The constructor includes an assertion to prevent this in debug mode.

First, create a logger interceptor. For example, a simple CustomLogInterceptor:

import 'package:dio/dio.dart';

class CustomLogInterceptor extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    print('Retrying request to: ${options.uri}');
    super.onRequest(options, handler);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    print('Retry successful with status code: ${response.statusCode}');
    super.onResponse(response, handler);
  }

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) {
    print('Retry failed with error: $err');
    super.onError(err, handler);
  }
}

Then, pass an instance of this interceptor to DioRefreshInterceptor:

final dio = Dio();
dio.interceptors.add(DioRefreshInterceptor(
  tokenManager: tokenManager,
  onRefresh: onRefresh,
  shouldRefresh: shouldRefresh,
  authHeader: authHeader,
  retryInterceptors: [CustomLogInterceptor()],
));

Contributing #

Contributions are welcome! Feel free to open issues or submit a pull request on GitHub. For significant changes, please open an issue first to discuss what you would like to change.

9
likes
150
points
814
downloads

Publisher

verified publisheriamdipanshus.in

Weekly Downloads

A flutter package for intercepting requests and automatically fetching refresh tokens on API failures

Repository (GitHub)
View/report issues

Topics

#dio #http #network #interceptor #middleware

Documentation

API reference

License

MIT (license)

Dependencies

dio, flutter, jwt_decoder, synchronized

More

Packages that depend on dio_refresh