This is another tutorial in the Retrofit series. It explains how to integrate authentication with username/email and password with Retrofit.
Retrofit Series Overview
- Getting Started and Creating an Android Client
- Basics of API Description
- Creating a Sustainable Android Client
- URL Handling, Resolution and Parsing
- How to Change API Base Url at Runtime
- Multiple Server Environments (Develop, Staging, Production)
- Share OkHttp Client and Converters between Retrofit Instances
- Upgrade Guide from 1.9
- Beyond Android: Retrofit for Java Projects
- How to use OkHttp 3 with Retrofit 1
- Synchronous and Asynchronous Requests
- Send Objects in Request Body
- Add Custom Request Header
- Manage Request Headers in OkHttp Interceptor
- Dynamic Request Headers with @HeaderMap
- Multiple Query Parameters of Same Name
- Optional Query Parameters
- Send Data Form-Urlencoded
- Send Data Form-Urlencoded Using FieldMap
- How to Add Query Parameters to Every Request
- Add Multiple Query Parameter With QueryMap
- How to Use Dynamic Urls for Requests
- Constant, Default and Logic Values for POST and PUT Requests
- Cancel Requests
- Reuse and Analyze Requests
- Optional Path Parameters
- How to Send Plain Text Request Body
- Customize Network Timeouts
- How to Trust Unsafe SSL certificates (Self-signed, Expired)
- Dynamic Endpoint-Dependent Interceptor Actions
- How to Update Objects on the Server (PUT vs. PATCH)
- How to Delete Objects on the Server
- Introduction to (Multiple) Converters
- Adding & Customizing the Gson Converter
- Implementing Custom Converters
- How to Integrate XML Converter
- Access Mapped Objects and Raw Response Payload
- Supporting JSON and XML Responses Concurrently
- Handling of Empty Server Responses with Custom Converter
- Send JSON Requests and Receive XML Responses (or vice versa)
- Unwrapping Envelope Responses with Custom Converter
- Wrapping Requests in Envelope with Custom Converter
- Define a Custom Response Converter
- Basic Authentication on Android
- Token Authentication on Android
- OAuth on Android
- Hawk Authentication on Android
- How to Refresh an Access Token
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);
}
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.