Handle Background Refresh token call in Retrofit parallel network calls - android

I am new to android programming and Retrofit , I am making a sample app where i have to make two parallel network calls using access token.
The problem comes when access token is expired and return 401 status code , if I see 401 HTTP status code I have to make a call to refresh token with this access token , but problem with parallel calls is that it leads to race condition for refreshing the refresh token , is there any best practice of way to avoid such situation and how to intelligently refresh the token without any conflict.

OkHttp will automatically ask the Authenticator for credentials when a response is 401 Not Authorised retrying last failed request with them.
public class TokenAuthenticator implements Authenticator {
#Override
public Request authenticate(Proxy proxy, Response response) throws IOException {
// Refresh your access_token using a synchronous api request
newAccessToken = service.refreshToken();
// Add new header to rejected request and retry it
return response.request().newBuilder()
.header(AUTHORIZATION, newAccessToken)
.build();
}
#Override
public Request authenticateProxy(Proxy proxy, Response response) throws IOException {
// Null indicates no attempt to authenticate.
return null;
}
Attach an Authenticator to an OkHttpClient the same way you do with Interceptors
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setAuthenticator(authAuthenticator);
Use this client when creating your Retrofit RestAdapter
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(ENDPOINT)
.setClient(new OkClient(okHttpClient))
.build();
return restAdapter.create(API.class);
Check this: Fore more details visit this link

Try to make a queue for the refresh token operations like:
class TokenProcessor {
private List<Listener> queue = new List<Listener>();
private final Object synch = new Object();
private State state = State.None;
private String token;
private long tokenExpirationDate;
public void getNewToken(Listener listener){
synchronized(synch) {
// check token expiration date
if (isTokenValid()){
listener.onSuccess(token);
return;
}
queue.add(listener);
if (state != State.Working) {
sendRefreshTokenRequest();
}
}
}
private void sendRefreshTokenRequest(){
// get token from your API using Retrofit
// on the response call onRefreshTokenLoaded() method with the token and expiration date
}
private void onRefreshTokenLoaded(String token, long expirationDate){
synchronized(synch){
this.token = token;
this.tokenExpirationDate = expirationDate;
for(Listener listener : queue){
try {
listener.onTokenRefreshed(token);
} catch (Throwable){}
}
queue.clear();
}
}
}
This is an example code, how it can be implemented.

To avoid race conditions, you could synchronize refresh token code using ReentrantLock. For instance, if request A and request B try to refresh token at the same time, since code is synchronized, refresh A gets to actually refresh the token. Once it completes, request B will run refreshToken() and there's should be some logic that tells request B that token has already been refreshed. An example could be storing timestamp of when the token refresh happens then check if token has been refreshed last 10 seconds.
val lock = ReentrantLock(true)
fun refreshToken(): Boolean {
lock.lock()
if (token has been refreshed in last 10 seconds): return true
api.refresh()
lock.unlock()
}
If you don't want to use last 10 seconds logic, here's a different approach. Whenever you refresh token, backend returns {accessToken, expiration-timestamp}. Now, request A saves this token and expiration in disk. Request B will just need to check to make sure token is not expired using the timestamp. If request B gets 401 and token has not expired, it means request A has refreshed the token. Sample code:
val lock = ReentrantLock(true)
fun refreshToken(): Boolean {
lock.lock()
if (token has not expired): return true
api.refresh()
lock.unlock()
}
Otherwise, you probably have to create a queue for refresh token operations as mentioned above.

Related

Valid FirebaseIdToken for backend API call

I am using firebase authentication in Android. I need to send IdToken to backend for every api call. What i did is given sequentially.
When app starts call getIdToken(boolean forceRefresh) to get the token of user.
As i need the token later for api call i saved it locally
Register FirebaseAuth.IdTokenListener to get new token when token is changed. As listener is called when token changed. Update local token.
But sometime my backed gives 401 for api call.
Here is my implementation
Get token
firebaseUser.getIdToken(false)
.addOnCompleteListener(Executors.newSingleThreadExecutor(), OnCompleteListener {
if (it.isSuccessful) {
setAccessToken(it.result?.token)
} else {
setAccessToken(null)
}
removeIdTokenListener()
registerForIdTokenListener()
})
Token change listener
private val idTokenListener = IdTokenListener {
val jwtToken = it.token
if( jwtToken != null) {
setAccessToken(jwtToken)
} else {
setAccessToken(null)
}
}
private fun registerForIdTokenListener() {
mAuth.addIdTokenListener(idTokenListener)
}
private fun removeIdTokenListener(){
mAuth.removeIdTokenListener(idTokenListener)
}
The problem i am facing is sometimes my backend gives 401 for invalid token.
What would be the right approach for sending token to server for every API call?
Some users in stackoverflow suggested to use getIdToken method in Http interceptor for every call. Is this good way to always call getIdToken for every api call?
Or only IdTokenListener listener register is enough to get always token update. Can i relay on this listener?
I have faced some issue using IdTokenListener. Sometime IdTokenListener is not triggered after register.
Help me to find out to do right thing for my app.

Retrofit & RxJava2 refresh token and retry

I have a login endpoint by which I receive an authToken and a refreshToken. The first one expires in an hour, so I should use the second one to refresh it and continue the application flow.
Now my app is filled with retrofit calls all over the place and I potentially could get a 401 at any given moment, so how can I make it so every time I get a 401 a refresh token is issued, and then the original request retried?
This is my refresh signature:
#POST("/auth/actions/refresh")
fun refreshToken(#Body tokenRefresh: TokenRefresh): Single<LoginResponse>
I thought about making a base repository class with a method withAuth() that takes any Observable/Single/Flowable and then applies this logic but I cannot find a way to implement it.
Saw a bunch of implementations but none of them match my needs... can anyone push me in the right direction?
This is the closest I've found, however there seems to be some errors on the flatmapping
I just came across similar requirement and came up with following solution. It's pretty simple, it just makes one attempt to call REST endpoint and if that call fails with HTTP 401 it reauthenticates and repeats the call again. Otherwise it just emits the original error.
fun <T> Single<T>.withAuth() = retryWhen { errors ->
var firstAttempt = true
errors.flatMapSingle { error ->
if (firstAttempt && error is HttpException && error.code() == 401) {
firstAttempt = false
reauthenticate()
} else {
Single.error(it)
}
}
}
where the reauthentication function has the following signature:
fun reauthenticate(): Single<AnyAuthResponse>
Please note that the concrete exception type might depend on HTTP implementation you actually use, so you may want to update the condition to detect HTTP 401 response, but the code should give you an overall picture of how to solve your problem.
I think you can do this without modifying all calls. Add an Authenticator to your Retrofit
Refreshing OAuth token using Retrofit without modifying all calls
You can use Interceptor to intercept each request and check whether it returns 401 - UnAuthorised Access and iff then refresh the token and replay the current API request.
public final class SessionInterceptor implements Interceptor {
// gets intercept
#Override public Response intercept(#NonNull final Chain chain) throws IOException {
final Request request = chain.request();
final Response response = chain.proceed(request);
final ResponseBody responseBody = response.body();
if (response.code() == 401) {
synchronized (this) {
// Refresh your token
// Update your authToken + Refreshed token
final retrofit2.Response response = refreshToken();
}
}
// Replay the original request
// Perform request, here original request will be executed
final Request original = chain.request();
final Request.Builder builder = original.newBuilder();
// Set your new refreshed token
if (accessToken.isSet()) {
builder.header(AUTHORIZATION, String.format(BEARER,
accessToken.get()));
}
final Request request = builder.method(original.method(), original.body()).build();
return chain.proceed(request);
}
}

Ensuring a valid auth token is always available

I've been trying to figure out how to authenticate users for my android app. It is based on a website which already has a developed api, using JWT to authenticate.
I have come against the problem of refreshing tokens. Let's say I want to fetch something from the API and I need the auth token for that. I check my current auth token. If it is expired, I need to get a new one using some sort of refresh token.
However, it seems like almost no matter how I think of trying to implement it, I run into a few problems:
I don't want the UI thread to wait while I get a new token
I would prefer that I don't have to explicitly check whether the token
is there (and then refresh it) before making any API call
I've come up with one solution that solves #1 and at least minimizes the pain of #2. I can have some sort of getToken method. As an example, using JS style promises because they're easier for me to understand:
function getToken() {
return new Promise((resolve) => {
// Check for token, and return if valid.
// Otherwise, go to the server and get a new one
...
resolve(token)
}
}
// When making an API call
getToken().then((token) => {
// Call API
})
I think I can work this out so that the request will never be running on the UI thread, which solves #1, and as far as #2, it's at least bearable.
My question is this: is there a better way to do this? It kind of seems like AccountManager might be able to handle this sort of thing for me, but the documentation for it is subpar at best, so I'm not sure how I would even implement it. If AccountManager can do it and you know of a good tutorial for it, please comment with that.
A way to accomplish this is intercept a 401 status code and refresh token.
If you are using Volley, you can extend Request class and override parseNetworkEror(VolleyError error) method. If need be, schedule a Job which will refresh the token (JobDispatcher) and trigger an event to communicate UI about the change (EventBus).
The following example is using OAuth authentication, but can be easily changed to implement JWT.
#Override
protected VolleyError parseNetworkError(VolleyError volleyError) {
if (getDataAccess().shouldRefreshToken(volleyError)) {
if (!EventBus.getDefault().hasSubscriberForEvent(TokenRefreshedEvent.class)) {
EventBus.getDefault().register(this);
}
CSApplication app = CSApplication.getInstance();
FirebaseJobDispatcher dispatcher = app.getJobDispatcher(app.getApplicationContext());
Job myJob = dispatcher.newJobBuilder()
.setService(JobRefreshToken.class)
.setTag("REFRESH_TOKEN")
.setTrigger(Trigger.NOW)
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
.setConstraints(Constraint.ON_ANY_NETWORK)
.build();
int result = dispatcher.schedule(myJob);
if (result == FirebaseJobDispatcher.SCHEDULE_RESULT_SUCCESS) {
LogUtils.log(LogUtils.Type.JOB, GsonRequest.class, "Scheduling job refresh token");
} else {
LogUtils.log(LogUtils.Type.JOB, GsonRequest.class, "Error on schedule refresh token");
}
}
return super.parseNetworkError(volleyError);
}
public boolean shouldRefreshToken(VolleyError error) {
boolean shouldRefreshToken = error.networkResponse != null && error.networkResponse.statusCode == 401;
if (shouldRefreshToken) {
Map<String, String> headers = error.networkResponse.headers;
if (headers.containsKey("WWW-Authenticate")) {
String value = headers.get("WWW-Authenticate");
boolean issuerInvalid = value.contains("The issuer is invalid");
shouldRefreshToken = !issuerInvalid;
if (issuerInvalid) {
log(LogUtils.Type.VOLLEY, DataAccess.class, "Issuer do token é inválido");
}
}
}
return shouldRefreshToken;
}
Job Code
getDataAccess().refreshToken(getApplicationContext(), new VolleyCallback<Void>() {
#Override
public void onSuccess(Void aVoid) {
EventBus.getDefault().post(new TokenRefreshedEvent(true));
job.jobFinished(params, false);
log(LogUtils.Type.JOB, JobRefreshToken.class, "Refresh Token job finished");
}
#Override
public void onError(VolleyError error) {
super.onError(error);
EventBus.getDefault().post(new TokenRefreshedEvent(false));
job.jobFinished(params, false);
}
});
return true;
}
What I ended up doing was creating a method getToken which either returns the current token or gets a new one (blocking). With this strategy, I need to make sure that it never gets called from the UI thread. I created a Retrofit2 interceptor which calls getToken. The benefit of this method is that I can just call my Retrofit methods without worrying about the token at all, and it checks for expiration and gets a new one as necessary.

Merge and handle two RxJava Observable of different types

My goal
I want to check if the server's token is still valid, let's say I know that information just by calling this getter : preferenceHelper.isTokenValid(). Then, if the token is invalid, calling a request to get a new token and updating the token locally, THEN, proceed with the next request to post the point to the server. That's because I need a valid token in order to make any further server request.
Let say I have those two server request that returns Observable:
This request is meant to get the server token, then upon reception, updating it.
Observable<Response<EntityToken>> updateServerToken = retrofitApi.authenticate(username,password);
This request is meant to post the current location to the server, then if it succeed, return the saved point
Observable<Response<EntityPoint>> updateServerToken = retrofitApi.postPoint(point);
Issues i'm facing currently:
Both observable that needs to be merged are from different type
Executing the token update request only if it needs to
Waiting for the token update request to complete before executing the request to post points
How should I write my RxJava Observable to satisfy all those condition?
First, I would create a method that checks if the entityToken is valid or not. If valid, use Observable.just() but you have to create an instance of Response somehow. If invalid, then call the server using the API in your requirement retrofitApi.authenticate(). Either path is taken, the method getTokenObservable() emits Observable<Response<EntityToken>>.
public Observable<Response<EntityToken>> getTokenObservable(EntityToken entityToken, String username, String password) {
boolean isTokenValid = preferenceHelper.isTokenValid(entityToken);
if (isTokenValid) {
//my assumption that you have something like this
Response<EntityToken> responseToken = new Response<EntityToken>();
responseToken.setEntityToken(entityToken);
return Observable.just(new Response<EntityToken>(entityToken.class));
} else {
Observable<Response<EntityToken>> updateServerToken = retrofitApi.authenticate(username, password);
return updateServerToken;
}
}
and then when calling it, use flatMap() which take emisssions of Observable<Response<EntityToken>> and returns emissions of Observable<Response<EntityPoint>>. Subscribe and proceed as normal.
Observable<Response<EntityToken>> updatePointObservable = getTokenObservable(entityToken, username, password);
updatePointObservable
.flatMap(new Func1<Response<EntityToken>, Observable<Response<EntityPoint>>>() {
#Override
public Observable<Response<EntityPoint>> call(Response<EntityToken> responseToken) {
EntityToken entityToken = responseToken.getEntityToken(); //my assumption
saveTokenLocally(entityToken); //this is where you save your token locally, change to the right method that you have
Observable<Response<EntityPoint>> updateServerTokenObservable = retrofitApi.postPoint(point, entityToken); //pass your entityToken to the call?
return updateServerTokenObservable;
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Response<EntityPoint>>() {
#Override
public void onCompleted() {
//your own logic
}
#Override
public void onError(Throwable e) {
//your own logic
}
#Override
public void onNext(Response<EntityPoint> entityPoint) {
//your own logic
}
});
As there is a dependency between the three calls, merge does not make any sense. instead, use flatMap:
Observable<Response<EntityPoint>> response =
retrofitApi.isTokenValid()
.flatMap(isValid ->
isValid
? Observable.just("")
: retrofitApi.authenticate(username,password)
.doOnNext(token -> doSomethingWithTheToken(token)
)
.flatMap(dummy -> retrofitApi.postPoint(point));

Blocking request interceptor with Retrofit?

Is there a nice way to implement "blocking" request interceptor?
The main idea is that all requests should be intercepted and added additional header - token.
If token does not exist yet it should be retrieved first, then added to that request and cached for future used. token is retrieved via API call.
I've tried to do synchronous request, but, that produces android.os.NetworkOnMainThreadException. And implementing with in_progress flags it doesn't look nice.
You can already do the 'intercept' part of this using RequestInterceptor. Just use RestAdapter.Builder.setRequestInterceptor().
It's a better idea to retrieve the token from the API outside the RequestInterceptor though, as it's not meant to do that. After that first call, you can just add the token anywhere you want in your requests inside RequestInterceptor.intercept().
Something like this:
Builder builder = new RestAdapter.Builder()
//Set Endpoint URL, Retrofit class... etc
.setRequestInterceptor(new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
String authToken = getAuthToken(); //Not included here, retrieve the token.
request.addHeader("Authorization", "Bearer " + authToken);
}
);
Well, you have already implemented your 'blocking' interceptor, your problem is android doesn't let you block the main thread with network calls.
You should probably wrap your retrofit calls in a service class that calls, asynchronously, to your getToken method, and makes the 'main' request only if and when that first one completes succesfully.
As of OkHTTP 2.2, you can now add interceptors that run on the network thread:
https://github.com/square/okhttp/wiki/Interceptors
An example interceptor for adding an auth token might be like this;
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// Get your auth token by going out over the network..
// add authorization header, defaulting to the original request.
Request authedRequest = request;
if (!TextUtils.isEmpty(authToken)) {
authedRequest = request.newBuilder().addHeader("Auth", authToken).build();
}
return chain.proceed(authedRequest);
}

Categories

Resources