Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refreshToken function isn't invoked #107

Closed
KhaledAlMana opened this issue Jan 28, 2024 · 5 comments
Closed

refreshToken function isn't invoked #107

KhaledAlMana opened this issue Jan 28, 2024 · 5 comments

Comments

@KhaledAlMana
Copy link

KhaledAlMana commented Jan 28, 2024

First of all, Thank you @agordn52 for this great framework.

I'm following the docs, and what has been mentioned in the issues/discussions.

refreshToken function isn't being invoked at all.

  @override
  refreshToken(Dio dio) async {
    AuthTokens? tokens = await StorageKey.userToken.read<AuthTokens>();
    if (tokens?.refreshToken == null) {
      await Auth.logout();
      routeToInitial(navigationType: NavigationType.pushAndForgetAll);
      return;
    }
    dynamic response = (await dio.post(baseUrl + "/auth/refresh",
            options: Options(
                headers: {"Authorization": "Bearer ${tokens?.refreshToken}"})))
        .data();
    //  Save the new token
    await StorageKey.userToken.store(response);
  }

Noting that the approach was mentioned, I have to retry manually.

Did I miss anything?

If we refresh on request instead, would it be better? but what if I run parallel requests?


Update [29-01-2024]

This is the interceptor approach.

class BearerAuthInterceptor extends Interceptor {
  @override
  void onRequest(
      RequestOptions options, RequestInterceptorHandler handler) async {
    if (!options.path.contains("auth/refresh")) {
      await api<ApiService>((request) => request.refreshTokens());
    }
    AuthTokens? tokens = await StorageKey.userToken.read<AuthTokens>();
    String? token = options.path.contains("auth/refresh")
        ? tokens?.refreshToken
        : tokens?.accessToken;

    if (token != null) {
      options.headers.addAll({"Authorization": "Bearer $token"});
    }
    return super.onRequest(options, handler);
  }

the refreshTokens function checks if a refresh token is needed then proceed.
if condition to eliminate the recursion.

I had to skip the previous approach, but I still do not know how reactive this approach is. Especially with parallel requests scenario.

@agordn52
Copy link
Contributor

Hi @KhaledAlMana,

Hope you are well.
Thanks for your comments about the framework.

Re. the refreshToken method
Have you implemented the shouldRefreshToken?
This will tell Nylo when when to refresh the token.

E.g.

@override
Future<bool> shouldRefreshToken() async {
    User? user = Auth.user();

    if (user.token.expiryDate.isPast()) {
        // Check if the token is expired
        // This will trigger the [refreshToken] method
        return true;
    }
    return false;
}

The full docs are here, hope that helps :)

@KhaledAlMana
Copy link
Author

KhaledAlMana commented Jan 29, 2024

Hello @agordn52,

Yes, and during the debug, it is being invoked. However, refreshToken isn't invoked and all I have no clue why.

  @override
  Future<RequestHeaders> setAuthHeaders(RequestHeaders headers) async {
    AuthTokens? tokens = await StorageKey.userToken.read<AuthTokens>();
    if (tokens?.accessToken != null) {
      headers.addBearerToken(tokens?.accessToken ?? "");
    }
    return headers;
  }
  
  @override
  Future<bool> shouldRefreshTokens() async {
    try {
      AuthTokens? tokens = await StorageKey.userToken.read<AuthTokens>();
      if (tokens?.accessToken == null) {
        return false;
      }
      // If the token is expired, where expiredAt is epoch time
      // add 15 seconds to the expiry time to account for latency
      if (tokens?.expiresAt != null &&
          DateTime.now().isAfter(
              tokens?.expiresAt!.subtract(Duration(seconds: 15)) ??
                  DateTime.now())) {
        return true;
      } else {
        return false;
      }
    } catch (e) {
      print(e);
      return false;
    }
  }


  @override
  refreshToken(Dio dio) async {
    // debug is not hitting this function, nor printing the below.
    print("refreshing token");
    AuthTokens? tokens = await StorageKey.userToken.read<AuthTokens>();
    print(tokens?.refreshToken);
    if (tokens?.refreshToken == null) {
      await Auth.logout();
      routeToInitial(navigationType: NavigationType.pushAndForgetAll);
      return;
    }
    dynamic response = (await dio.post(baseUrl + "/auth/refresh",
            options: Options(
                headers: {"Authorization": "Bearer ${tokens?.refreshToken}"})))
        .data();
    //  Save the new token
    await StorageKey.userToken.store(response);
  }

@agordn52
Copy link
Contributor

agordn52 commented Jan 29, 2024

Hi @KhaledAlMana,

Thanks for sharing your code.
I see a few things wrong that should be changed, please try the below 👍

@override
Future<RequestHeaders> setAuthHeaders(RequestHeaders headers) async {
    AuthTokens? tokens = await StorageKey.userToken.read<AuthTokens>();
    if (tokens?.accessToken != null) {
      headers.addBearerToken(tokens?.accessToken ?? "");
    }
    return headers;
}
  
@override
Future<bool> shouldRefreshTokens() async {
  AuthTokens? tokens = await StorageKey.userToken.read<AuthTokens>();
  if (tokens?.accessToken == null) {
    return true; // if accessToken is null then you should want to get a new 'AuthTokens'
   }
   // If the token is expired, where expiredAt is epoch time
  // add 15 seconds to the expiry time to account for latency
  if (DateTime.now().isAfter(tokens?.expiresAt?.subtract(Duration(seconds: 15)) ?? DateTime.now())) {
        return true;
  }
  return false;
}

@override
refreshToken(Dio dio) async {
    // debug is not hitting this function, nor printing the below.
    print("refreshing token");
    AuthTokens? tokens = await StorageKey.userToken.read<AuthTokens>();
    print(tokens?.refreshToken);
    if (tokens?.refreshToken == null) {
      await Auth.logout();
      routeToInitial(navigationType: NavigationType.pushAndForgetAll);
      return;
    }
    dynamic response = (await dio.post(baseUrl + "/auth/refresh",
            options: Options(
                headers: {"Authorization": "Bearer ${tokens?.refreshToken}"})))
        .data; // data() not a function, use data instead
    //  Save the new token
    await StorageKey.userToken.store(response);
}

@agordn52
Copy link
Contributor

I'm going to close this as resolved.

If you need help understanding networking, the docs on nylo.dev are up-to-date so you'll be able to learn more 👍

@KhaledAlMana
Copy link
Author

Sorry, I have been busy lately.

I got the .data; issue but the problem is that the function refreshToken is still not being called.
After the latest update, I tested it again, it works fine.

Thank you @agordn52 for your support.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants