Spring fo Android : request authorization headers (not basic) - android

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);

Related

How to access cookie and check if it has expired using okhttp3 and PersistentCookieStore?

I am working on an Android app in which a log in post request is made to a webservice. The request returns a cookie which expires in 20 minutes.
Using okhttp3 and this PersistentCookieStore library, I got the cookie to be stored and subsequently added it as request header to access authentication-required get requests (e.g. personal information that are non-public).
The code goes this way,
CookieJar myCookieJar = new PersistentCookieJar(new SetCookieCache(),
new SharedPrefsCookiePersistor(this));
OkHttpClient client = new OkHttpClient.Builder().cookieJar(HttpRequests.cookieJar).build();
I then call a method like this inside an (after I have gone through another log in Async task to get the cookie) Async task to perform a get request that requires authentication,
public static String PostReq(String url, String json) {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.addHeader("Cookie", "key=value")
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
catch(Exception e){
}
}
The .addHeader("Cookie", "key=value") adds the cookie to the header to tell the webservice that I am authenticated.
Here comes my difficulty. Since the cookie expires after 20 minutes, I would like to be able to access the cookie itself to check for the expiration time and possibly redirect the user to the log in activity by calling the method,
myCookie.expiresAt()
and comparing it to
System.currentTimeMillis()
I tried to look at the PersistentCookieStore codes and found that it uses a SharedPreference with the key "CookiePersistence". I looked inside this file while my emulator was running the app and found it to be empty however.
How would I be able to access this cookie that I have obtained? Much thanks for any advice to be given.
OK, this is old, but I was facing the same problem, and here is how I fixed it.
Hold a reference to your SetCookieCache used to instantiate your CookieJar:
SetCookieCache cookieCache = new SetCookieCache();
CookieJar myCookieJar = new PersistentCookieJar(
cookieCache,
new SharedPrefsCookiePersistor(this)
);
Then use this to find your cookie and check it:
for (Cookie cookie : cookieCache) {
if (cookie.name().equals("cookie_name") && cookie.persistent()) {
//cookie is still good
break;
}
}
Or use cookie.expiresAt() to do your thing.

Okhttp Put method returning 'Method Not Allowed'

I am attempting to call a put method on my server using OkHttp from an Android application.
This is the api method signature:
public void Put(int userId, string regId)
{
}
This is the Android code to call the above method:
private boolean SendGCMRegIdToServer(String registrationId, Integer userId) throws IOException {
HttpUrl url = new HttpUrl.Builder()
.scheme("http")
.host(serverApiHost)
.addPathSegment("AppDashboard")
.addPathSegment("api")
.addPathSegment("GCM/")
.build();
MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
String json = "{'userId':" + userId + ","
+ "'regId':'" + registrationId + "'"
+ "}";
RequestBody requestBody = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.put(requestBody)
.build();
//this should post the data to my server
Response response = client.newCall(request).execute();
if(response.code() == 400)
return false;
return true;
}
Now the problem is I am getting the error code 405 in the response saying Method not allowed, but I cannot see where the problem is because I can successfully call the method using Postman on the server itself as below:
http://localhost/AppDashboard/api/GCM?userId=5&regId=123
I'm thinking it may have something to do with an integer or string being passed incorrectly in the JSON string, but cannot see why this isn't working.
i had the same problem and server was returning 405 . after some search i realized that is a configuration problem on IIS that does not let put requests. so there is no problem in android code and you should config your server to let this kind of requests.
see this , this and this
Ok thanks for replies guys but seems I was getting a little confused between the two methods I was using to pass the params to my API.
Here's what I did:
changed the signature of the method to post with a param [FromBody] as a Model (only supports one paramater)...
public void Post([FromBody]UserGcmRegIdModel model)
{
}
I was then able to change my method call to the following using a nicer JSONBuilder and using .post in the request builder rather than .put
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("UserId", userId);
jsonObject.put("RegId", registrationId);
} catch (JSONException e) {
e.printStackTrace();
}
String json = jsonObject.toString();
RequestBody requestBody = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
I still don't know if there is a problem with put() methods on IIS but using a post in my case was absolutely fine so I'm going with that...
I see two different approaches in your REST api calls. In the one of OkHttp you send a PUT method with a JSON object serialized, and in POSTMAN you send a PUT (although I guess you do a GET) request with the parameters within the URL, I mean not in JSON body structure.
Anyway, HTTP 405 is telling you that your backend does not support the PUT method, and probably it's expecting a POST method with the "X-HTTP-Method-Override:PUT" HTTP header since POST is more standard method in REST than PUT.
What would I do is check your POSTMAN request carefully and adjust the one of Android to be the same method, parameters and headers, not more.
Answer Update (as question has been updated)
Of course there is a problem with that verb, as I said above IIS handles only the standard methods and PUT is not one of those. You have three choices:
Change your PUT to POST.
Use POST with X-HTTP-Method-Override to PUT. (reference)
Modify IIS config to support non standard REST methods. I
personally wouldn't suggest the 3rd one, since it's attached to the
backend config (e.g. imagine you change IIS to NancyFX).

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);
}
}

