-
-
Notifications
You must be signed in to change notification settings - Fork 61
Description
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:
FreshcallsvalidateTokenBeforeRequestbefore every request.- Developers define custom expiration logic, such as using
expiresInand a customissuedAtfield. - If the token is expired,
Freshtriggers therefreshTokencallback to refresh it.
Pros:
- Provides maximum flexibility.
- Requires no changes to
OAuth2TokenorFreshinternals. - Fully backward compatible.
Cons:
- Developers must manage and store
issuedAtthemselves.
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:
-
FreshusesexpiresInandissuedAt(if provided) to calculate the token's expiration time:final expirationTime = token.issuedAt + token.expiresIn!; if (DateTime.now().millisecondsSinceEpoch / 1000 >= expirationTime) { await refreshToken(token); }
-
If the token is expired or nearing expiration,
Freshrefreshes it before sending the request. -
If
issuedAtis not provided, developers can handle expiration manually viashouldRefresh.
Pros:
- Simple to use: Enable
refreshIfExpiredand letFreshhandle expiration logic. - Customizable: Developers can provide
issuedAtfor precise expiration handling or rely on default behavior. - Fully backward compatible and opt-in.
Cons:
- Requires modifying the
OAuth2Tokenclass to include an optionalissuedAtfield. - Adds slight complexity to
Freshinternals.