Communication between AngularJS and a Jersey Webservice which are on a different domain. Can't access correct session

angularjs, cors, java-ee-6, jersey, xmlhttprequest

Solution

Depending on the version of AngularJS you are using you might have to set it on each $http.

Since 1.2 you can do:

$http.get(url,{ withCredentials: true, ...})

From 1.1.1 you can globally configure it:

config(['$httpProvider', function($httpProvider) {
  $httpProvider.defaults.withCredentials = true;
}]).

If you're using an older version of Angular, try passing a config object to $http that specifies withCredentials. That should work in versions before 1.1:

$http({withCredentials: true, ...}).get(...)

See also mruelans answer and:

- https://github.com/angular/angular.js/pull/1209

- http://docs.angularjs.org/api/ng.$http

- https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS?redirectlocale=en-US&redirectslug=HTTP_access_control#section_5

Problem

Lately I've been playing around with AngularJS and Java EE 6. I've build an webservice with Jersey and deployed the project on Glassfish. Because I needed some kind of authentication and an OAuth implementation or an JDBCRealm seemed overkill I decided to just create a session if the user successfully logged in. ``` @POST @Path("/login") @Produces({MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_JSON}) public Response login(LoginDAO loginData, @Context HttpServletRequest req) { req.getSession().invalidate(); loginData.setPassword(PasswordGenerator.hash(loginData.getPassword())); User foundUser = database.login(loginData); if(foundUser == null) { return Response.status(Status.CONFLICT).build(); } req.getSession(true).setAttribute("username", foundUser.getUsername()); return Response.ok().build(); } @GET @Path("/ping") public Response ping(@Context HttpServletRequest req) { if(req.getSession().getAttribute("username") == null) { return Response.ok("no session with an username attribute has been set").build(); } return Response.ok(req.getSession(true).getAttribute("username")).build(); } ``` This seems to work alright, if I post to /login from Postman or from a basic jQuery webpage deployed on glassfish I do get the correct username back and a session has been placed. If I then send a GET request to /ping I do get the username back from which I logged in. I've an AngularJS application deployed on a node.js webserver which needed to login. Because this server is on another port its on another domain and I had to go through the pain of enabling cors. I did this by building a container response filter which sets the response headers. ``` public class CrossOriginResourceSharingFilter implements ContainerResponseFilter { @Override public ContainerResponse filter(ContainerRequest creq, ContainerResponse cresp) { cresp.getHttpHeaders().putSingle("Access-Control-Allow-Origin", "http://localhost:8000"); cresp.getHttpHeaders().putSingle("Access-Control-Allow-Credentials", "true"); cresp.getHttpHeaders().putSingle("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT"); cresp.getHttpHeaders().putSingle("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With"); return cresp; } } ``` This did made it possible for me to send different types of HTTP requests from AngularJS to Java EE 6 application deployed on glassfish. The problem is that when I send a POST request from AngularJS to the /login method, a session is created and I do get my username back. But when I send a GET request to the /ping method I get the "no session with an username attribute has been set" notice. I believe this has to do with cross domain prevention and that I've to set the withCredentials tag when I send a xhr request. I've been trying to do this in AngularJS but haven't found out how to do this. ``` function LoginCtrl($scope, $http) { $scope.login = function() { $http.post("glassfish:otherport/api/login", $scope.credentials). success(function(data) { console.log(data); }). error(function(data, error) { console.log(error); }); }; }; ``` And in another controller: ``` $scope.getUsername = function() { $http.get("glassfish:otherport/api/ping", {}). success(function(data) { $scope.username = data; }). error(function() { $scope.username = "error"; }) } ``` I've tried to set withCredentials is true ``` $http.defaults.withCredentials = true; ``` This however didn't solve my problem. I also tried to send it with every request in the config parameter but this didn't solve my problem either.

Original source