Hao Do Van
Hao Do Van

Reputation: 21

Flutter: How to refresh the token within the allowable time of the access token when it is still valid?

I'm new to Flutter and using the http package for network calls. My goal is to refresh the token and retry the request if the access token has expired. I’ve seen the Dio package mentioned, but it seems complicated to me, so I’d prefer to stick with http. Problem: I want to: Check if the access token has expired before making an API call. If expired, call the refresh token API to get a new token. Retry the original request with the new token. This res to login This res to refresh-token This my app flutter:

This login function to POST accesstoken

 static Future<bool> loginUser(String email, String password) async {
    try {
      var reqBody = {"email": email, "password": password};

      var response = await http.post(
        Uri.parse(login),
        headers: {"Content-Type": "application/json"},
        body: jsonEncode(reqBody),
      );

      var jsonResponse = jsonDecode(response.body);
      if (jsonResponse['status']) {
        SharedPreferences prefs = await SharedPreferences.getInstance();
        prefs.setString('token', jsonResponse['token']);
        prefs.setString('refreshToken', jsonResponse['refreshToken']);
        return true;
      } else {
        return false;
      }
    } catch (error) {
      throw Exception('Failed to login user: $error');
    }
  }
static DateTime? lastApiCallTime;
  static Future<void> checkAndRefreshToken() async {
    if (lastApiCallTime != null &&
        DateTime.now().difference(lastApiCallTime!).inMinutes < 3) {
      return;
    }
    lastApiCallTime = DateTime.now();
    await refreshAccessToken();
  }
static Future<bool> refreshAccessToken() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    String? refreshToken = prefs.getString('refreshToken');

    if (refreshToken == null) {
      throw Exception('No refresh token found');
    }

    try {
      var response = await http.post(
        Uri.parse(refreshTokenUrl),
        headers: {"Content-Type": "application/json"},
        body: jsonEncode({'refreshToken': refreshToken}),
      );

      if (response.statusCode == 200) {
        var jsonResponse = jsonDecode(response.body);
        prefs.setString('token', jsonResponse['token']);
        print('New token: ${jsonResponse['token']}');
        return true;
      } else {
        return false;
      }
    } catch (error) {
      print('Failed to refresh token: $error');
      return false;
    }
  }

I’m not sure how to properly retry the original request after refreshing the token.

How can I: Detect when the token has expired (e.g., response 401 or similar). Automatically refresh the token and retry the request with the new token.

Upvotes: 1

Views: 82

Answers (1)

2IIZ
2IIZ

Reputation: 132

You can try recursivity. Something like this should do it :

Future<void> recursiveAPICall() async {
  final response = await http.get(Uri.parse(apiLink));


  if (response.statusCode == 200) {
    // do your logic
  } else if (response.statusCode == 401){
    // check for the accesstoken expiration and get another one
    recursiveAPICall()
  }
}

EDIT :

Integrating it with your code, something like that:

Future<void> recursiveAPICall() async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  String? token = prefs.getString('token');
  String? refreshToken = prefs.getString('refreshToken');

  if (refreshToken == null) {
    throw Exception('No refresh token found');
  }

  var response = await http.get(
    Uri.parse(apiLink),
    headers: {"Authorization": "Bearer $token"},
  );

  if (response.statusCode == 200) {
    // Do your logic here
    print('API call successful');
  } else if (response.statusCode == 401) {
    // Token might be expired, try to refresh it
    var refreshResponse = await http.post(
      Uri.parse(refreshTokenUrl),
      headers: {"Content-Type": "application/json"},
      body: jsonEncode({'refreshToken': refreshToken}),
    );

    if (refreshResponse.statusCode == 200) {
      var jsonResponse = jsonDecode(refreshResponse.body);
      prefs.setString('token', jsonResponse['token']);
      print('New token: ${jsonResponse['token']}');
      // Retry the API call after refreshing the token
      await recursiveAPICall();
    } else {
      print('Failed to refresh token, cannot proceed with API call');
    }
  } else {
    print('API call failed with status: ${response.statusCode}');
  }
}

Upvotes: 0

Related Questions