So I am building an Android app which talks to an API.
Whenever a user logs in, I see the response in the log cat which contains the user details including the authentication token but the user gets unauthorized access although the login is successful.
My question is; how do I authorize the user? How do I save the token header in android using volley? This concept is new to me. Please help.
After you login, you need to save the session cookie, access token or whatever your api provides. I usually use SharedPreferences.
Later within your next api Request, you need to override the getHeaders();
JsonObjectRequest volleyRequest = new JsonObjectRequest(url, null, responseListener, errorListener){
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> params = new HashMap<>();
// put your accesstoken if available
params.put("X-CSRF-Token", accessToken);
// put a session cookie if available
params.put("cookie", sessionName + "=" + sessionId);
return params;
}
};
Related
i am trying to sign a http request to aws api gateway in android using okhttp. i have more or less used the code in this stackoverflow question stackoverflow question
i use CognitoCachingCredentialsProvider() to get a credentialsProvider object. i then use getCredentials() to get the credentials. i then use the following: credentials.getAWSAccessKeyId(), credentials.getAWSSecretKey() and credentials.getSessionToken() to get the necessary keys and token. i use them in postman and am able to successfully execute the api gateway.
the request fails in android using okhttp, returning a code 403 with the message "Missing Authentication Token".
this is how i prepare the request: i build a DefaultRequest object, setting the endpoint and httpmethod. i then use AWS4Signer to sign the request, passing the credentials object as the signer.sign(defaultRequest, credentials) parameter.
i get a map of headers by calling getHeaders() on the defaultRequest. i create two lists, one called key for the key and one called value for the value. i then loop through the map, loading the keys and corresponding values into the two lists.
i then build my okhttp request as follows:
Request request = new Request.Builder()
.url(my ApiEndPoint)
.addHeader(key.get(0), value.get(0))
.addHeader(key.get(1), value.get(1))
.addHeader(key.get(2), value.get(2))
.addHeader(key.get(3), value.get(3))
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.post(body)
.build();
i notice the following:
in the headers map, key x-amz-security-token has a value ....ending in hKADF87VZ44w9IvZ1gU=
printing out the okhttp request, the key x-amz-security-token has a value .... ending in hKADF87VZ44w9IvZ1gU\u003d
the = is replaced by \u003d, could this be the problem? if so, how to prevent this?
otherwise, any help in solving this problem will be greatly appreciated.
thanks
managed to solve the problem. seems that assigning the headers to the OkHttp request was the problem. so here's my code:
i first get AWSSessionCredentials credentials. then:
AmazonWebServiceRequest amazonWebServiceRequest = new AmazonWebServiceRequest() {
};
String API_GATEWAY_SERVICE_NAME = "execute-api";
com.amazonaws.Request requestAws = new DefaultRequest(amazonWebServiceRequest, API_GATEWAY_SERVICE_NAME);
you can use either the service endpoint:
URI uri = URI.create("https://apigateway.eu-west-1.amazonaws.com");
or your api url (the invoke url for api as per Api Gateway console Stages option (The deployed api)):
String invokeUrl = "https://xxxx.execute-api.eu-west-1.amazonaws.com/yyy/zzzzz";
// using the invoke url
URI uri = URI.create(invokeUrl);
requestAws.setEndpoint(uri);
requestAws.setResourcePath(invokeUrl);
requestAws.setHttpMethod(HttpMethodName.POST);
now sign the request
AWS4Signer signer = new AWS4Signer();
signer.setServiceName(API_GATEWAY_SERVICE_NAME);
signer.setRegionName(Region.getRegion(Regions.EU_WEST_1).getName());
signer.sign(requestAws, credentials);
get the headers
// get map of headers
Map<String, String> headers = requestAws.getHeaders();
// create objects for the headers to add manually in OkHttp request builder
String x_date = null;
String x_token = null;
String authorization = null;
//get and assign values
for (Map.Entry<String, String> entry : headers.entrySet()) {
if (entry.getKey().equals("x-amz-security-token")) {
x_token = entry.getValue();
}
if (entry.getKey().equals("X-Amz-Date")) {
x_date = entry.getValue();
}
if (entry.getKey().equals("Authorization")) {
authorization = entry.getValue();
}
}
build the OkHttp request:
Request request = new Request.Builder()
.url(invokeUrl)
.addHeader("Content-Type", "application/json")
.addHeader("X-Amz-Date", x_date)
.addHeader("x-amz-security-token", x_token)
.addHeader("Authorization", authorization)
.post(body)
.build();
now make your OkHttp call.
hope this is helpful to someone.
Good evening,
Following this discussion, we are facing a new problem. We are trying to make a POST request (login) work on Android using the Volley library to make HTTP requests. The /login/ works well on Postman or Advanced REST Client, but it doesn't when using Volley. We have seen many other persons facing this problem and trying to find an answer on SO, but the only answer was to disable CSRF and we really don't want to do this.
On Postman, the response is 200 OK.
On Android Volley, the response is 403 forbidden : CSRF cookie not set.
Since we set CSRF_USE_SESSIONS as True, it doesn't make sense for us.
CSRF_USE_SESSIONS is True in Django
The library used to make HTTP requests on Android is Volley
We don't want to disable CSRF protection/middleware (I'm pointing this because many answers in other posts talking about this problem suggest to disable CSRF, but we are using it for both web client and mobile apps)
Here is the Java request :
private void loginPost(final String csrf) {
RequestQueue queue = Volley.newRequestQueue(getActivity());
String url = "https://api.ourapi.com/login/";
JSONObject object = new JSONObject();
try {
object.put("username", "hello");
object.put("password", "world");
System.out.println(object);
} catch (JSONException e) {
Log.d("Dude", "RIIIIIIIIIIIIIIIIIIIP");
}
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest
(Request.Method.POST, url, object, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
// response
System.out.println("######################################");
System.out.println(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// TODO Auto-generated method stub
System.out.println(error);
}
}
) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Accept", "application/json");
params.put("X-CSRFToken", csrf);
System.out.println(params);
return params;
}
};
queue.add(jsonObjectRequest);
}
Here is the Django Login class-based view :
class Login(LoginView):
form_class = AuthenticationForm
template_name = 'users/login.html'
def post(self, request, *args, **kwargs):
if request.META.get('HTTP_ACCEPT') == 'application/json':
form = self.get_form()
if not form.is_valid():
print(form.errors.as_text())
return JsonResponse({'error': form.errors.as_text()}, status=400)
return super().post(request, *args, **kwargs)
We think that we are missing something in the Volley request headers or somewhere else. Can you guys help us ?
EDIT:
Here are our CSRF settings:
CSRF_COOKIE_AGE = None
CSRF_COOKIE_DOMAIN = '.ourapi.com'
CSRF_COOKIE_HTTPONLY = True
CSRF_COOKIE_SECURE = True
CSRF_USE_SESSIONS = True
Self answer, here !
I kind of misunderstood how CSRF and session cookies work in Django. In the process_view function of the CsrfViewMiddleware, the reason message 403 forbidden - CSRF cookie not set is triggered when the CSRF token is None (see here). The csrf_token value comes from the _get_token(request) method from the same class, but it seems that the value returned is None (see here).
All we had to do was simply sending the session cookie to the server with the CSRF token in the header as X-CSRFToken !
So I'm running into a problem when calling rest-auth/user/.
I am able to login and obtain the key from said login, but from that I'm not sure how to use it in regards to rest-auth/user/. I've tried using it with GET in volley, as well as POSTing it in volley. But everytime I try to do so, I get a 403 back saying credentials were not provided. I've also tried saving the token to Android's SharedPreferences.
I'm not sure what could be wrong or how to fix this problem, so any help would be appreciated.
My code looks like this:
getUserQueue = Volley.newRequestQueue(this);
JSONObject jsObj = new JSONObject();
try {
jsObj.put("token", token);
} catch (JSONException e) {
e.printStackTrace();
}
JsonObjectRequest jsObjRequest = new JsonObjectRequest
(Request.Method.GET, "http://hurst.pythonanywhere.com/supportal/rest-auth/user/", jsObj, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
result = response.getString("username");
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
});
// add the request object to the queue to be executed
getUserQueue.add(jsObjRequest);
From comments it is clear that you are using different authentication schemes for rest, will explain these,
SessionAuthentication
For authenticating session authentication you need persistant cookie implementation in android volley, check here for this, where Set-Cookie header is parsed from server api response & send over next requests.
BasicAuthentication
In Basic authentication scheme, username & password are send over every request ( after login ?), with Authorization header value ( Base64 encoded).To check how to implement this check here
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> params = new HashMap<String, String>();
String creds = String.format("%s:%s","USERNAME","PASSWORD");
String auth = "Basic " + Base64.encodeToString(creds.getBytes(), Base64.DEFAULT);
params.put("Authorization", auth);
return params;
}
JSONWebTokenAuthentication
In JWT authentication scheme, after successful login you will get a JWT token, you need to send this token in every request that need user authorization, for this to work, set Authorization header value with JWT token_after_login;Only difference with Basic authentication is how header is send.
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> params = new HashMap<String, String>();
String auth = "JWT " + token // token you will get after successful login
params.put("Authorization", auth);
return params;
}
I'm trying to make a GET request in an Android application using the Volley library. This GET request is to verify account credentials using Basic HTTP Authentication. I verified the URL with credentials works in my browser as it returns successful XML. The format is:
http://username:password#myanimelist.net/api/account/verify_credentials.xml
where username and password obviously represent real user credentials. Volley throws this error:
BasicNetwork.performRequest: Unexpected response code 401 for http://username:password#myanimelist.net/api/account/verify_credentials.xml
Here is my Android code that handles the request:
private static final String HTTP_PRE = "http://";
private static final String VERIFY_CREDENTIALS = "myanimelist.net/api/account/verify_credentials.xml";
public void verifyCredentials(String username, String password) {
RequestQueue queue = Volley.newRequestQueue(context);
String url = HTTP_PRE + username + ":" + password + "#" + VERIFY_CREDENTIALS;
StringRequest stringRequest = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
processResponse(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// handle error
Log.d(TAG, "error: " + error.getMessage());
}
});
queue.add(stringRequest);
}
This solution to override the getHeaders() method provided the same result:
How does one use Basic Authentication with Volley on Android?.
Here is my implementation of that solution:
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = super.getHeaders();
if (params == null){
params = new HashMap<>();
}
String creds = String.format("%s:%s", username, password);
params.put("Authorization", creds);
return params;
}
Which returned this error without the credentials built directly into the URL:
BasicNetwork.performRequest: Unexpected response code 401 for http://myanimelist.net/api/account/verify_credentials.xml
If someone could provide advice, I'd really appreciate it. This is my first time using Basic HTTP Authentication so I could be missing something obvious.
I solved this problem following the first answer here: Http Authentication in android using volley library. I had tried something similar and many other solutions but this was the only one that worked.
Basic Authentication uses BASE64 encoding. You're missing
String creds = String.format("%s:%s", username, password);
creds = Base64.encodeToString(creds.getBytes(), Base64.NO_WRAP);
The Authorization HTTP header requires to indicate the method used (Basic|Digest). At last you headers should look like this:
GET http://username:password#myanimelist.net/api/account/verify_credentials.xml
Accept: text/xml,text/plain
...
Authorization: Basic XXXXXXXXXXXXXX==
I want to use Http Digest with Volley. So far I have used the following code:
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> params = new HashMap<String, String>();
String creds = String.format("%s:%s","admin","mypass");
String auth = "Digest " + Base64.encodeToString(creds.getBytes(), Base64.NO_WRAP);
params.put("Authorization", "Digest " +auth);
return params;
}
So far I get a response from server as wrong credentials which means that authentication is working but only wrong credentials are getting passed. But my credentials are right.
You use Base64 encoding which is fine for Basic Auth but this is not how digest works.
Digest & Basic Auth specs can be found here: https://www.rfc-editor.org/rfc/rfc2617
the newer Digest Specs can be found here: https://www.rfc-editor.org/rfc/rfc7616
And nice extra explanation on wikipedia here: https://en.wikipedia.org/wiki/Digest_access_authentication
For Volley implementation of Digest Auth you can use:
http://www.java2s.com/Open-Source/Android_Free_Code/Framework/platform/com_gm_android_volleyHttpDigestStack_java.htm
You will just need to pass this http stack when you create your Network which you then use to create your RequestQueue:
RequestQueue requestQueue;
requestQueue = new RequestQueue(
new DiskBasedCache(rootDir),
new BasicNetwork(new HttpDigestStack())
);
requestQueue.start();