Unable to set cookies with Spring Boot on the serve side - javascript

I'm using Spring Boot on server side. When I'm adding cookie to response it adds Set-cookie header with right value but when browser receives response it displays that header but won't set the cookie. Also Postman stores all cookies fine.
Spring
public ResponseEntity<?> authenticate(#RequestBody AuthenticationRequest request, HttpServletResponse response) throws Exception {
Cookie cookie = new Cookie("token", "COOKIE_VALUE");
cookie.setHttpOnly(true);
cookie.setSecure(false);
response.addCookie(cookie);
return ResponseEntity.ok("Connection succeeded");
}
JSfetch (from React app from different port)
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
var raw = JSON.stringify({"username":"TestUser","password":"pwd"});
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
fetch("http://IP_ADDRESS:8080/authenticate", requestOptions)
Chrome's seeing cookie in the headers
But it won't add it to the storage
So does Firefox. What did I miss? Is there a solution to this?
I'm using my internet ip address in fetch with port 8080 - not localhost. But localhost didn't do the trick either.
UPD. It seems working though when the ports are the same. I tried to return jsp page instead and that page executes the fech statement and it has stored the cookie. So solution to this is probably to compile react app and put it on the server. Anyway how to deal with cookies when the ports are not the same?

Cookies are accessibles only if the JS is provided from the same origin (host:port).
You may use URL rewriting approach to use the same origin for your assets and API. You may look at devServer if your are using Webpack.
Consider LocalStorage that offer more modern approach to deal with it as well.
Regards.

Chrome has changed its recent policies not to support localhost or development cookies, so you have to work around and play it with HTTP cookie
ResponseCookie resCookie = ResponseCookie.from(cookieName, cookieValue)
.httpOnly(true)
.sameSite("None")
.secure(true)
.path("/")
.maxAge(Math.toIntExact(timeOfExpire))
.build();
response.addHeader("Set-Cookie", resCookie.toString());
This thing works for me but, make sure it only works for https (not HTTP) and this thing is a makeover for development purposes only, once if you host your server chrome allows response cookies else it just blocks all kinds of HTTP cookies.

Ok, changing this in spring boot
#CrossOrigin
to this
#CrossOrigin(origins = "http://MY_IP_ADDRESS", allowCredentials = "true")
saved my day. I still don't get it why it didn't work when I set the headers manually as follows in the post mapping method
response.addHeader("Access-Control-Allow-Credentials", "true");
response.addHeader("Access-Control-Allow-Origin", "http://MY_IP_ADDRESS");

Related

Replicating a browser https request in javascript

I'm trying to hit an API that needs a cookie to return data.
If i hit the url directly in the browser I get the data i want. The protocol is https.
However, whenever I try to fetch the data using window.fetch I run into CORS errors. I think this is happening because I cant get the cookie in the client request, which is causing the server to redirect to an auth server that is not sending back a CORS header.
I have tried using { credentials: 'include' } to no avail.
I was assuming that because the cookie exists in the browser it will be part of the request.
Any fundamental knowledge I'm missing here?
hmm this is a bit weird, by default fetch uses { credentials: 'same-origin' } which will allow cookies to be sent { credentials: 'include' } also will send cookies even if its not the same origin and that's the only difference.
what i am thinking here is that either somewhere in your code the cookie gets deleted somehow or your request is firing before the cookie is set, or the server doesn't allow CORS or it doesn't allow the OPTIONS method if its a different origin.

Cookie is not set automatically by the browser

I am trying to login by calling an API via a POST HTTP request.
post(
postLogin(email),
JSON.stringify({password: passwd}),
{ headers: { "Content-Type":"application/json" },
credentials: 'include' // i also tried with 'same-origin'
}
)
I am using Redux and React. In the API response, I receive the Set-Cookie header (I can see it in browser dev tools), but for some reason I cannot access it in my code and the browser doesn't set the cookie. I'm using Chrome Version 63.0.3239.84. It is a cross-origin request, so I have the following CORS headers set so I think it is not from here.
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:*
Access-Control-Allow-Methods:GET, OPTIONS
Access-Control-Allow-Origin:*
Do you have any suggestions or someone has this issue too? Thank you!
I have finally fixed it. The problem was that my application and the API were not in the same domain. I configure a local NGINX in order to have them under the same domain and it works fine.

No 'Access-Control-Allow-Origin' header present AngularJS

I am trying to build a quick demo site that I do not have control over the server I am trying to connect to. Here is the code that I am using to build it with AngularJS. I am running the file through a simple Python HTTP Server and viewing it at localhost:8000.
var retrieveAppliances = function () {
console.log('Attempting to retrieve appliance list.');
var requestUrl = '****';
$http({
method: 'GET',
url: requestUrl,
})
.then(function (response) {
console.log(response);
});
};
retrieveAppliances();
I have read multiple places to try switching the method to JSONP but doing so resulted in a parsing error.
While I have considered trying to build a server.js file and running NodeJS with it, I am unsuccessful in learning the basics of making an AJAX request and proxying that to my app.js.
I will greatly appreciate any help that someone may be able to give me, with clear and easy to follow steps.
If you're running an Ajax call to a different origin (e.g. different host, port or protocol) and the server at that origin does not have support for cross origin requests, then you cannot fix that from your client. There is nothing you can do from the client.
If the server supported JSONP, you could use that, but that also requires specific server support.
The only solutions from a browser web page are:
CORS support on the target server.
JSONP (also requires support on the target server).
Set up your own server that you do have access to (either on your existing page domain or with CORS) and then have that server get the file/data for you and proxy it back to you. You can either write your own proxy or deploy a pre-built proxy.
Find some existing third party proxy service that you can use.
If you're interested in making your own node.js proxy, you can see a simple example here: How to create a simple http proxy in node.js?.

