How can i send authentication tokens in android for api - android

I want to be able to add authentication token every request and do it in a general way.
How to best achieve this in android app.

Write a ClientHttpRequestInterceptor if you are using Spring for android or just
http://developer.android.com/reference/org/apache/http/HttpRequestInterceptor.html
Then add the interceptor to your requests using a general singleton class for processing requests.
ArrayList<ClientHttpRequestInterceptor> interceptor = new ArrayList<ClientHttpRequestInterceptor>();
interceptor.add(new LoginInterceptor(getApplicationContext()));
restTemplate.setInterceptors(interceptor);
This interceptor should add the auth token if available:
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException {
String appAuthToken = settings.getString(
AUTH_TOKEN.toString(), "");
// response
request.getHeaders().add("auth-token", appAuthToken);
return execution.execute(request, body);
}
Thats it server side you can check this.

Related

Spring fo Android : request authorization headers (not basic)

I have an app that uses Spring for Android on the Client side and Spring Boot on the Server side. We would like to add client authorization to some requests. We already use Firebase and OAuth2 and after reading on the subject, I feel like the best way to go would be to use the Authentification header to send the JWT to our server, with the bearer method for the authorization :
Authorization: Bearer <token>
Something like that ...
My problem is this : Spring for Android only has BasicAccessAuthentification built-in, but I dont have a username:password scheme for my client credentials, only a JWT. I quite naively tried creating a Class that extends HttpAuthentication :
import org.springframework.http.HttpAuthentication;
public class HttpBearerAuthentification extends HttpAuthentication {
private String token;
public HttpBearerAuthentification(String token){
this.token = token;
}
public String getHeaderValue() {
return String.format("Bearer %s", token);
}
#Override
public String toString() {
return String.format("Authorization: %s", getHeaderValue());
}
}
This class is based on the HttpBasicAuthentication class in Spring for Android.
And then doing the request :
// regular request that I know works
...
HttpBearerAuthentification auth = new HttpBearerAuthentification(token);
headers.setAuthorization(auth)
...
// send request and get answer as usuall
I know the requests themselves work (requests without without authorization, at least) so this is not the problem. Would that be the right way to do it ? I understand the success of the request also depends on how the server handle the request. But for now, my question is really about the client side. Is this code enough to send the JWT to the server ?
After some testing on requests sent with SpringForAndroid, it seems that is is quite easy to set values for headers. Because the HttpHeaders class actually implements the Map interface, all I had to do was :
protected HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + userRegistrationToken);

OkHttp MockWebServer with dynamic URLs using Retrofit

My app uses dynamic URLs to make web-service calls (on Android). baseUrl is set as empty and we pass Retrofit2 #Url parameters in the service interface:
public interface UserService {
#GET
public Call<ResponseBody> profilePicture(#Url String url);
}
We don't know the host/domain in advance, so MockWebServer is not able to intercept the requests. The call to fetch the initial list of dynamic URLs is made in different screens. One idea is to create a new flavor providing a local data source for URLs to be used, which is my fallback plan.
I am curious if MockWebServer has any other methods to help test such cases and can be limited to test code.
You could use an OkHttp interceptor to rewrite the hostname and port?
I was also facing the same kind of issue. When I use MockWebserver in testing I have to change base URL to target mock web server localhost and port. I tried this it is working fine.
private static final Interceptor mRequestInterceptor = new Interceptor() {
#Override
public okhttp3.Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
final InetSocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(), 8080);
HttpUrl httpUrl = request.url().newBuilder().scheme("http://").host(address.getHostName()).port(8080)
.build();
request = request.newBuilder()
.url(httpUrl)
.build();
return chain.proceed(request);
}
};
After this base url changes to "http://localhost:8080/"

How to handle auth0 403 error without adding specific code everywhere (Retrofit/okhttp/RxAndroid)

