3

I have a login endpoint by which I receive an authToken and a refreshToken. The first one expires in an hour, so I should use the second one to refresh it and continue the application flow.

Now my app is filled with retrofit calls all over the place and I potentially could get a 401 at any given moment, so how can I make it so every time I get a 401 a refresh token is issued, and then the original request retried?

This is my refresh signature:

 @POST("/auth/actions/refresh")
    fun refreshToken(@Body tokenRefresh: TokenRefresh): Single<LoginResponse>

I thought about making a base repository class with a method withAuth() that takes any Observable/Single/Flowable and then applies this logic but I cannot find a way to implement it.

Saw a bunch of implementations but none of them match my needs... can anyone push me in the right direction?

This is the closest I've found, however there seems to be some errors on the flatmapping

M Rajoy
  • 4,028
  • 14
  • 54
  • 111

3 Answers3

0

I just came across similar requirement and came up with following solution. It's pretty simple, it just makes one attempt to call REST endpoint and if that call fails with HTTP 401 it reauthenticates and repeats the call again. Otherwise it just emits the original error.

fun <T> Single<T>.withAuth() = retryWhen { errors ->
    var firstAttempt = true
    errors.flatMapSingle { error ->
        if (firstAttempt && error is HttpException && error.code() == 401) {
            firstAttempt = false
            reauthenticate()
        } else {
            Single.error(it)
        }
    }
}

where the reauthentication function has the following signature:

fun reauthenticate(): Single<AnyAuthResponse>

Please note that the concrete exception type might depend on HTTP implementation you actually use, so you may want to update the condition to detect HTTP 401 response, but the code should give you an overall picture of how to solve your problem.

d.aemon
  • 761
  • 1
  • 7
  • 20
0

I think you can do this without modifying all calls. Add an Authenticator to your Retrofit Refreshing OAuth token using Retrofit without modifying all calls

Tuby
  • 3,158
  • 2
  • 17
  • 36
0

You can use Interceptor to intercept each request and check whether it returns 401 - UnAuthorised Access and iff then refresh the token and replay the current API request.

public final class SessionInterceptor implements Interceptor {
  // gets intercept
  @Override public Response intercept(@NonNull final Chain chain) throws IOException {

   final Request request = chain.request();
   final Response response = chain.proceed(request);
   final ResponseBody responseBody = response.body();

   if (response.code() == 401) {
      synchronized (this) {
      // Refresh your token
      // Update your authToken + Refreshed token
      final retrofit2.Response response = refreshToken();
      }
   }

   // Replay the original request
   // Perform request, here original request will be executed
   final Request original = chain.request();
   final Request.Builder builder = original.newBuilder();

   // Set your new refreshed token
   if (accessToken.isSet()) {
     builder.header(AUTHORIZATION, String.format(BEARER, 
     accessToken.get()));
    }

    final Request request = builder.method(original.method(), original.body()).build();

    return chain.proceed(request);
 }
}
Vikalp Patel
  • 10,669
  • 6
  • 61
  • 96