Android - Retrofit 2 - Authenticator Result

I managed to get a decent solution using the TokenAuthenticator and an Interceptor and thought I'd share the idea as it may help some others.

Adding the 'TokenInterceptor' class that handles adding the token to the header is the token exists, and the 'TokenAuthenticator' class handles the case when there is no token, and we need to generate one.

I'm sure there are some better ways to implement this, but it's a good starting point I think.

public static class TokenAuthenticator implements Authenticator {
    @Override
    public Request authenticate( Route route, Response response) throws IOException {
    ...
    Session body = call.execute().body();
    Logger.d("Session token: " + body.token);
    // Storing the token somewhere.
    session.token = body.token;
    ...
}


private static class TokenInterceptor implements Interceptor {
@Override
    public Response intercept( Chain chain ) throws IOException {
        Request originalRequest = chain.request();

        // Nothing to add to intercepted request if:
        // a) Authorization value is empty because user is not logged in yet
        // b) There is already a header with updated Authorization value
        if (authorizationTokenIsEmpty() || alreadyHasAuthorizationHeader(originalRequest)) {
            return chain.proceed(originalRequest);
        }

        // Add authorization header with updated authorization value to  intercepted request
        Request authorisedRequest = originalRequest.newBuilder()
                .header("Auth-Token", session.token )
                .build();
        return chain.proceed(authorisedRequest);
    }
}

Source:

http://lgvalle.xyz/2015/07/27/okhttp-authentication/


I have similar authenticator and it works with 2.0.0-beta2.

If you get lots of login attempts from you Authenticator, I suggest make sure that when you make the synchronous call, you are not using Authenticator with that call. That could end up in loop, if also your "emailLogin" fails.

Also I would recommend adding loggingInterceptor to see all trafic to server: Logging with Retrofit 2


I know it's a late answer but for anyone still wondering how to Add / Refresh token with Retrofit 2 Authenticator, here is a working solution:

Note: preferenceHelper is your Preference Manager class where you set/get your shared preferences.

public class AuthenticationHelper implements Authenticator {

    private static final String HEADER_AUTHORIZATION = "Authorization";
    private static final int REFRESH_TOKEN_FAIL = 403;

    private Context context;

    AuthenticationHelper(@ApplicationContext Context context) {
        this.context = context;
    }

    @Override
    public Request authenticate(@NonNull Route route, @NonNull Response response) throws IOException {
        // We need to have a token in order to refresh it.
        String token = preferencesHelper.getAccessToken();
        if (token == null)
            return null;

        synchronized (this) {
            String newToken = preferencesHelper.getAccessToken();
            if (newToken == null)
                return null;

            // Check if the request made was previously made as an authenticated request.
            if (response.request().header(HEADER_AUTHORIZATION) != null) {

                // If the token has changed since the request was made, use the new token.
                if (!newToken.equals(token)) {
                    return response.request()
                            .newBuilder()
                            .removeHeader(HEADER_AUTHORIZATION)
                            .addHeader(HEADER_AUTHORIZATION, "Bearer " + newToken)
                            .build();
                }

                JsonObject refreshObject = new JsonObject();
                refreshObject.addProperty("refreshToken", preferencesHelper.getRefreshToken());

                retrofit2.Response<UserToken> tokenResponse = apiService.refreshToken(refreshObject).execute();

                if (tokenResponse.isSuccessful()) {

                    UserToken userToken = tokenResponse.body();
                    if (userToken == null)
                        return null;

                    preferencesHelper.saveAccessToken(userToken.getToken());
                    preferencesHelper.saveRefreshToken(userToken.getRefreshToken());


                    // Retry the request with the new token.
                    return response.request()
                            .newBuilder()
                            .removeHeader(HEADER_AUTHORIZATION)
                            .addHeader(HEADER_AUTHORIZATION, "Bearer " + userToken.getToken())
                            .build();
                } else {
                    if (tokenResponse.code() == REFRESH_TOKEN_FAIL) {
                        logoutUser();
                    }
                }
            }
        }
        return null;
    }

    private void logoutUser() {
        // logout user
    }
}

Also note:

  1. preferenceHelper and apiService needs to be provided in some way.
  2. This is not an example that will work for all systems and api's but an example in how adding and refreshing the token should be done using Retrofit 2 Authenticator