87

I am using OAuth and I need to put the OAuth token in my header every time I make a request. I see the @Header annotation, but is there a way to make it parameterized so i can pass in at run time?

Here is the concept

@Header({Authorization:'OAuth {var}', api_version={var} })

Can you pass them in at Runtime?

@GET("/users")
void getUsers(
    @Header("Authorization") String auth, 
    @Header("X-Api-Version") String version, 
    Callback<User> callback
)
jpotts18
  • 4,951
  • 5
  • 31
  • 31
  • Did you ever figure this out? I need to pass in a token in the header also – theSociableme Sep 11 '13 at 15:40
  • I am also looking for a solution to this, from documentation it sounds like [@Headers()](http://square.github.io/retrofit/javadoc/retrofit/http/Header.html) annotation on the method _adds fields_ to header one by one, but only supports literals. And [@Header("parameter") String string](http://square.github.io/retrofit/javadoc/retrofit/http/Header.html) parameter annotation _replaces_ the header with the supplied value. – nana Sep 12 '13 at 01:02
  • 2
    Same here, couldn't find out how to handle sessions when using retrofit. – frankelot May 27 '14 at 02:21
  • We did not need to pass all items, retrofit itself handle all. Please check my answer [link](https://stackoverflow.com/a/49607975/6299045) in StackOverflow. – Subin Babu Apr 21 '18 at 07:07

4 Answers4

100

Besides using @Header parameter, I'd rather use RequestInterceptor to update all your request without changing your interface. Using something like:

RestAdapter.Builder builder = new RestAdapter.Builder()
    .setRequestInterceptor(new RequestInterceptor() {
        @Override
        public void intercept(RequestFacade request) {
            request.addHeader("Accept", "application/json;versions=1");
            if (isUserLoggedIn()) {
                request.addHeader("Authorization", getToken());
            }                    
        }
    });

p/s : If you are using Retrofit2, you should use Interceptor instead of RequestInterceptor

Since RequestInterceptor is not longer available in Retrofit 2.0

user3738870
  • 1,415
  • 2
  • 12
  • 24
Felix
  • 2,705
  • 1
  • 23
  • 14
  • 3
    This is not directly related but if you find yourself needing to get values from the request object in order to generate your Authorization header, you'll need to extend ApacheClient and in execute duplicate the Request object (List
    headers = ...; Request requestNew = new Request(request.getMethod(), request.getUrl(), headers, request.getBody()); request = requestNew).
    –  Jan 07 '14 at 18:14
  • 1
    That's a trick that messes a coded up, better use @nana's answer – Ivan Fazaniuk Mar 23 '16 at 11:32
  • 1
    `RestAdapter` depends on Retrofit1, in Retrofit2 it is `Retrofit`. Im going to use Retrofit2, so it no issues if use `RequestInterceptor` as above code? – Huy Tower Sep 16 '16 at 09:12
56

Yes, you can pass them in runtime. As a matter of fact, pretty much exactly as you typed it out. This would be in your API interface class, named say SecretApiInterface.java

public interface SecretApiInterface {

    @GET("/secret_things")
    SecretThing.List getSecretThings(@Header("Authorization") String token)

}

Then you pass the parameters to this interface from your request, something along those lines: (this file would be for example SecretThingRequest.java)

public class SecretThingRequest extends RetrofitSpiceRequest<SecretThing.List, SecretApiInteface>{

    private String token;

    public SecretThingRequest(String token) {
        super(SecretThing.List.class, SecretApiInterface.class);
        this.token = token;
    }

    @Override
    public SecretThing.List loadDataFromNetwork() {
        SecretApiInterface service = getService();
        return service.getSecretThings(Somehow.Magically.getToken());
    }
}

Where Somehow.Magically.getToken() is a method call that returns a token, it is up to you where and how you define it.

You can of course have more than one @Header("Blah") String blah annotations in the interface implementation, as in your case!

I found it confusing too, the documentation clearly says it replaces the header, but it DOESN'T!
It is in fact added as with @Headers("hardcoded_string_of_liited_use") annotation

Hope this helps ;)

nana
  • 4,426
  • 1
  • 34
  • 48
  • 1
    I found in the docs that it doesn't replace an existing header: "Note that headers do not overwrite each other." Check http://square.github.io/retrofit/ and "Header Manipulation" – Amio.io Jun 28 '14 at 07:41
38

The accepted answer is for an older version of Retrofit. For future viewers the way to do this with Retrofit 2.0 is using a custom OkHttp client:

OkHttpClient httpClient = new OkHttpClient.Builder()
  .addInterceptor(new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
      Builder ongoing = chain.request().newBuilder();
      ongoing.addHeader("Accept", "application/json;versions=1");
      if (isUserLoggedIn()) {
        ongoing.addHeader("Authorization", getToken());
      }
      return chain.proceed(ongoing.build());
    }
  })
  .build();

Retrofit retrofit = new Retrofit.Builder()
  // ... extra config
  .client(httpClient)
  .build();

Hope it helps someone. :)

