FlutterFlow API Interceptor Working Example

Hello FlutterFlow community, I've been working on a custom interceptor to server as a JWT refresh mechanism. However, the documentation is VERY sparse and it took a lot of trial and error. I just thought I would share this here to possible help anyone else having difficulties with this.

Feel free to reach out with questions or suggestions for improvement!

This will be best applied to projects that are utilizing custom authentication with the built in Authenticated User.

Here is my working implementation:

// Automatic FlutterFlow imports
import '/backend/schema/structs/index.dart';
import '/backend/schema/enums/enums.dart';
import '/actions/actions.dart' as action_blocks;
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
import 'index.dart'; // Imports other custom actions
import '/flutter_flow/custom_functions.dart'; // Imports custom functions
import 'package:flutter/material.dart';
// Begin custom action code
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!

import 'dart:convert'; // For JSON encoding/decoding
import 'dart:developer'; // For logging
import '/backend/api_requests/api_interceptor.dart';
import '/auth/custom_auth/auth_util.dart'; // Import your auth manager
import '/backend/api_requests/api_calls.dart'; // Import the RefreshTokenCall

class ExpiryCheckInterceptor extends FFApiInterceptor {
  @override
  Future<ApiCallOptions> onRequest({
    required ApiCallOptions options,
  }) async {
    log("Checking JWT token expiry now");
    // Access the token expiration from your custom auth manager
    final tokenExpiration = authManager.tokenExpiration; // Directly access it
    final refreshToken = authManager.refreshToken; // Fetch refresh token
    if (tokenExpiration != null && refreshToken != null) {
      log("Got token expiration and refresh token");
      final currentTime = DateTime.now();
      final timeDifference = tokenExpiration.difference(currentTime);

      // Check if the token needs refreshing
      if (timeDifference.isNegative || timeDifference.inMinutes <= 10) {
        log('Token is expired or will expire soon. Refreshing...');
        try {
          final response = await RefreshTokenCall.call(
            refreshToken: refreshToken,
          );

          if (response.statusCode == 200) {
            log('Token refreshed successfully.');
            final responseBody = response.jsonBody;

            // Update auth manager with new tokens
            authManager.updateAuthUserData(
                authenticationToken: responseBody['access_token'],
                tokenExpiration: tokenExpiryTime(responseBody['expires_in']),
                refreshToken: responseBody['refresh_token']);

            // Update options with the new token
            options.headers['Authorization'] =
                'Bearer ${responseBody['access_token']}';
          } else {
            log('Failed to refresh token: ${response.statusCode}');
          }
        } catch (e) {
          log('Error while refreshing token: $e');
        }
      } else {
        log("Token is valid for more than 10 minutes");
      }
    } else {
      log('No token expiration or refresh token found. User might not be authenticated.');
    }

    // Proceed with the original request
    return options;
  }

  @override
  Future<ApiCallResponse> onResponse({
    required ApiCallResponse response,
    required Future<ApiCallResponse> Function() retryFn,
  }) async {
    // Simply return the original response for now
    return response;
  }
}
8
1 reply