I am using Auth0, which gives me a JWT (json web token) and a refreshtoken. I use this JWT in the http headers to communicate with my backend.
It could happen, that the server gives me a 403, when it decides that the JWT has expired. In this event, I can ask Auth0 to issue me a new JWT, using the refreshtoken. It means I call the Auth0 backend, pass it the refreshtoken, and it gives me a new JWT, which I can then use in my requests.
My question is, how can I efficiently write this behaviour in all my networking code? I will have a couple of endpoints to talk to, and they all might return the 403.
I am thinking I should first make an interceptor that adds the JWT to all requests.
Then there should be behaviour that detects the 403, quietly does a networkcall to Auth0, retrieving the new JWT. Then the original request should be tried again, with the new JWT in its headers.
So I would prefer to have this 403 handling somewhere invisible to my other code, and definitely not have to rewrite it everywhere.
Any pointers on how to achieve this will be appreciated.
--
To be clear, I am basically looking for pointers on how to achieve this using RxAndroid Observables. When a certain Observable finds the 403, it should 'inject' a new network call.
I solved this issue by writing an Interceptor for OkHttp. It checks the statuscode of the network call. If it's a 403, call Auth0 servers and request a new id_token. Then use this token in a new version of the original request.
To test, I wrote a little webserver that checks the TestHeader for fail or succeed and returns a 403 if it's fail.
public class AuthenticationInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
Request authenticationRequest = originalRequest.newBuilder()
.header("TestHeader", "fail")
.build();
Response origResponse = chain.proceed(authenticationRequest);
// server should give us a 403, since the header contains 'fail'
if (origResponse.code() == 403) {
String refreshToken = "abcd"; // you got this from Auth0 when logging in
// start a new synchronous network call to Auth0
String newIdToken = fetchNewIdTokenFromAuth0(refreshToken);
// make a new request with the new id token
Request newAuthenticationRequest = originalRequest.newBuilder()
.header("TestHeader", "succeed")
.build();
// try again
Response newResponse = chain.proceed(newAuthenticationRequest);
// hopefully we now have a status of 200
return newResponse;
} else {
return origResponse;
}
}
}
Then I attach this Interceptor to an OkHttpClient which I plug into the Retrofit adapter:
// add the interceptor to an OkHttpClient
public static OkHttpClient getAuthenticatingHttpClient() {
if (sAuthenticatingHttpClient == null) {
sAuthenticatingHttpClient = new OkHttpClient();
sAuthenticatingHttpClient.interceptors().add(new AuthenticationInterceptor());
}
return sAuthenticatingHttpClient;
}
// use the OkHttpClient in a Retrofit adapter
mTestRestAdapter = new RestAdapter.Builder()
.setClient(new OkClient(Network.getAuthenticatingHttpClient()))
.setEndpoint("http://ip_of_server:port")
.setLogLevel(RestAdapter.LogLevel.FULL)
.build();
// call the Retrofit method on buttonclick
ViewObservable.clicks(testNetworkButton)
.map(new Func1<OnClickEvent, Object>() {
#Override
public Object call(OnClickEvent onClickEvent) {
return mTestRestAdapter.fetchTestResponse();
}
}
)
Instead of refreshing tokens only after receiving a 403 response, you could check the expiration time locally and refresh accordingly by checking the token's exp claim. For example, this example uses the same approach in Angular. It's not specific to Android, but the idea is the same:
jwtInterceptorProvider.tokenGetter = function(store, jwtHelper, auth) {
var idToken = store.get('token');
var refreshToken = store.get('refreshToken');
if (!idToken || !refreshToken) {
return null;
}
// If token has expired, refresh it and return the new token
if (jwtHelper.isTokenExpired(idToken)) {
return auth.refreshIdToken(refreshToken).then(function(idToken) {
store.set('token', idToken);
return idToken;
});
// If not expired, return the token directly
} else {
return idToken;
}
}

Make a synchronous Retrofit call from inside an OkHttp Interceptor

I am trying to automatically refresh an auth token if it is expired. I am using the new Interceptor class that was introduced in OkHttp 2.2. In the intercept method I am trying the original request with chain.proceed(request), checking the response code, and if the token is expired I am making a call to a separate Retrofit service, synchronously, to obtain a new token.
The strange thing is, no code past the synchronous call seems to run. If I try debugging with a breakpoint on the synchronous call's line, then do a step-over, I am stopped in Dispatcher.java at :
if (!executedCalls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
Any idea as to what I might be doing wrong here? I could probably just craft a new request by hand, but I am just kind of curious why a Retrofit call doesn't seem to work here.
My Interceptor:
public class ReAuthInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// try the request
Response response = chain.proceed(request);
// if we receive a "401 - Not Authorized" then refresh the auth token and try again
if (response.code() == 401) {
// get a new auth token and store it
UserToken userToken = MobileClient.getOkraService().login(AuthUtil.getUserToken(MSWorksApplication.getContext()));
AuthUtil.authenticate(MSWorksApplication.getContext(), userToken);
Log.d("TEST", "TEST TEST TEST");
// use the original request with the new auth token
Request newRequest = request.newBuilder().header("Authorization", AuthUtil.getAuthToken(MSWorksApplication.getContext())).build();
return chain.proceed(newRequest);
}
else {
// the auth token is still good
return response;
}
}
}
I had this problem - if you're getting an HTTP response code that would cause Retrofit to call failure() instead of success() an exception will be thrown.
If you wrap
UserToken userToken = MobileClient.getOkraService().login(AuthUtil.getUserToken(MSWorksApplication.getContext()));
in a try/catch(RetrofitError e) you'll be able to execute code after a failure, however I've found this to be quite cumbersome and am still in the process of finding a nicer solution.

Empty request body using SpringAndroidSpiceRequest