Angular 2 sending header for authorization

I am trying to get data from API that has oAuth authentication.
What I am doing is sending the auth request from server1 (where my Angular app is) to server2 and I get the access token. Then I put the token in the JS variable and I am trying to access an API endpoint.
My js code looks like this:
let headers = new Headers({ 'Authorization': "Bearer " + oAuthAccessToken });
let options = new RequestOptions({ headers: headers });
let url = "http://example.com/api/getSomething"
this.http.post(url, body, options)
.subscribe( res => {
console.log(res.json())
});
The problem is that I am always getting "401 Unathorized". When I inspect the request in the Network tab of Chrome Dev Tools I see two strange things - first the request method is OPTIONS not POST and the header Authorization is missing.
Any idea what I might be doing wrong ? Why is the header not set ?
Edit:
The problem was that Angular sends OPTIONS request before the POST and my app firewall was expecting Authorization header to be always present. This header is not present in the OPTIONS request so I was getting Unauthorized. I changed my server app to send proper headers on OPTIONS request and now everything is fine.
Thanks for the help.
I think the browser try to discover which http methods are allowed, so the first request is an request with the OPTIONS method. Usually the backend service answers with Access-Control-Allow-Methods inside the header. Afterwards the browser sends the real request.
I think that you need to allow CORS, then it should work as expected
As you are dealing with cross-domain requests, Chrome is preflighting the request to look for CORS headers. If the request is acceptable, it will then send the real request. so the option request is just to check is the server support CORS.
From : https://stackoverflow.com/a/21783145/3279156
Content-Type should be like below:
let header= new Headers({'Content-type':'application/x-www-form-urlencode'});
header.append('Authorization',"Bearer " + token);
let opt= new RequestOptions({headers:header});

Set-Cookie in HTTP header is ignored with AngularJS

I'm working on an application based on AngularJS on client side and Java for my API (Tomcat + Jersey for WS) on server side.
Some path of my API are restricted, if the user doesn't have a session the response status returned is 401. On the client side, 401 http status are intercepted to redirect the user to the login page.
Once the user is authenticated, I create a session on the server side httpRequest.getSession(true);
and the response send to the client does have the Set-cookie instruction in its header :
Set-Cookie:JSESSIONID=XXXXXXXXXXXXXXXXXXXXX; Domain=localhost; Path=/api/; HttpOnly
The problem is that the cookie is never put on the client side. When I inspect cookie for localhost domain it's empty, so the next requests don't have this cookie in their header and client side still couldn't access to the restricted path of my API.
The client and the server are on the same domain but they don't have the same path and the same port number :
Client : http://localhost:8000/app/index.html
Server : http://localhost:8080/api/restricted/
Additional info : CORS is enabled on the both side :
"Access-Control-Allow-Methods", "GET, POST, OPTIONS"
"Access-Control-Allow-Origin", "*"
"Access-Control-Allow-Credentials", true
Any idea for making the Set-cookie works properly ?
Is it an AngularJS related issue ?
I found an issue in AngularJS that help me to move forward.
It seems that "Access-Control-Allow-Credentials" : true was not set on the client side.
Instruction $httpProvider.defaults.withCredentials = true was ignored.
I replace $resource call by a simple $http call with {withCredentials:true} in the config parameter.
I've managed to solve an issue very similar to yours.
My Play! backend tried to set a session Cookie which I could not catch in Angular or store via browser.
Actually the solution involved a bit of this and a bit of that.
Assuming you've solved the initial issue, which can be solved only by adding a specific domain to the Access-Control-Allow-Origin and removing the wildcard, the next steps are:
You have to remove the HTTP-Only from the Set-Cookie header, otherwise you will never be able to receive a cookie "generated" by your angular code
This setup will already work in Firefox, though not in Chrome
To make it work for Chrome too, you need to:
a) send a different domain from localhost in the cookie, using the domain your WS are "hosted". You can even use wildcards like .domain.com instead of ws.domain.com
b) then you'll need to make a call to the domain you specified in the cookie, otherwise Chrome won't store your cookie
[optional] I would remove that /api path in favor of a /
And that should to the trick.
Hope to have been of some help
In your post request on the client side, make sure to add the following:
For jquery ajax requests:
$.ajax({
url: "http://yoururlgoeshere",
type: "post",
data: "somedata",
xhrFields: {
withCredentials: true
}
});
With Angular's $http service :
$http.post("http://yoururlgoeshere", "somedata", {
withCredentials: true
});
You need work on both the server and client side.
Client
Set $http config withCredentials to true in one of the following ways:
Per request
var config = {withCredentials: true};
$http.post(url, config);
For all requests
angular.module("your_module_name").config(['$httpProvider',
function($httpProvider) {
$httpProvider.interceptors.push(['$q',
function($q) {
return {
request: function(config) {
config.withCredentials = true;
return config;
}
};
}
]);
}
]);
Server
Set the response header Access-Control-Allow-Credentials to true.
The addition HttpOnly means that the browser should not let plugins and JavaScript see the cookie. This is a recent convention for securer browsing. Should be used for J_SESSIONID but maybe not here.
Just solved a problem like this.
I was doing this and not working...:
$cookies.put('JSESSIONID', response.data);
Cookies are saved in the browser, but when I sent a new request, all the cookies were sent exept mine. (my cookie is JSESSIONID)
then i look in the chrome inspector and i found this:
THE PROBLEM IS THAT WAS NOT THE CORRECT PATH!!!
then I tried this and my cookies were sent. yay! :
$cookies.put('JSESSIONID', response.data, {'path':'/'});
I do not know if this is your case, but this worked for me.
regards!

Categories

Resources