How can i send authentication tokens in android for api

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.

Web service returns SOAP when content-type is not specified

We developed a .NET web service for mobile application several years ago. This service is being called by iPhone/Android/Blackberry/WindowsPhone and all native apps developed by a third party. We added support for JSON, so some apps access this service using JSON calls, and some using SOAP.
The webservice returns JSON only when the request is sent with HTTP header Content-type: application/json.
We encountered a problem with one Android platform (specifically the Galaxy Nexus), where the Content-Type header is missing for GET requests. Our third party app developer tried many solutions but could not find a way to force send the Content-Type for GET requests.
However, we did notice that the Accept header is set correctly and sent, but I found no way to change the web service to use that header instead of Content-Type to return JSON in those cases.
Here is the example request, which results with XML response, and not JSON as needed.
GET /mobile/service.asmx/Logon?system=2&username='test'&password='1234' HTTP/1.1
Accept: application/json
User-Agent: Apache-HttpClient/UNAVAILABLE (java 1.4)
Connection: Keep-Alive
And excerpt from the webservice code:
[WebMethod(
BufferResponse = false,
CacheDuration = 0
)]
[ScriptMethod(UseHttpGet = true,ResponseFormat = ResponseFormat.Json) ]
public LogonResponse Logon(int system, string username, string password)
{
return service.Logon(system, username, password);
}
Is there a way to force JSON response in some way, or inspecting the Accept header to do so? (Other than migrating to WCF?)
If not, I was told by the app developer they use the Spring framework to make HTTP requests. If there's a solution on how to make it work on the app side and force send the Content-Type header for GET requests, it's also appreciated!
Thanks!
You could try this (unable to test at the moment).
public LogonResponse Logon(int system, string username, string password)
{
string accept = HttpContext.Current.Request.Headers("Accept");
if (!string.IsNullOrEmpty(accept)) {
if (accept.ToLower.EndsWith("application/json")) {
HttpContext.Current.Response.ContentType = "application/json";
}
}
return service.Logon(system, username, password);
}
Edit: Updated the Request to response
Thanks for the reply.
Although setting the content-type did not solve the issue, it did point me in the right direction.
The following solved it for me:
[WebMethod(
BufferResponse = false,
CacheDuration = 0
)]
[ScriptMethod(UseHttpGet = true,ResponseFormat = ResponseFormat.Json) ]
public LogonResponse Logon(int system, string username, string password)
{
return SetOutput<LogonResponse>(service.Identify(username, password));
}
and the actual conversion:
public static T SetOutput<T>(object response)
{
var accept = HttpContext.Current.Request.Headers["Accept"];
var ctype = HttpContext.Current.Request.ContentType;
if (string.IsNullOrEmpty(ctype) && !string.IsNullOrEmpty(accept))
if (accept.ToLower().EndsWith("application/json"))
{
var serializer = new JavaScriptSerializer();
var json = serializer.Serialize(response);
HttpContext.Current.Response.ContentType = "application/json";
HttpContext.Current.Response.Write(json);
HttpContext.Current.Response.End();
}
return (T)response;
}
Cheers!
Use the following to also wrap the whole thing with the expected "d" element:
public static T SetOutput<T>(T response)
{
var accept = HttpContext.Current.Request.Headers["Accept"];
var ctype = HttpContext.Current.Request.ContentType;
if (string.IsNullOrEmpty(ctype) && !string.IsNullOrEmpty(accept))
if (accept.ToLower().EndsWith("application/json"))
{
var wrapper = new JSONWrapper<T> {d = response};
var serializer = new JavaScriptSerializer();
var json = serializer.Serialize(wrapper);
HttpContext.Current.Response.ContentType = "application/json";
HttpContext.Current.Response.Write(json);
HttpContext.Current.Response.End();
}
return response;
}
public class JSONWrapper<T>
{
public T d { set; get; }
}

Categories

Resources