I'm writing an Android app, which is a client to my web application. I'm trying to use RoboSpice to perform network requests.
First of all I decided to test an API call to obtain an OAuth2 token. The following curl command can be called to obtain it from command line:
curl -X POST -d "grant_type=password&username=user&password=pass" http://testid:testsecret#localhost:8000/oauth2/token/
user and pass are the credentials for a registered user and testid and testsecret are the id and secret of a registered app in my web application. This call returns a JSON object with a token and other parameters.
I'm trying to do the same request using RoboSpice. Here's the code I wrote for the request:
public class OAuth2Request extends SpringAndroidSpiceRequest<String> {
private final String user;
private final String pass;
public OAuth2Request(String user, String pass) {
super(String.class);
setRestTemplate(new RestTemplate());
getRestTemplate().getMessageConverters().add(new StringHttpMessageConverter());
this.user = user;
this.pass = pass;
}
#Override
public String loadDataFromNetwork() throws RestClientException {
String client_id = "testid";
String client_secret = "testsecret";
HttpBasicAuthentication authHeader = new HttpBasicAuthentication(client_id, client_secret);
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAuthorization(authHeader);
requestHeaders.setUserAgent("AndroidNotesApp/1.0");
String data = String.format("grant_type=password&username=%s&password=%s", this.user, this.pass);
HttpEntity<String> requestEntity = new HttpEntity<>(data, requestHeaders);
String url = "http://10.0.2.2:8000/oauth2/token/";
return getRestTemplate().postForObject(url, requestEntity, String.class);
}
}
The SpiceManager in my activity is declared like:
protected SpiceManager spiceManager = new SpiceManager(JacksonSpringAndroidSpiceService.class);
and the request is made by the following lines:
OAuth2Request req = new OAuth2Request(user, pass);
spiceManager.execute(req, new OAuth2RequestListener());
user and pass are Strings, which get their values from EditText views.
But when I try to run this request, I get an exception 400 BAD REQUEST.
I set up logging in my django app to print the requests which come to /oauth2/token/, and I see, that POST parameters are empty in this request (I expect them to be the same as during the curl request, something like {'grant_type': 'password', 'password': 'pass', 'username': 'user'}).
Why are POST parameters empty in case of RoboSpice request? What am I doing wrong?
P.S. Just in case: the oauth2 authentication in my django web application is implemented using DjangoOAuthToolkit with DjangoRestFramework.
UPDATE: I decided to setup nginx proxy before my django web application to log the request body. The request body I get from the Android app is the following:
\x22grant_type=password&username=user&password=pass\x22
So the strange \x22 symbol is added in the beginning and in the end of the body (I believe it is a double-quote " symbol, but I'm not sure). Seems that these \x22 screw up POST parameter parsing in django. How can I get rid of these symbols?
I managed to solve my problem, so I'm posting an answer in case it helps someone.
SpringAndroidSpiceRequest by default tries to map a java object into JSON, so when I tried to send a String in request body, it wrapped it in double quotes to make it a JSON string. I don't need to send a request body as a JSON string, and in order to do that I need to define additional message converters.
Strangely, these lines in constructor don't seem to do anything
setRestTemplate(new RestTemplate());
getRestTemplate().getMessageConverters().add(new StringHttpMessageConverter());
When I used debugger, it showed just one message converter, MappingJacksonHttpMessageConverter. So I decided to add my own message converters in loadDataFromNetwork method.
I needed two message converters: FormHttpMessageConverter, which will process request and make a request POST body from MultiValueMap, and MappingJacksonHttpMessageConverter, which will process the JSON response into OAuth2Token POJO, which I also declared.
I believe, that for simple testing of REST API with client (POST plain strings and receive plain strings) it'll be better to choose another implementation for SpiceRequest other than SpringAndroidSpiceRequest, but I decided to stick with it, as it'll be easier to implement the complete client for my web application.
The complete code for OAuth2Request:
public class OAuth2Request extends SpringAndroidSpiceRequest<OAuth2Token> {
private final String user;
private final String pass;
public OAuth2Request(String user, String pass) {
super(OAuth2Token.class);
this.user = user;
this.pass = pass;
}
#Override
public OAuth2Token loadDataFromNetwork() throws RestClientException {
String client_id = "testid";
String client_secret = "testsecret";
HttpBasicAuthentication authHeader = new HttpBasicAuthentication(client_id, client_secret);
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAuthorization(authHeader);
requestHeaders.setUserAgent("AndroidNotesApp/1.0");
MultiValueMap<String, String> data = new LinkedMultiValueMap<>();
data.add("grant_type", "password");
data.add("username", this.user);
data.add("password", this.pass);
HttpEntity<?> requestEntity = new HttpEntity<>(data, requestHeaders);
String url = "http://10.0.2.2:8000/oauth2/token/";
getRestTemplate().getMessageConverters().clear();
getRestTemplate().getMessageConverters().add(new FormHttpMessageConverter());
getRestTemplate().getMessageConverters().add(new MappingJacksonHttpMessageConverter());
return getRestTemplate().postForObject(url, requestEntity, OAuth2Token.class);
}
}

Categories

Resources