Skip to content

Add support for proactive token validation #110

@AristideVB

Description

@AristideVB

Description:

The Fresh package currently supports reactive token refresh via the shouldRefresh callback, which is triggered after requests. While this approach works well, many use cases (e.g., OAuth2 with expiresIn) can benefit from proactive token validation, where tokens are validated and refreshed before requests are made.

Proactively validating tokens:

  • Prevents sending expired tokens, reducing unnecessary backend errors (e.g., AUTH_NOT_AUTHENTICATED in GraphQL, 401 Unauthorized or invalid_token in REST APIs)
  • Improves efficiency by avoiding retries

This feature request proposes two potential solutions to enable proactive token validation in Fresh.


Proposed Solutions:

Here’s the updated GitHub issue with Option 1 (developer-managed logic) and Option 2 (built-in expiration handling with refreshIfExpired and enhanced OAuth2Token).

Option 1: Developer-Managed Expiration Logic

Introduce a validateTokenBeforeRequest callback, giving developers complete control over token expiration handling.

Example:

Fresh<OAuth2Token>(
  tokenStorage: tokenStorage,
  refreshToken: (token, client) async {
    // Refresh token logic
  },
 shouldRefresh: (response) {
 // GraphQL or REST should refresh logic
        return response.errors?.first.extensions?['code'] ==
            'AUTH_NOT_AUTHENTICATED';
      },
  validateTokenBeforeRequest: (token) async {
    final now = DateTime.now().millisecondsSinceEpoch / 1000;
    final issuedAt = await getIssuedAtFromStorage(); // Developer handles issuedAt
    return token.expiresIn != null && issuedAt != null
        ? now >= (issuedAt + token.expiresIn!)
        : false;
  },
);

How It Works:

  1. Fresh calls validateTokenBeforeRequest before every request.
  2. Developers define custom expiration logic, such as using expiresIn and a custom issuedAt field.
  3. If the token is expired, Fresh triggers the refreshToken callback to refresh it.

Pros:

  • Provides maximum flexibility.
  • Requires no changes to OAuth2Token or Fresh internals.
  • Fully backward compatible.

Cons:

  • Developers must manage and store issuedAt themselves.

Option 2 Built-In Expiration Handling with Enhanced OAuth2Token

Introduce a refreshIfExpired boolean and enhance OAuth2Token with an optional issuedAt field for built-in expiration logic.

Example:

class OAuth2Token {
  const OAuth2Token({
    required this.accessToken,
    this.tokenType = 'bearer',
    this.expiresIn,
    this.refreshToken,
    this.scope,
    this.issuedAt, // Optional field for token issuance time
  });

  final String accessToken;
  final String? tokenType;
  final int? expiresIn; // Duration in seconds
  final String? refreshToken;
  final String? scope;
  final int? issuedAt; // Timestamp or DateTime when the token was issued
}

Fresh<OAuth2Token>(
  tokenStorage: tokenStorage,
  refreshToken: (token, client) async {
    // Refresh token logic
  },
   shouldRefresh: (response) {
 // GraphQL or REST should refresh logic
        return response.errors?.first.extensions?['code'] ==
            'AUTH_NOT_AUTHENTICATED';
      },
  refreshIfExpired: true, // Enable built-in expiration handling
);

How It Works:

  1. Fresh uses expiresIn and issuedAt (if provided) to calculate the token's expiration time:

    final expirationTime = token.issuedAt + token.expiresIn!;
    if (DateTime.now().millisecondsSinceEpoch / 1000 >= expirationTime) {
      await refreshToken(token);
    }
  2. If the token is expired or nearing expiration, Fresh refreshes it before sending the request.

  3. If issuedAt is not provided, developers can handle expiration manually via shouldRefresh.

Pros:

  • Simple to use: Enable refreshIfExpired and let Fresh handle expiration logic.
  • Customizable: Developers can provide issuedAt for precise expiration handling or rely on default behavior.
  • Fully backward compatible and opt-in.

Cons:

  • Requires modifying the OAuth2Token class to include an optional issuedAt field.
  • Adds slight complexity to Fresh internals.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions