Dagger 2 get old token when token is refreshed

10
January 12, 2019, at 6:10 PM

I am having a problem with dagger 2. Briefly, dagger 2 makes the injection in my fragment, and if the token is expired. TokenAuthenticator asks for a new token that is saved in SharedPreferences.

My fragment is not recreated and dagger 2 uses the expired token to make the call instead of the new one.

Now I'll explain in detail.

Dagger 2

My dagger 2 logic is trivial.

NetworkModule.java

@Provides
@Nullable
String provideAuthToken(Context context) {
    return AccountUtils.getCurrentUserToken(context);
}
@Provides
AuthenticationInterceptor provideInterceptor(@Nullable String authToken) {
    return new AuthenticationInterceptor(authToken);
}
@Provides
TokenAuthenticator provideAuthenticator(Context context, @Nullable String authToken) {
    return new TokenAuthenticator(context, authToken);
}
@Provides
OkHttpClient.Builder provideOkHttpClientBuilder(AuthenticationInterceptor interceptor, TokenAuthenticator authenticator) {
    OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
    okHttpClientBuilder.connectTimeout(15, TimeUnit.SECONDS);
    okHttpClientBuilder.readTimeout(15, TimeUnit.SECONDS);
    okHttpClientBuilder.writeTimeout(15, TimeUnit.SECONDS);
    if (!okHttpClientBuilder.interceptors().contains(interceptor)) {
        okHttpClientBuilder.addInterceptor(interceptor);
        okHttpClientBuilder.authenticator(authenticator);
    }
    return okHttpClientBuilder;
}

It gets the token from SharedPreferences and does the retrofit things to make the call to the API..

Retrofit Call

Then I make a simple call to the API (It is a GET with an Authorization token)

ProfileFragment.java

@Inject
ViewModelFactory viewModelFactory;
UserViewModel userViewModel;
@Override
public void onAttach(Context context) {
    ((BaseApplication) context.getApplicationContext())
            .getAppComponent()
            .inject(this);
    super.onAttach(context);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_profile, container, false);
    ...
    userViewModel = ViewModelProviders.of(this, viewModelFactory).get(UserViewModel.class);
    userViewModel.getUserInfoMutableLiveData().observe(this, this::consumeResponse);
    ...
}
private void consumeResponse(UserResponse userResponse) {
    switch (userResponse.status) {
        case LOADING:
            showProgressBar();
            break;
        case SUCCESS:
            handleSuccessResponse(userResponse.data);
            break;
        case ERROR:
            dismissAll();
            if (getActivity() != null) {
                ResponseHelper.handleErrorResponse(getActivity(), userResponse.error,
                        mContainer, mMainContainer, this);
            }
            break;
    }
}

Refresh Token

Now let me explain what happens.

ProfileFragment is created, dagger 2 uses injection to create the components by getting the token saved in SharedPreferences.

ProfileFragment makes the call to the API, it gets 401 error code because the token is expired.

TokenAuthenticatior is called to refresh the token, it successfully refresh the token and save the NEW token in SharedPreferences.

The user info is called again, however, as ProfileFragment is not recreated, it makes the call with the same dependency injection which has the old token. It calls the API with the old token then we get a error 401.

If I leave this page now and come back it works fine, because it does the dependency injection again and gets the new token which is saved in SharedPreferences.

Solutions

  1. That's why I was thinking about some way that could make the injection again. For example, recreating the profile fragment, but I don't think this is a good idea. I had no problems with that before using dagger 2.

Hope someone will help. Thanks!

Answer 1

It's generally a bad idea to inject things that change within the current scopes lifetime since you can't just inject something again. Once the token becomes invalid you'd have to recreate the whole component, in this case also forcing you to recreate the fragment, as you pointed out.

A better approach would be to have your TokenAuthenticator and AuthenticationInterceptor depend on AccountUtils, then they can read & update the token as necessary. There is no need to propagate the 401 to the user, as you can silently refresh the token within the Authenticator.

READ ALSO
How to get login() > 0 for this rest api code [on hold]

How to get login() > 0 for this rest api code [on hold]

I need the result of this coding is login () > 0, where is the wrong code ?

39
Run a SQL statement in MySQL on each connection

Run a SQL statement in MySQL on each connection

I must be going nuts here, but I could swear I read about a configuration option in MySQL that will run the defined SQL statement on every successful connection to the database, with the cavet that it would not run for root or those with a certain privilege...

44
How to access a variable from a knex query inside of a second knex query?

How to access a variable from a knex query inside of a second knex query?

I have the following route where I want to get the sum associated to an id I get from the first query

12
How to break out values in a row to be in its own field based on a date value

How to break out values in a row to be in its own field based on a date value

In the Table Setup below you will see that the Amount data is broken down in various rows in the tableI am creating a view where i want to break this out where each Amount value is in its own field based on the CreatedDt field

16