Unable to call web-service from angularjs app - javascript

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.

Related

CORS policy: No 'Access-Control-Allow-Origin' / 500 (Internal Server Error) Problem

Basically, I'm trying to get a username by id from Sequelize. The problem is that I am either stuck with a CORS problem or 500 Internal Server error depending on the response(status)
cors and 500
controller code
async getUserFromUserId (req, res) {
try {
// const user = await User.findByPk(req.body.id)
const id = req.body.id
const user = await User.findByPk(id)
res.send(user.username)
} catch (err) {
// or res.status(some random number).send() for CORS problem to appear
res.status(500).send({
error: 'an error has occured trying to fetch the users id'
})
}
},
client code
this.notifiedUser = (await UserService.getUserFromUserId({id: UserId})).data
I get a Status: 200 OK from postman though.
Postman Solution
Edit:
I have seen how the other Solution for the cors thingy, but the solutions does not specify as to why I get "undefined" results after resolving the cors problem.
So, CORS is actually really obnoxious in this regard, but there's a fairly straightforward way to fix this. It's a super useful security feature, though it is frustrating at best sometimes.
Your browser does what is called a Preflight Request, which is of the http verb OPTIONS. Your browser calls whatever route you want, but instead of what you asked it to do, it calls using OPTIONS first. Your server should accept all routes that the client can ask for with the OPTIONS method, and your server should respond with the following headers to be an externally available, cross-origin API.
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, ...
(note, you should not put the ... in, but you can put any HTTP verb in this list)
If you require your own headers (for auth purposes), you want to add this header for Client -> Server.
Access-Control-Allow-Headers: YourHeader, YourHeader2, YourHeader3
You want to add this one for Server -> Client
Access-Control-Expose-Headers: YourHeader,YourHeader3
Note that the OPTIONS call is an entirely separate call that you should handle as well as the GET method.
You've now told the browser what it is allowed to ask for, and what it can expect to get back from your API. If you don't respond to the OPTIONS request, the browser terminates the request, resulting in a CORS error.
I'm going to take a gander at a guess and assume you're likely using Express, which this answer describes how to set the headers on.
What do the headers mean, in English?
Access-Control-Allow-Origin
From where are clients allowed to access this resource (endpoint)? This can match partial domains with wildcards, or just a * to allow anywhere.
Access-Control-Allow-Methods
What HTTP methods are permissible on this route?
Access-Control-Expose-Headers
When I get a response from the server, what should I (the browser) expose to the client-side?
Access-Control-Allow-Headers
What am I as the client side allowed to send as headers?
Okay, so I figured out the problem.
In a way, I did not have to deal with any of the cors stuff because I believe that was not the main source of the problem.
So, instead of accessing my database data through "GET" and getting the data by doing this:
this.data = (Service.function(bodyValue)).data
I did "POST" to get the data, and accessed the data by simply doing this
const response = Service.function({
id: bodyValue
})
this.data = response.data
This accesses the data without having to get "secured" information from the database, but by accessing the data from the database by getting Observer object info from the database.
The Observer object looks as follows, which treats the user data as an object instead of pure data.
Compared to a data object, where each data {...} has user information.
I am not sure if I am using the correct words, but these are to the extent of my current understanding.
If your origin is from localhost, Chrome usually blocks any CORS request originating from this origin.
You can install this extension:
https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf?hl=en
Or you can disable the security when running chrome (add the flag):
--disable-web-security

Fetch api preflight request failing

I'm trying to make a GET request from a public API but cant seem to figure out why the cors preflight request keeps failing.
Response for preflight has invalid HTTP status code 404
I've done some searching around and it seems like this is something that needs to be changed server side, but I just want to make sure theres nothing I might be doing wrong. I'm using the following code:
try {
let guideData = await fetch(guideUrl, {
method: 'GET',
headers: {
Authorization: `Bearer ${json.access_token}`,
}
});
} catch(err) {
console.log("Failed to fetch data", err);
}
I've tested using Postman and Hurl, both seem to work fine, I just can't get it to work on my client.
When you are sending http request from Postman and Curl the two don't behave like a web browser would. In your case you are using a web browser running js. The code itself looks fine minus the fact that you need to encode your token to make the Authorization work when sent.
The issue definitely seems to be server side, the gist is when you send OPTIONS it will tell you whether or not it's a problem with you or server. Most likely than not from the response you posted, it is definitely CORS.

Cannot 'GET' mLab Data b/c of CORS