pablisco
  • 14,027
  • 4
  • 48
  • 70
  • 5
    In common usage with dagger2, retrofit2 will be singleton, therefore httpclient wont be created each time. in that case isUserLoggedIn() does not make sense, am i right? Only solution i can see currently is to force retrofit2 reinitialization when user login status is changed, so that appropriate header gets added or removed from request.. or there is some obvious solution that i cant see currently ? Thanks. – bajicdusko May 07 '16 at 15:06
  • 2
    @bajicdusko this is my exact same connundrum. Have you found a solution? It seems so wasteful, and strange that the previous version was more efficient. – deed02392 Jun 12 '16 at 20:11
  • @deed02392 You can set a composite `Interceptor` to which you can set or reset the interceptor at a later stage. However, I would argue that having retrofit as a singleton may a sign of early optimisation. There is no overhead on creating a new retrofit instance: https://github.com/square/retrofit/blob/master/retrofit/src/main/java/retrofit2/Retrofit.java – pablisco Jun 12 '16 at 20:48
  • I did not really think about it deeply. I have some ApiFactory class which is also initialized with dagger2, and it is responsible for initialization of retrofit. I have exposed one public method in ApiFactory which forces reinitialization of retrofit instance when needed, so its quite simple. I might doing it wrong, but it did the job, and i am using it only for Authorization header so its used when user login or logout. Another option is to use @Header annotation inside endpoint definition, which was not acceptable for me. I should set it on each endpoint which is not practical. – bajicdusko Jun 12 '16 at 20:49
  • @pablisco Ah from my understanding you couldn't add or remove `Interceptor`s once you'd created a Retrofit2 instance. – deed02392 Jun 12 '16 at 21:11
  • @bajicdusko when you say your factory re-initialises do you mean the original instance is destroyed simultaneously? This is not the standard factory pattern afaik, can you give me some reference to what this pattern might be called? Also does this mean you initialite Retrofit2 with parameters on this ApiFactory method? – deed02392 Jun 12 '16 at 21:13
  • @deed02392 yes, original instance is replaced with new one. Here take a look at my ApiFactory class https://jsfiddle.net/218k7hyo/ . I have actually two instances for retrofit, one for AWS upload and another for API communication. "retrofitUploadInstance" is not what interests you. As i said, i might be doing it wrong, i'd like for someone to explain. As for pablisco answer, it is suggested to use retrofit as singleton because of performance issues.. – bajicdusko Jun 12 '16 at 21:24
  • @deed02392 [citation needed] ;) – pablisco Jun 12 '16 at 21:29
  • Late to the party, but I usually have two API instances `@Named("authorized")` and `@Named("unauthorized")`. Authorized API gets injected with an interceptor injecting the header. The latter doesn't, unauthorized is also the default. I think that's also one way to do it. – Kevin D. Jul 05 '16 at 23:09
  • @pablisco What happens if I use `RequestInterceptor` instead of `Interceptor`? It's OK? What does your recommend? – Huy Tower Sep 16 '16 at 09:28
  • @TranDucHuy: `RequestInterceptor` is not longer available in Retrofit 2.0. – pablisco Sep 16 '16 at 10:38
  • I have a doubt about getToken() method. When I send the same request using Postman using basic Auth, it encodes the passed values in Base64 and then appends that as key for authorization header. While using retrofit, am I supposed to encode it in base64 on my own? – Sahil Patel Mar 22 '18 at 06:37
  • @SahilPatel It sounds like you are having a different issue. I suggest you open a new question for it. – pablisco Mar 22 '18 at 11:16
  • @pablisco what's a composite interceptor and how do we use one to replace the interceptor? It does seem wasteful to create a new instance every time we have a different access token and secret. But I'm willing to be convinced otherwise! :) – the_new_mr Feb 19 '19 at 16:06
  • @the_new_mr By composite, I mean an Interceptor that delegates it's actions to other interceptors. If you are worried about performance you can always set the token inside the interceptor mutable and set it when it changes. However, unless you change the token every 16ns I personally don't see a great danger of wastefulness as the GC would take care of the old one and should only happen when the user changes credentials in most cases. – pablisco Feb 20 '19 at 08:25
  • @pablisco Ok. Thanks for that info. In my case, it's not a case of user logging in/out but requests for different users on their behalf in the background so potential for the need to perform multiple requests in succession with different access token/secret pairs. I think I'll go with a mechanism of changing the token/secret in the interceptor from outside it as you suggested. I might do a performance test as well out of curiosity. I'll report back here when I do. – the_new_mr Feb 20 '19 at 14:58
8

Retrofit 2.3.0

OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
    okHttpClientBuilder
            .addInterceptor(new Interceptor() {
                @Override
                public okhttp3.Response intercept(Chain chain) throws IOException {
                    Request request = chain.request();
                    Request.Builder newRequest = request.newBuilder().header("Authorization", accessToken);
                    return chain.proceed(newRequest.build());
                }
            });

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(GithubService.BASE_URL)
            .client(okHttpClientBuilder.build())
            .addConverterFactory(GsonConverterFactory.create())
            .build();

I am using this to connect to GitHub.

Soon Santos
  • 2,107
  • 22
  • 43