Retrofit — Basic Authentication on Android

This is another tutorial in the Retrofit series. It explains how to integrate authentication with username/email and password with Retrofit.

Retrofit Series Overview

  1. Introduction to Call Adapters (Coming soon)
  2. Custom Call Adapter to Separate OnResponse Callback (Coming soon)
  3. How to Integrate RxJava 1.x Call Adapter (Coming soon)
  4. How to Integrate RxJava 2.x Call Adapter (Coming soon)
  5. How to Integrate Guava Call Adapter (Coming soon)
  6. Custom Call Adapter to Separate Network and Gson Errors (Coming soon)
  1. Callbacks (Coming soon)
  2. Annotations (Coming soon)
  3. Fluent Interface with Builders (Coming soon)

Within the getting started and sustainable android client, we created an initial version of the Android client to perform API/HTTP requests. We’ll use the client foundation from the previous tutorial and enhance it with additional functionality for basic authentication.

Integrate Basic Authentication

Let’s update the ServiceGenerator class and create a method which adds authentication to requests. The snippet below extends the ServiceGenerator class from above. If you still rely on the first major release of Retrofit, just sneak ahead and look at the second code block :)

Retrofit 2

public class ServiceGenerator {

    public static final String API_BASE_URL = "https://your.api-base.url";

    private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();

    private static Retrofit.Builder builder =
            new Retrofit.Builder()
                    .baseUrl(API_BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create());

    private static Retrofit retrofit = builder.build();

    public static <S> S createService(Class<S> serviceClass) {
        return createService(serviceClass, null, null);
    }

    public static <S> S createService(
            Class<S> serviceClass, String username, String password) {
        if (!TextUtils.isEmpty(username)
                && !TextUtils.isEmpty(password)) {
            String authToken = Credentials.basic(username, password);
            return createService(serviceClass, authToken);
        }

        return createService(serviceClass, null, null);
    }

    public static <S> S createService(
            Class<S> serviceClass, final String authToken) {
        if (!TextUtils.isEmpty(authToken)) {
            AuthenticationInterceptor interceptor =
                    new AuthenticationInterceptor(authToken);

            if (!httpClient.interceptors().contains(interceptor)) {
                httpClient.addInterceptor(interceptor);

                builder.client(httpClient.build());
                retrofit = builder.build();
            }
        }

        return retrofit.create(serviceClass);
    }
}

Retrofit 1.9

public class ServiceGenerator {

    public static final String API_BASE_URL = "https://your.api-base.url";

    private static RestAdapter.Builder builder = new RestAdapter.Builder()
                .setEndpoint(API_BASE_URL)
                .setClient(new OkClient(new OkHttpClient()));

    public static <S> S createService(Class<S> serviceClass) {
        return createService(serviceClass, null, null);
    }

    public static <S> S createService(Class<S> serviceClass, String username, String password) {
        if (username != null && password != null) {
            // concatenate username and password with colon for authentication
            String credentials = username + ":" + password;
            // create Base64 encodet string
            final String basic =
                    "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);

            builder.setRequestInterceptor(new RequestInterceptor() {
                @Override
                public void intercept(RequestFacade request) {
                    request.addHeader("Authorization", basic);
                    request.addHeader("Accept", "application/json");
                }
            });
        }

        RestAdapter adapter = builder.build();
        return adapter.create(serviceClass);
    }
}

The new (second) method has two new parameters: username and password. You can use the username parameter for email, too. The basic approach of creating the client is the same as in the first method: Use the Retrofit (RestAdapter in Retrofit 1) class to create the OkHttp client for any HTTP requests and response handling.

The difference now: we use a Interceptor (RequestInterceptor in Retrofit 1) to set the authorization header value for any HTTP request executed with this OkHttp client. But this is only done if the parameters for username and password are provided. If you don’t pass any username and password to the method, it will create the same client as the first method. That’s why we can simplify the first method from the ServiceGenerator class. Retrofit 2 provides OkHttp 3's Credentials class, which can do the work for us.

For the authentication part we have to adjust the format of given username/email and password. Basic authentication requires both values as a concatenated string separated by a colon. Additionally, the newly created (concatenated) string has to be Base64 encoded.

Almost every webservice and API evaluates the Authorization header of the HTTP request. That’s why we set the encoded credentials value to that header field. In case the webservice you’re going to call with this client specifies another header field to expect the user’s credentials, adjust the header field from Authorization to your header field.

The Accept header is important if you want to receive the server response in a specific format. In our example we want to receive the response JSON formatted, since Retrofit 1.9 ships with Google’s Gson to serialize objects into their JSON representation and vice versa.

In our Retrofit 2 example, we've extracted the logic into an AuthenticationInterceptor class:

public class AuthenticationInterceptor implements Interceptor {

    private String authToken;

    public AuthenticationInterceptor(String token) {
        this.authToken = token;
    }

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

        Request.Builder builder = original.newBuilder()
                .header("Authorization", authToken);

        Request request = builder.build();
        return chain.proceed(request);
    }
}

Usage

Just call new method from ServiceGenerator class like we did before in the example part of the Retrofit Getting Started post. For example, let’s assume you defined a LoginService like the code below.

Retrofit 2

public interface LoginService {  
    @POST("/login")
    Call<User> basicLogin();
}

Retrofit 1.9

public interface LoginService {  
    @POST("/login")
    void basicLogin(Callback<User> cb);
}

The interface above has only one method: basicLogin. It has the User class as response value and doesn’t expect any additional query or path parameters.

Now you can create your HTTP client by passing your given credentials (username, password).

Retrofit 2

LoginService loginService =  
   ServiceGenerator.createService(LoginService.class, "user", "secretpassword");
Call<User> call = loginService.basicLogin();  
call.enqueue(new Callback<User >() {  
    @Override
    public void onResponse(Call<User> call, Response<User> response) {
        if (response.isSuccessful()) {
            // user object available
        } else {
            // error response, no access to resource?
        }
    }

    @Override
    public void onFailure(Call<User> call, Throwable t) {
        // something went completely south (like no internet connection)
        Log.d("Error", t.getMessage());
    }
}

Retrofit 1.9

LoginService loginService =  
    ServiceGenerator.createService(LoginService.class, "user", "secretpassword");
loginService.basicLogin(new Callback<User>() {  
    @Override
    public void success(User user, Response response) {
        // user object available
    }

    @Override
    public void failure(RetrofitError error) {
        // handle errors, too
    }
});

The ServiceGenerator method will create the HTTP client including the the defined authorization header. Once you call the basicLogin method of loginService, the provided credentials will be automatically passed to the requested API endpoint.

What Comes Next

The next tutorial describes how to implement a custom OAuth client with Retrofit.

Feel free to let us know any questions or problems via Twitter: @futurestud_io.


Explore the Library

Find interesting tutorials and solutions for your problems.

Miscellaneous