I have an application that is implemented with clean architecture with MVVM pattern. In this app we need a refresh token request that is needed in all the app features. When a refresh token is success then call the last request again. What is the best way to implement this?
I have two idea:
1 - Implement it in every feature and use it. So if I have three features in my app I will implement it three time.
2 - Implemented globally
I know the first idea but I can't figure out how to do the second one which I think is better.
I use retrofit for networking . The structure is : data , domain , presentation .
With Retrofit you can create a custom Authenticator which will trigger when a request was denied because of an authentication error (typically 401, the documentation for the Authenticator interface explains more). In your authenticator you can retrieve a new token and automatically create a new request with the new token.
An authenticator will be something like:
class Authenticator : okhttp3.Authenticator {
override fun authenticate(route: Route?, response: Response): Request? {
// Make your network request to retrieve a new token
val newToken = ...
// Check if a new token was retrieved
val retrievedNewToken: Boolean = true
return if (retrievedNewToken) {
response.request().newBuilder()
.header("Authorization", "token-value-here")
.build()
} else {
// Couldn't get new token, abort the request
null
}
}
}
And use it in your OkHttpClient:
val client = OkHttpClient.Builder()
.authenticator(Authenticator())
.build()
This is a fairly general answer as I can't provide any specific way of doing it since I don't know the rest of your code, but that's the gist of it. Something to be aware of is that you might need to handle if a new token request is already happening, as it will potentially make multiple requests for a new token if you make several requests right after each other that all are denied.
Related
I have a retrofit api interface where one of the calls requires OAuth1 authentication and the others do not. I am trying to find a way to add the Authentication header without using an Interceptor for a few reasons.
At the time the OkHttp Client is created I wont know OAuth1 keys and the client is used for other things too.
It is only one request that needs OAuth1 authentication
What I was trying to do is wrap the interface call in a Call and then adding the header before I process
val call = api.getRecentTweetsV1("$query -filter:replies -filter:retweets")
val oauth = Oauth1Signing.Builder()
.consumerKey(consumerKey)
.consumerSecret(consumerSecret)
.accessToken(token)
.accessSecret(tokenSecret).build()
val authHeader = oauth.signRequest(call.request())
call.request().headers.newBuilder().add("Authorization", authHeader).build()
val headers = call.request().headers
response = call.execute().body()
The problem is I don't see the header getting added in, the call.request().headers value is always empty after I try to add it in.
I cant use the #Header parameter in the interface call because the OAuth1Signer (modified version of this, I removed the Interceptor usage) takes in a OkHttp Request object to build the header.
Is there another way to add the header after the request has been built?
I have two APIs say API 1 and API 2. API 1 get authentication token and API 2 get user profile info. I need to hit API 2 from ViewModel but if API 2 gives an authentication error, then I need to get new token first and then hit API 2(user info)
Currently, I use RxJava Single and Retrofit for all APIs but for APIs one by one. There are a lot of APIs that use authentication token and if it expires in any of the APIs then I need to call authentication API and get the token again and then call the APIs with a new authentication token.
What should be the best way to design a wrapper so that it can be used with all APIs with code reusability as well.
Thanks in advance.
I have some idea. If you have basemodel for getting response for API, you can make custom getter for response code and check if response code in token_expired.
This is sample response that I got From API.
{
response_code: "200",
response_msg: "",
result: ...
}
And this is my BaseModel.
class BaseModel<T> {
#SerializedName("response_code")
var response_code: Int? = null
get() {
if(field == ErrorStatusCode.TOKEN_EXPIRE) {
ErrorCodeHandler.requestNewToken()
}
return field
},
#SerializedName("response_msg")
var response_msg: String? = null
#SerializedName("result")
var data: T? = null
}
So whenever token expire, I will request Net token by call ErrorCodeHandler.requestNewToken()
But If you want to recall API that found token timeout I have no idea what to do too.
PS. Hope this help.
Lately I have been facing a problem that I wasn't able to came out with a solution, I am building an android application following the Clean Architecture and everything was going fine until I had to think about the authentication role.
I have this structure (layers) on my app:
[ui](activities and fragments) -> [presentation](view models) -> [domain](use case) -> data -> [remote, cache, database].
Now, let's suppose that I want to log in into my app, first I'll go through the login screen and put users credentials, after that I'll call the LoginViewModel and then the LoginUseCase passing the email and password. In its turn, the use case will call the repository, let's say, authenticate and then I'll make a request to the backend with the credentials, if everything's ok then I'll receive back a token that I should store in some fashion, the problem start here, I've created a interceptor that is responsible to get the token from the header, but I have to save it and for that I need to access the shared preferences, is correct to have access to it inside my interceptor? And in every request I had to send it to my backend, what's the best approach ?
I also saw this tutorial https://medium.com/#tsaha.cse/advanced-retrofit2-part-2-authorization-handling-ea1431cb86be but I think that it's not correct to have access to database inside your application class, am I wrong?
Thank you all for reading this, I'm struggling to find out the best approach, so any help are welcome.
it's not really related to CleanArchitecture. Here're answers for your questions:
I have to save it and for that I need to access the shared preferences, is correct to have access to it inside my interceptor?
-> Yes, you should save it to SharedPreferences, but you should not access SharedPreferences inside your interceptor. You should make your interceptor Singleton and create a new setHeaderToken(String token) function in your interceptor. After authorizing, you can set header token to your interceptor. Something like:
class MyInterceptor{
String token = null;
public void setHeaderToken(String token){
//do set...
};
#Override
public Response intercept(Chain chain) throws IOException {
if(token == null) //do nothing
else // do add header
}
}
// add the singleton Interceptor to your OkHttp Client Builder and use it.
You should not connect Application class with your database directly. You should use a domain layer to do so instead.
I have an app that authenticates using OAuth2 and fetches data from a RESTful service using Retrofit. Now, I have the token retrieval and refreshing up and running. The token is refreshed like so (schedulers omitted):
// Each Retrofit call observable is "wrapper" using this method
protected <T> Observable<T> wrap(#NonNull final Observable<T> page) {
return authenticate()
.concatMap(token -> page)
.onErrorResumeNext(throwable -> {
Log.w(TAG, "wrap: ErrorResumeNext", throwable);
return refreshAccessToken()
.flatMap(accessToken -> page);
}));
}
// Retrieves the access token if necessary
Observable<AccessToken> authenticate() {
// Already have token
if(accessToken != null) return Observable.just(accessToken);
// No token yet, fetch it
return api.getAccessToken(...);
}
// Refreshes the token
Observable<AccessToken> refreshAccessToken() {
return api.refreshToken(...);
}
This works, but in some cases, multiple requests are sent at once and they both invoke the refreshing process - basically my app ends up refreshing the token as many times as there were requests at the moment.
So, the question is: How do I ensure that when the token needs to be refreshed, it is done only once, no matter how many ongoing requests require the token to be refreshed? Can I somehow make the other requests "wait" until the first request sucessfully invoked and retrieved the new token?
We have accomplished this behavior using a hot observable for refreshing the token and providing access to its instance for all requests that failed to authenticate.
Use share operator to turn your basic cold observable for refreshing a token into a hot one, so every other subscriber shares its result. Once the request comes back, all awaiting observers gets notifies and in that moment (in the operator chain it comes right before share() into a callback for doOnUnsubscribe) destroy the refreshing observable instance so the next subscriber will create new one. All this can be easily achieved by a singleton pattern, where you wrap the refreshing observable into a singleton wrapper class and just ask for it through getInstance(). If there is no request going on -- the instance is null -- getInstance should create a new one.
There are some other things you need to take care of, error during refreshing and invalidating the token all together for example, but these are the basics.
I don't have much time right now to elaborate more on this, but if you will encounter some trouble implementing this by your own, leave a comment and I will post some code examples by tomorrow. They wouldn't make much sense without a context.
I am creating app that consumes REST Api. My API has ability to login/logout in order to access private data. I am creating consumer(client Android app) with Retrofit + Robospice + Jackson.
Everything was okey, but than authorization part came into play.
I need to provide token and other credentials in my request Authorization Header.
This can be easily done by using RequestInterceptor. Here is complete tutorial how to use basic Authentication.
Basic Authentication with Retrofit .
It is clear how to implement this. But in my case I also have resources that can be accessed without credentials.
Here is part of my API declared with Retrofit Annotations
public interface MalibuRestApi {
//Doesn't need credentials
#GET("/store/categories")
Category.List categoryList();
//Doesn't need credentials
#GET("/store/categories/{id}/products")
Product.List productList(#Path("id")int categoryId);
// Needs credentials
#POST("/store/users/{id}/logout")
// Needs credentials
User logout(#Path("id") int id,#Body Credentials userCredentials);
// Needs credentials !!!!!!!!!!!!!!!!!!!!
#POST("/store/users/{id}/orders/")
void makeAnOrder(#Path("id") int userId,#Body Order order,Callback<Void> callback);
}
Please have a look on makeAnOrder method. It uses POST body to pass details about order. So combining credentials and order seems horrible and not efficient, and I won't use it under no circumstances.
It is possible to use interceptor.
builder.setRequestInterceptor(new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
if
String token = .... // getting token.
request.addHeader("Accept", "application/json");
request.addHeader("Authorization",token);
}
});
I can filter requests and add Auth headers where I need them according to request URL, but ......
According to the discussion here.
#JakeWharton
The relative URL is not exposed in the request interceptor because it
may not be fully resolved yet. A request interceptor has the ability
to perform path replacements and append query parameter
There is one possible workaround.
#powerje
I'm not sure how to access the relative URL in RequestInterceptor but
the solution I used in a similar situation was to check my UserManager
(a global which manages the currently logged in user) to see if a user
is logged in, if they are I add the auth header, otherwise I don't.
I have already similar class Session manager that is created in custom Application class, so I assume that it will live until application(linux dalvik/art process) is destroyed.
So it is possible to do something like this.
builder.setRequestInterceptor(new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
sessionManager =(SessionProvider) getApplicationContext().getSessionManager();
if(sessionManager.userLoggedIn) {
String token = .... // getting token.
request.addHeader("Accept", "application/json");
request.addHeader("Authorization",token);
}
}
I haven't tested this yet, but it pretends to work.
In this case redundant headers are passed to public resource requests, that don't really require them.
So maybe this can be a sort of solution not a question, but I really need some advice about any other ways (maybe not better) to solve this problem.
I will be grateful for any help.