HTTP headers for AJAX/ denied authentication? - javascript

I would like to know what is best practice with AJAX authentication process.
When authentication is valid I return HTTP header 200 with response "ok"
What HTTP header do I need to send from server if authentication is NOT valid.
Do I need to set HTTP header to code 500 (ERROR)
or leave it on 200 and implement logic which checks response variable?

You don't want to send a 500 error, since that implies an unexpected server-side error that is not caused by the user.
You'll want to read up on the rfc spec for status codes:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
4XX status codes are for client errors, which is where you'll want to be looking. In your case, you could use 401 if authentication failed, and 403 if that user is not allowed to view the resource.

How about returning HTTP401?
You can handle in in AJAX error handler and redirect the whole page to login screen, if it's your requirement.
$.ajax({
statusCode: {
401: function() {
alert("User not logged in");
}
}
});

Related

No response in browser but getting 401 in postman

I'm calling an API with a JWT. When the token expires or when I send an invalid token, I get a 401 in postman, but when I do it in browser, I get a "Network error". And it works when the token is valid.
I use axios to call the API and the error.response is undefined. When I stringify the error object in catch I see something like this:
{
message : 'Network Error',
name : 'Error',
stack : 'Error: Network Error\n at createError (webpack-internal:///./node_modules/axios/lib/core/createError.js:16:15)\n at XMLHttpRequest.handleError (webpack-internal:///./node_modules/axios/lib/adapters/xhr.js:83:14)'
}
I get 200 in the OPTIONS request, but in the main GET request I get the above error. This is probably because of CORS. But I don't know what to change. The Access-Control-Allow-Origins is *.
I don't understand why I don't get the 401 in browser. Can someone please tell me how to fix this?
Screenshot of network tab:
Options:
Your server is probably not setting Access-Control-Allow-Credentials to true to allow origin access to authorized users.
I don't think you actually have a bug. It's just that axios throws on all 4xx responses by default. Try the following:
axios.get("...", { validateStatus(status) { return true; } })
To fix it up, try add other Cross-Origin Resource Sharing (CORS) headers. Check them at
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

Is it correct to send a 401 error before the user tries to login?

In a REST Api i'm developing, someone told me to directly send a request to the "get current user (/me)" url without checking if there is a session token and simply redirect him to the login page when receiving a 401 response.
Is this correct? I thought that the browser should not receive a 4xx response unless the user does something he is not supposed to.
I thought that the browser should not receive a 4xx response unless the user does something he is not supposed to.
That's more restrictive than described by the standard, see RFC 7231
The 4xx (Client Error) class of status code indicates that the client seems to have erred.
The server can't distinguish errors by the user from errors by the user agent - there's just a single HTTP request, that has to be judged of itself.
Another way of expressing the same idea: REST's uniform interface constraint implies that clients with human users should be getting the same self descriptive responses as clients without human users.
We do exactly this. We basically send a HTTP request, and if it comes back as a 401, we know we need to log the user in.
We also send a Link header to tell the client where the user should go for log in:
HTTP/1.1 401 Unauthorized
Link: </login>; rel="authenticate"

Express: Is it necessary to respond with status 200?

Is it necessary to respond with a status 200 code or is it the default behavior?
response.json({
status: 'OK',
});
vs.
response
.status(200)
.json({
status: 'OK',
});
When I hit the route in my browser, I get a 200 response in both cases
By now, I only use status code for other responses than 200 (e.g. 404, 500)
The Express response object wraps the underlying Node.js response object. In Node.js, if you don't set a response, it will always be 200. Express operates the same way for most requests. It will also automatically handle setting some error response codes for you depending on if and where an error was thrown.
Further, Express will set the response code for you on certain types of routes, for example, if you've defined a redirect, it will automatically set the 302 code for you.

Unable to call web-service from angularjs app

Unable to call post webservice from my application. following is the code.
var postLogin = "http://0.0.0.0:000/ddd/v1/login";
var loginvalue = {"email":"some#mail.com","password":"cbsjc6dw3bgjyfdgdKHGGDF="};
var config = {
headers: {
'Content-Type': 'application/json'
}
}
$http.post(postLogin ,loginvalue,config ).success( function(response) {
alert("response "+ response)
$scope.defer.resolve(response);
}).error(function(error){
alert("dddddd" + JSON.stringify(error));
})
If i write this code then it is returning as 400 error but if i use the postman application of google then i am getting the response without any error. So i am in confusion that whatever the code i have written is right or wrong. Hence i need to solve this issue.
Please go through the above image.
This usually happens when Client and Server are on different domains. The POST requests done by the client are first verified with a OPTIONS pre-flight check, to see if a POST would be possible. Sometimes, servers are configured to not allow OPTIONS request method. This will be the outcome of a pre-flight OPTIONS check, in such a case.
There is more information here - Why is an OPTIONS request sent and can I disable it?
Other resources for understanding the concept and helping us to configure the Response headers from the Server-side application are here:
https://medium.com/#praveen.beatle/avoiding-pre-flight-options-calls-on-cors-requests-baba9692c21a
https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
At the end of the day, if the Server is NOT configured to handle Cross-site requests, nothing can be done from the client-side.
Also, there are cases where the server does allow cross-site request, processes and send the response back to client, without the Access-Control-Allow-Origin header or with the Access-Control-Allow-Origin header, but not the same as the request origin or a wildcard "*". In such cases, browser stops processing the response, even when the call turns out to be in HTTP 200 OK Status.
Below is one such example, that I recently encountered while integrating with an external application.

