Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dagger + Retrofit. Adding auth headers at runtime

I'm wondering if there is a way for Dagger to know that it should recreate an object when new data is available.

The instance I am speaking of is with the request headers I have for retrofit. At some point (when the user logs in) I get a token that I need to add to the headers of retrofit to make authenticated requests. The issue is, I'm left with the same unauthenticated version of retrofit. Here's my injection code:

@Provides
    @Singleton
    OkHttpClient provideOkHttpClient(Cache cache) {
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        OkHttpClient client = new OkHttpClient.Builder()
                .addInterceptor(interceptor)
                .cache(cache).build();
         client
                .newBuilder()
                .addInterceptor(
                    chain -> {
                        Request original = chain.request();
                        Request.Builder requestBuilder = original.newBuilder()
                                .addHeader("Accept", "Application/JSON");
                        Request request = requestBuilder.build();
                        return chain.proceed(request);
                    }).build();
        return client;
    }

  @Provides
    @Singleton
    Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) { 
        Retrofit retrofit = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxErrorHandlingCallAdapterFactory.create())
                .baseUrl(mBaseUrl)
                .client(okHttpClient)
                .build();
        return retrofit;
}

@Provides
    @Singleton
    public NetworkService providesNetworkService(Retrofit retrofit) {
        return retrofit.create(NetworkService.class);
    }

Any ideas on how to make this work?

like image 554
AIntel Avatar asked Mar 27 '17 16:03

AIntel


2 Answers

I personally created an okhttp3.Interceptor that does that for me, which I update once I have the required token. It looks something like:

@Singleton
public class MyServiceInterceptor implements Interceptor {
  private String sessionToken;

  @Inject public MyServiceInterceptor() {
  }

  public void setSessionToken(String sessionToken) {
    this.sessionToken = sessionToken;
  }

  @Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();

    Request.Builder requestBuilder = request.newBuilder();

    if (request.header(NO_AUTH_HEADER_KEY) == null) {
      // needs credentials
      if (sessionToken == null) {
        throw new RuntimeException("Session token should be defined for auth apis");
      } else {
        requestBuilder.addHeader("Cookie", sessionToken);
      }
    }

    return chain.proceed(requestBuilder.build());
  }
}

In the corresponding dagger component, I expose this interceptor so I can set the sessionToken when I need to.

That is some stuff that Jake talked about it his talk Making Retrofit Work For You.

like image 128
oldergod Avatar answered Nov 14 '22 04:11

oldergod


Please consider using the approach mentioned by @oldergod as it is the "official" and much better way, whereas the approaches mentioned below are not advised, they may be considered as workarounds.


You have a couple of options.

  1. As soon as you get the token, you have to null out the component that provided you the Retrofit instance, create a new component and ask for a new Retrofit instance, which will be instantiated with necessary okhttp instance.
  2. A fast and bad one - Save the token in SharedPreferences, create okHttp header, which will apply token reading from SharedPreferences. If there is none - send no token header.
  3. Even uglier solution - declare a static volatile String field, and do the same thing like in step 2.

Why the second option is bad? Because on each request you would be polling disk and fetch data from there.

like image 12
azizbekian Avatar answered Nov 14 '22 04:11

azizbekian