Volley throwing AuthFailureError (401) with Basic HTTP Authentication - android

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==

Related

How to properly format a POST API request with JSON?

I have teaching myself how to develop Android applications and I recently started using the Volley library for networking. I have been able to send basic requests with it. I was experimenting with Pocket's API to see if my app could fetch the items. Their documentation page mentions that I have to send a JSON request like so
POST /v3/oauth/request HTTP/1.1
Host: getpocket.com
Content-Type: application/json; charset=UTF-8
X-Accept: application/json
{"consumer_key":"1234-abcd1234abcd1234abcd1234",
"redirect_uri":"pocketapp1234:authorizationFinished"}
So I made a JSONObject in my app, added the key consumer_key with the value of my consumer key, then added in the key redirect_uri with its respective value. I sent this JSONObject as a request to the required URL as a POST request using Volley. The response code I received was 403, which is caused by a bad consumer key. I have double-checked the consumer key, so the problem lies with my request. How should I go about making the JSON request? Do I have to add the extra data, like Content-Type? If so, how?
If it is not too much, could you point me to a beginner-friendly resource on JSON, since I do not have much knowledge about web development?
Thank you.
What request methode do you use on your code? I am facing issue when using StringRequest method. Its work when use JsonObjectRequest method.
Here is how to create request besides on my experience.
Create Header Parameter
final Map<String, String> headers = new HashMap<String, String>();
headers.put("Content-Type", "application/json");
headers.put("X-Accept", "application/json");
headers.put("consumer_key", "your-consumer-key");
headers.put("redirect_uri", "https://kamus.nusagates.com");
Create JSONObject From headers
JSONObject obj = new JSONObject(headers);
Create JsonObjectRequest
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest("https://getpocket.com/v3/oauth/request", obj, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
//get all response data
Log.d("respon", String.valueOf(response));
try {
//get code from response
Log.d("respon code", response.getString("code"));
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
return headers;
}
#Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
String server = String.valueOf(response.headers);
Log.d("header", server);
return super.parseNetworkResponse(response);
}
};
Add the request to The Queue
RequestQueue queue = Volley.newRequestQueue(MainActivity.this);
queue.add(jsonObjectRequest);
May this could help you solve your problem.
Cheers

"403 CSRF cookie not set" when doing POST on Android, with CSRF_USE_SESSIONS = True (Django 1.11)

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 !

Django Rest Auth Token in Android Volley

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;
}
JSONWebTokenAuthenticatio‌​n
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;
}

BasicNetwork.performRequest: Unexpected response code 429 (android)

I m using Volley library for sending a request to server for Login to an app. it doesn't have any problem until couple of hours ago. but without any reason, i m getting this error "BasicNetwork.performRequest: Unexpected response code 429"
the code is this:
public void loginRequest(final String username, final String password) {
String URL = Misc.Server_Url() + "login";
final StringRequest sr = new StringRequest(Request.Method.POST, URL, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
JSONObject obj;
try {
obj = new JSONObject(response);
if (obj.getInt("success") == 1) {
startActivity(new Intent(ActivityLogin.this, ActivityArticles.class));
finish();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
}) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<>();
params.put("Content-Type", "application/x-www-form-urlencoded");
return params;
}
#Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("username", username.trim());
params.put("password", password);
return params;
}
};
RetryPolicy policy = new DefaultRetryPolicy(2 * 1000, 2, 2);
sr.setRetryPolicy(policy);
AppController.getInstance().addToRequestQueue(sr);
}
I have searched in Wikipedia for this error (429) and i find that it means : "The user has sent too many requests in a given amount of time"
from server side(php) for more security if from an ip get more than for example 60 request within 10 second it will block that ip for a while... and client get 429 error code. i m wondering how it will occure when i send a single request to server same as above code!!! and in policy i set the try to 2 times Not more than that. i dont know why when i send this request i get error 429. means you have send 60 request within limited period of time.
do you know how to solve this problem?
thanks in advance...
Yes, as you said, the 429 response code states so. However, the tricky part is that the server sends this response code for either
You have sent too many requests in a short duration
The server has received too many requests by many others during that time
If you read the RFC related to the response code, you'll see that the RFC does not state that the server must identify individual users and only send them the busy status: 429. It could be because others are sending too many requests and the server is sending a 429 response to all.
In addition, the RFC states that the server should respond with a 429 response and the server MAY send the following parameter in its response header.
Retry-After: 3600
This would mean you should retry after this timeout.
the reason was coz of caching system in server.
if we send new request each time, it works fine. but if our request use from caching strategy system in server... it occur 429 error number...

