handle401 static method

Future<Result<String>> handle401(
  1. Failure failure,
  2. Future<Result<String>> retryAction(),
  3. String endpoint
)

Handles 401 UnAuthorization errors by attempting to refresh the token or calling the logout callback.

Implementation

static Future<Result<String>> handle401(
  Failure failure,
  Future<Result<String>> Function() retryAction,
  String endpoint,
) async {
  // 1. Check if we are already on the refresh endpoint to avoid infinite loop
  if (refreshTokenEndpoint != null && (endpoint == refreshTokenEndpoint || endpoint.endsWith(refreshTokenEndpoint!))) {
    PackageLogger.error('401 on refresh endpoint - calling logout');
    onLogout?.call();
    return failure;
  }

  // 2. Check if current endpoint matches the whitelist (if provided)
  // If list is empty/null: Trigger refresh for ALL 401 errors
  bool isInWhitelist = refreshWhitelist == null || refreshWhitelist!.isEmpty || refreshWhitelist!.any((e) => endpoint == e || endpoint.endsWith(e));

  // 3. If not in whitelist, directly call logout
  if (!isInWhitelist) {
    PackageLogger.warning('Endpoint not in whitelist - calling logout');
    onLogout?.call();
    return failure;
  }

  // 4. If refresh endpoint is not configured, call logout
  if (refreshTokenEndpoint == null || refreshTokenEndpoint!.isEmpty) {
    PackageLogger.error('No refresh endpoint configured - calling logout');
    onLogout?.call();
    return failure;
  }

  // 5. Attempt to refresh the token
  try {
    PackageLogger.log('Attempting to refresh token via $refreshTokenEndpoint');

    // Get refresh token body from user callback or use empty body
    final refreshBody = getRefreshTokenBody?.call() ?? {};

    // Call the refresh token endpoint
    final refreshResult = await post(
      refreshTokenEndpoint!,
      body: refreshBody,
      isRetry: true, // Mark as retry to prevent infinite loop
    );

    // Check if refresh was successful
    if (refreshResult is Success<String>) {
      PackageLogger.success('Token refresh successful');

      // Parse the response to extract new tokens
      try {
        final responseData = jsonDecode(refreshResult.value);

        // Notify user to save the new tokens via callback
        if (onTokenRefreshed != null) {
          await onTokenRefreshed!(responseData is Map<String, dynamic> ? responseData : {'response': responseData});
        }

        // Retry the original request with new tokens
        PackageLogger.log('Retrying original request after token refresh');
        return await retryAction();
      } catch (e) {
        PackageLogger.error('Failed to parse refresh token response: $e');
        onLogout?.call();
        return failure;
      }
    } else {
      // Refresh failed
      PackageLogger.error('Token refresh failed - calling logout');
      onLogout?.call();
      return failure;
    }
  } catch (e) {
    PackageLogger.error('Error during token refresh: $e');
    onLogout?.call();
    return failure;
  }
}