How to prevent browser to invoke basic auth popup and handle 401 error using Jquery?

I need to send authorization request using basic auth. I have successfully implemented this using jquery. However when I get 401 error basic auth browser popup is opened and jquery ajax error callback is not called.
I was facing this issue recently, too. Since you can't change the browser's default behavior of showing the popup in case of a 401 (basic or digest authentication), there are two ways to fix this:
Change the server response to not return a 401. Return a 200 code instead and handle this in your jQuery client.
Change the method that you're using for authorization to a custom value in your header. Browsers will display the popup for Basic and Digest. You have to change this on both the client and the server.
headers : {
"Authorization" : "BasicCustom"
}
Please also take a look at this for an example of using jQuery with Basic Auth.
Return a generic 400 status code, and then process that client-side.
Or you can keep the 401, and not return the WWW-Authenticate header, which is really what the browser is responding to with the authentication popup. If the WWW-Authenticate header is missing, then the browser won't prompt for credentials.
You can suppress basic auth popup with request url looking like this:
https://username:password#example.com/admin/...
If you get 401 error (wrong username or password) it will be correctly handled with jquery error callback. It can cause some security issues (in case of http protocol instead of https), but it's works.
UPD: This solution support will be removed in Chrome 59
As others have pointed out, the only way to change the browser's behavior is to make sure the response either does not contain a 401 status code or if it does, not include the WWW-Authenticate: Basic header. Since changing the status code is not very semantic and undesirable, a good approach is to remove the WWW-Authenticate header. If you can't or don't want to modify your web server application, you can always serve or proxy it through Apache (if you are not using Apache already).
Here is a configuration for Apache to rewrite the response to remove the WWW-Authenticate header IFF the request contains contains the header X-Requested-With: XMLHttpRequest (which is set by default by major Javascript frameworks such as JQuery/AngularJS, etc...) AND the response contains the header WWW-Authenticate: Basic.
Tested on Apache 2.4 (not sure if it works with 2.2).
This relies on the mod_headers module being installed.
(On Debian/Ubuntu, sudo a2enmod headers and restart Apache)
<Location />
# Make sure that if it is an XHR request,
# we don't send back basic authentication header.
# This is to prevent the browser from displaying a basic auth login dialog.
Header unset WWW-Authenticate "expr=req('X-Requested-With') == 'XMLHttpRequest' && resp('WWW-Authenticate') =~ /^Basic/"
</Location>
Use X-Requested-With: XMLHttpRequest with your request header.
So the response header will not contain WWW-Authenticate:Basic.
beforeSend: function (xhr) {
xhr.setRequestHeader('Authorization', ("Basic "
.concat(btoa(key))));
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
},
If you're using an IIS Server, you could setup IIS URL Rewriting (v2) to rewrite the WWW-Authentication header to None on the requested URL.
Guide here.
The value you want to change is response_www_authenticate.
If you need more info, add a comment and I'll post the web.config file.
If WWW-Authenticate header is removed, then you wont get the caching of credentials and wont get back the Authorization header in request. That means now you will have to enter the credentials for every new request you generate.
Haven't explored the why or scope of fix, but I found if I'm doing a fetch request, and add the header x-requested-with: 'XMLHttpRequest', I no longer get the popup auth box in Chrome and don't need a server change. It's talking to the node http library. Looks like WWW-Authenticate header comes back from the server, but Chrome handles it differently. Probably spec'd.
Example:
fetch(url, {
headers: {
Authorization: `Basic ${auth}`,
'x-requested-with': 'XMLHttpRequest'
},
credentials: 'include'
})
Alternatively, if you can customize your server response, you could return a 403 Forbidden.
The browser will not open the authentication popup and the jquery callback will be called.
In Safari, you can use synchronous requests to avoid the browser to display the popup. Of course, synchronous requests should only be used in this case to check user credentials... You can use a such request before sending the actual request which may cause a bad user experience if the content (sent or received) is quite heavy.
var xmlhttp=new XMLHttpRequest;
xmlhttp.withCredentials=true;
xmlhttp.open("POST",<YOUR UR>,false,username,password);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
Make an /login url, than accept "user" and "password" parameters via GET and don't require basic auth. Here, use php, node, java, whatever and parse your passwd file and match parameters (user/pass) against it. If there is a match then redirect to http://user:pass#domain.com/ (this will set credential on your browser) if not, send 401 response (without WWW-Authenticate header).
From back side with Spring Boot I've used custom BasicAuthenticationEntryPoint:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().authorizeRequests()
...
.antMatchers(PUBLIC_AUTH).permitAll()
.and().httpBasic()
// https://www.baeldung.com/spring-security-basic-authentication
.authenticationEntryPoint(authBasicAuthenticationEntryPoint())
...
#Bean
public BasicAuthenticationEntryPoint authBasicAuthenticationEntryPoint() {
return new BasicAuthenticationEntryPoint() {
{
setRealmName("pirsApp");
}
#Override
public void commence
(HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx)
throws IOException, ServletException {
if (request.getRequestURI().equals(PUBLIC_AUTH)) {
response.sendError(HttpStatus.PRECONDITION_FAILED.value(), "Wrong credentials");
} else {
super.commence(request, response, authEx);
}
}
};
}

Categories

Resources