fresh 🍋

Pub build coverage License: MIT GitHub stars


An token refresh library for dart. This package exposes the core components that are common to various refresh token implementations (REST, GraphQL, etc...).

Most users should use one of the integration packages instead:

Package For
fresh_dio dio HTTP client
fresh_graphql gql_link GraphQL

Use package:fresh directly only if you are building a custom integration (e.g. for package:http or another HTTP client).

What's Included

  • FreshMixin<T> - Mixin providing token lifecycle management and single-flight refresh coordination
  • TokenStorage<T> - Interface for reading, writing, and deleting tokens
  • InMemoryTokenStorage<T> - Simple in-memory storage implementation
  • Token / OAuth2Token - Base token class and standard OAuth2 token with expiresAt support
  • AuthenticationStatus - Stream-based auth state (initial, authenticated, unauthenticated)
  • RevokeTokenException - Throw from performTokenRefresh to clear the token and signal logout

Building a Custom Integration

Implement performTokenRefresh on a class using FreshMixin:

class MyHttpFresh extends MyHttpClient with FreshMixin<OAuth2Token> {
  MyHttpFresh({required TokenStorage<OAuth2Token> tokenStorage}) {
    this.tokenStorage = tokenStorage;
  }

  @override
  Future<OAuth2Token> performTokenRefresh(OAuth2Token? token) async {
    final response = await post('/auth/refresh', body: {'refresh_token': token?.refreshToken});
    return OAuth2Token(
      accessToken: response['access_token'],
      refreshToken: response['refresh_token'],
    );
  }

  Future<Response> authenticatedRequest(String path) async {
    var currentToken = await token;

    final response = await get(path, headers: {'Authorization': 'Bearer ${currentToken?.accessToken}'});

    if (response.statusCode == 401) {
      final refreshed = await refreshToken(tokenUsedForRequest: currentToken);
      return get(path, headers: {'Authorization': 'Bearer ${refreshed.accessToken}'});
    }

    return response;
  }
}

refreshToken() handles deduplication automatically - concurrent calls share a single in-flight refresh.

Libraries

fresh