how to solve 401 error

I am trying to sent JSON format data (using Volley) from two EditText-views and a method that return unique Device ID to a URL from my Android application and I receive
"[8970] BasicNetwork.performRequest: Unexpected response code 401 for https://gorountsiallyetlasornall:5wxGq5UNlY6wdWmNAyYPVVrN#bulberadev.cloudant.com/notebook"
Here is My method:
private void doPost() {
final String url = "https://gorountsiallyetlasornall:5wxGq5UNlY6wdWmNAyYPVVrN#bulberadev.cloudant.com/notebook";
final String deviceId = getDeviceId(getApplicationContext());
try {
try {
JSONObject jsonObject = new JSONObject();
String title = editTitle.getText().toString();
String content = editContent.getText().toString();
jsonObject.put("title", title);
jsonObject.put("content", content);
jsonObject.put("deviceId", "<" + deviceId + ">");
} catch (JSONException e) {
e.printStackTrace();
}
requestQueue = Volley.newRequestQueue(this);
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST,
url, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
VolleyLog.v("Response:%n %s", response.toString(4));
} catch (JSONException e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// Log.e("VOLLEY", "ERROR");
}
});
requestQueue.add(jsonObjectRequest);
} catch (Exception e) {
e.printStackTrace();
}
}
it should be in a format :
{
"title":"Birth day",
"content":"Buy a gift for my mom!",
"deviceId":"<Device ID>"
}
A 401 is an Unauthorized error.
https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_Client_Error
This means that the user and password is not getting recognized. You're providing it by means of the URL, but this only works for the browser. If the service you're using accepts Basic HTTP authorization headers, this code will provide you the needed headers:
#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;
}
Original code from https://stackoverflow.com/a/18980454/3286819
Of course, username and password needs to be your own. In this case:
username: gorountsiallyetlasornall
password: 5wxGq5UNlY6wdWmNAyYPVVrN
URL: https://bulberadev.cloudant.com/notebook (notice I've removed the user and password)
Some more info: https://yakivmospan.wordpress.com/2014/04/04/volley-authorization/
Error 401 is an HTTP error for unauthorised. This is not a Volley or android related fault. In this case the URL you have provided
https://gorountsiallyetlasornall:5wxGq5UNlY6wdWmNAyYPVVrN#bulberadev.cloudant.com/notebook
Cannot be interpreted by Volley as a login either. This url is sometimes used by cURL and other tools to hardcode the username in password into the URI for Basic Authentication HTTP.
For this your username is gorountsiallyetlasornall and your password is 5wxGq5UNlY6wdWmNAyYPVVrN.
According to Basic Autentication, explained https://luckymarmot.com/paw/doc/HTTP_Basic_Auth
It needs to be converted to Base 64 then added to the header of the request.
I have converted your username and password into a Basic Auth base 64 encoded String for you bellow.
Z29yb3VudHNpYWxseWV0bGFzb3JuYWxsOjV3eEdxNVVObFk2d2RXbU5BeVlQVlZyTg==
Add this into your Volley header by extending a Volley request and overriding the function getHeaders to return a HashMap with the following key value pair.
"Authorization" , "Basic Z29yb3VudHNpYWxseWV0bGFzb3JuYWxsOjV3eEdxNVVObFk2d2RXbU5BeVlQVlZyTg"
Your request will now work.
Please let me know if you want a more detailed explanation.
PS. Hopefully the values you posted in your question is not your real username and password. If so, then don't do that in the future.

Categories

Resources