Search code examples
javaandroidandroid-studioretrofit2okhttp

Unable to Refresh Bearer Token using Retrofit


I'm using Retrofit and OkHttp in my Project and i want to refresh token when the server gives 401 error code and store the new token in shared preference and make the same call again without notifing the user. Token type is bearer and expires every hour.

But the output that i desired is not coming, when the token expires the authenticator does nothing.

ApiInterface.java

public interface ApiInterface {
    @FormUrlEncoded
    @POST("auth/login")
    Call<ResponseBody> login(@FieldMap HashMap<String, Object> map);

    @FormUrlEncoded
    @POST("auth/register")
    Call<ResponseBody> register(@FieldMap HashMap<String, Object> map);

    @FormUrlEncoded
    @POST("auth/forgotpassword")
    Call<ResponseBody> forgotPassword(@FieldMap HashMap<String, Object> map);

    @POST("auth/refresh")
    Call<ResponseBody> refreshToken(@Header("Authorization") String token);

    @POST("auth/GetAllInterests")
    Call<ResponseBody> getAllInterest();

    @POST("auth/AddUserProfile")
    @Multipart
    Call<ResponseBody> addUserProfile(
            @Part("display_name") RequestBody display_name,
            @Part("children_age_show") RequestBody children_age_show,
            @Part("address") RequestBody address,
            @Part("no_of_children") RequestBody no_of_children,
            @Part("children_age") RequestBody children_age,
            @Part("date_of_birth") RequestBody date_of_birth,
            @Part("interests") RequestBody interests,
            @Part List<MultipartBody.Part> images
    );

    @POST("auth/GetUserProfile")
    Call<ResponseBody> getUserProfile();

    @FormUrlEncoded
    @POST("auth/ChangePassword")
    Call<ResponseBody> changePassword(@FieldMap HashMap<String, Object> map);

    @POST("auth/logout")
    Call<ResponseBody> logout();
}

ApiClient.java

public class ApiClient {
    private static Retrofit retrofit = null;
    private static ApiInterface apiInterface = null;

    public static Retrofit getRetrofit(Context context) {
        OkHttpClient client = new OkHttpClient.Builder()
                .addInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Request request = chain.request();

                        if (AppPreference.getPreference(context, AppPersistence.keys.AUTH_TOKEN) != null) {
                            request = request.newBuilder()
                                    .addHeader("Authorization", AppPreference.getPreference(context, AppPersistence.keys.AUTH_TOKEN))
                                    .build();
                        }

                        return chain.proceed(request);
                    }
                })
                .authenticator(new TokenAuthenticator(context))
                .build();

        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl(Constants.BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .client(client)
                    .build();
        }
        return retrofit;
    }

    public static ApiInterface getApiInterface(Context context) {
        if (apiInterface == null) {
            getRetrofit(context);
            apiInterface = retrofit.create(ApiInterface.class);
        }
        return apiInterface;
    }
}

TokenAuthenticator.java
public class TokenAuthenticator implements Authenticator {
    Context context;

    public TokenAuthenticator(Context context) {
        this.context = context;
    }

    @Override
    public Request authenticate(Route route, Response response) throws IOException {
        Log.d("Authenticator", "Authenticator Called");
        if (response.code() == 401) {
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(Constants.BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();

            Log.d("Authenticator", "Requesting for New Token");
            ApiInterface apiInterface = retrofit.create(ApiInterface.class);

            retrofit2.Response<ResponseBody> response1 = apiInterface
                    .refreshToken(AppPreference.getPreference(context, AppPersistence.keys.AUTH_TOKEN))
                    .execute();

            if (response1.isSuccessful()) {
                try {
                    String body = response1.body().string();
                    JSONObject jsonObject = new JSONObject(body);

                    if (jsonObject.getBoolean("status")) {
                        String newToken = "Bearer " + jsonObject.getJSONObject("Data").getString("access_token");
                        AppPreference.setPreference(context, AppPersistence.keys.AUTH_TOKEN, newToken);

                        Log.d("Authenticator", "New Token Generated");
                        return response.request().newBuilder()
                                .header("Authorization", newToken)
                                .build();
                    }
                } catch (Exception e) {
                    Log.e("TokenAuthenticator", e.getMessage(), e);
                }
            }

            Toast.makeText(context, response1.body().string(), Toast.LENGTH_SHORT).show();
        }

        return null;
    }
}

Solution

  • Please try the below way

     (1)   HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
                    interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
                    OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
        
                    try {
                        httpClient.addInterceptor(new Interceptor() {
                            @Override
                            public Response intercept(Interceptor.Chain chain) throws IOException {
                                Request request = chain.request();
                                // try the request
                                Response response = chain.proceed(request);
                                if (response shows expired token) {
                                    // get a new token (I use a synchronous Retrofit call)
                                    // create a new request and modify it accordingly using the new token
                                    Request newRequest = request.newBuilder().build();
        
                                    // retry the request
                                    return chain.proceed(newRequest);
                                }
                                // otherwise just pass the original response on
                                return response;
                            }
                        });
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    String baseUrl = "";
                    Retrofit retrofit = new Retrofit.Builder()
                            .client(okHttpClient)
                            .baseUrl(baseUrl)
                            .addConverterFactory(GsonConverterFactory.create())
                            .build();
                    apiService = retrofit.create(ApiService.class);
    
    
    (2)  public class TokenAuthenticator implements Authenticator {
    
            private final ApiService apiservice;
    
            public TokenAuthenticator(TokenServiceHolder tokenServiceHolder) {
                this.tokenServiceHolder = tokenServiceHolder;
            }
    
            @Override
            public Request authenticate(Proxy proxy, Response response) throws IOException {
                newtoken = apiservice.refreshToken().execute();
                return response.request().newBuilder()
                        .header(AUTHORIZATIONKEY, newtoken)
                        .build();
            }
    
            @Override
            public Request authenticateProxy(Proxy proxy, Response response) throws IOException {
                // Null indicates no attempt to authenticate.
                return null;
            }
        }
    

    Hope it may help you