I can't execute the 'GET' request with the getTasks() function.
$(document).ready(function(){
getTasks();
});
const apiKey = 'xxxxxxx';
function getTasks(){
$.ajax({
type: 'GET',
url: 'https://api.mlab.com/api/1/databases/taskmanager/collections/tasks?apiKey='+apiKey,
contentType: 'application/json',
xhrFields: {
withCredentials: true
},
success: function(data){
console.log(data);
},
error: function(){
console.log('FAIL')
}
})
}
The error that I get is:
api.mlab.com/api/1/databases/taskmanager/collections/tasks?apiKey=xxxxxxx
Failed to load resource: the server responded with a status of 400
(Bad Request)​
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'null' is therefore not allowed access. The response
had HTTP status code 400.
I understand that Google-Chrome on Windows is CORS enabled, and will not (by default) allow communication with a different domain. I'm not sure what a preflight request is. Regardless, I tried to implement what I saw from Using CORS - HTML5 Rocks​ (from the CORS from jQuery section), but to no avail.
At a guess, the remote API simply does not respond to pre-flight requests for GET calls (because it shouldn't have to).
Your code is triggering a pre-flight request because it is non-simple. This is due to your adding a Content-type: application/json header. A request Content-type header is used to indicate the request payload format. As it is a GET, there is no payload.
Try this instead...
$.getJSON('https://api.mlab.com/api/1/databases/taskmanager/collections/tasks', {
apiKey: apiKey
}).done(function(data) {
console.log(data)
}).fail(function() {
console.log('FAIL')
})
CORS is there to protect you. If you want some more info on it, wikipedia has a good entry on it.
It appears the issue here is that you're trying to access your mongodb hosted by mlab directly from your web app. As you can see in your code, you're providing credentials/api keys to make that request.
My guess is that mlab's intent of not allowing CORS is to prevent you from doing this. You should never put your private API keys in html to be hosted on a web page, as it's easily accessible by reading source code. Then someone would have direct access to your mongodb.
Instead, you should create a server-side application (node, or... ** Whatever **) that exposes an api you control on the same domain (or a domain you give permission to via CORS).
As far as the "preflight" request, if you look in your chrome debugging tools, you should see an additional request go out with the "OPTIONS" method. This is the request that chrome (and most other http clients) send out first to a server hosted on a different domain. it's looking for the Access-Control-Allow-Origin header to find out whether it's allowed to make the request. Pretty interesting stuff if you ever have some time to dig into it.

jQuery, CORS, JSON (without padding) and authentication issues

I have two domains. I'm trying to access a JSON object from one domain through a page on another. I've read everything I could find regarding this issue, and still can't figure this out.
The domain serving the JSON has the following settings:
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "GET, OPTIONS"
Header set Access-Control-Allow-Headers "origin, authorization, accept"
From my other domain, I'm calling the following:
$.ajax({
type:'get',
beforeSend: function(xhr) {
var auth = // authentication;
xhr.setRequestHeader("Authorization", "Basic " + auth);
}
url:myUrl,
dataType:'json',
error: function(xhr, textStatus, errorThrown) { console.log(textStatus, errorThrown); }
})
I know that 'auth' is initialized properly (logged and checked). However, this does not work. In Firefox's Console, I get
Request URL: ...
Request Method:
OPTIONS
Status Code:
HTTP/1.1 401 Authorization Required
If I get rid of the beforeSend:... part, I see the following
Request Method:
GET
Status Code:
HTTP/1.1 401 Authorization Required
However, the domain serving JSON also can serve JSONP. I don't want to use this, mainly because the application will be running constantly on a dedicated browser, and I'm worried about this issue. More importantly, I would really like to know what is actually wrong with what I am doing. I know that for practical purposes there are various ways to overcome the JSONP memory leak (such as not using jQuery).
At any rate, when I did use JSONP, my code looked like this:
$.ajax({
url:newUrl,
dataType:'jsonp',
jsonp:'jsonp'
}).done(function(d){console.log(d)})
This gets the following
Request Method:
GET
Status Code:
HTTP/1.1 200 OK
after it prompts me with an alert box for a username and password.
Is there a fundamental difference in the way jQuery handles JSONP requests as opposed to JSON requests? And if so, how can I fix this?
Thanks.
Edit: Here's what I did find.
Basically, because I need authentication, the GET request is sending an Authorization header. However, this is not a "simple" header, and so the browser is sending a pre-flight request (the OPTIONS). This preflight request doesn't have any authentication, though, and so the server was rejecting it. The "solution" was to set the server to let OPTIONS request not require authentication, and report an HTTP status of 200 to it.
Reference: http://www.kinvey.com/blog/item/61-kinvey-adds-cross-origin-resource-sharing-cors
mail-archive[.com]/c-user#axis.apache.org/msg00790.html (not allowed to post more links)
Unfortunately, the "solution" is only working on Firefox and not Chrome. Chrome simply shows the request in red, but doesn't give me any more info on why it failed.
Edit 2: Fixed on Chrome: The server I was trying to get data from had a security certificate which was not trusted. The preflight request on Chrome failed because of this. Solution
superuser[.com]/questions/27268/how-do-i-disable-the-warning-chrome-gives-if-a-security-certificate-is-not-trust (not allowed to post more links)
Welp, now that I have enough rep a while later, I might as well answer this question and accept it.
When you attempt to send a GET json request to a server with headers, the browser first sends an OPTION request to make sure that you can access it. Unfortunately, this OPTION request cannot carry with it any authentication. This means that if you want to send a GET with auth, the server must allow an OPTION without auth. Once I did this, things started working.
Some examples available here may illustrate further how access control can be combined with CORS. Specifically the credentialed GET example. Access control requires that the request set the withCredentials flag to true on the XMLHttpRequest, and for the server handling the OPTIONS method to do two things:
Set Access-Control-Allow-Credentials: true
Not use a wildcard * in the Access-Control-Allow-Origin header. This has to be set to the origin exactly according to the MDN docs on HTTP access control (CORS).
Essentially, the thing processing the OPTIONS request needs to send back appropriate response headers so you can make that credentialed request.
In your question you stated that the service you are interacting with is returning Access-Control-Allow-Origin: *, which is not compatible with a credentialed cross-domain request. This needs to return the origin specifically.
The aforementioned MDN Http Access Control (CORS) documentation also links to the Server-Side Access Control documentation outlining how a server would potentially respond to various cross domain requests - including handling a cross domain credentialed POST request that requires you to send back the correct headers in response to the OPTIONS method. You can find that example here.
Why don't you try typing the URL you are fetching the JSON from into your browser and seeing what happens. It sounds like you literally just need to authenticate into this other website to access it.
If your site needs to work in other browsers like IE, you WILL need JSONP, by the way. The security won't allow the cross site request to work. The headers won't change that. I believe you will also need to add a security policy in your headers.

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