I'm developing an application, where there are a number of subdomains, for example api.*.com, which is responsible for REST request processing. I'm trying to set cookie for subdomains, using:
public class PlayerTokenUtils {
final private static int DEFAULT_AGE = (int) TimeUnit.DAYS.toSeconds(7);
private PlayerTokenUtils() {
throw new IllegalAccessError();
}
public static PlayerToken updateResponse(String token, HttpServletResponse response) {
Cookie cookie = new Cookie("player", token);
cookie.setPath("/");
cookie.setHttpOnly(true);
cookie.setDomain(".mavarazy.com");
cookie.setMaxAge(DEFAULT_AGE);
response.addCookie(cookie);
return token;
}
}
I see in Firebug, that everything works fine for registration Request (http://api.mavarazy.com:3333/registration/base/signin) & Response contains:
Set-Cookie player=rHIHtISWzw; Domain=.mavarazy.com; Expires=Sun, 02-Nov-2014 07:44:54 GMT; Path=/; HttpOnly
But further requests to the server with api.mavarazy.com do not contain player Cookie, returned from Set.
The environment for testing, that I'm using:
I modified my /etc/hosts
127.0.0.1 mavarazy.com
127.0.0.1 api.mavarazy.com
My REST server is running Tomcat.
All REST requests for the browser go through Node.js proxy.
What am I doing wrong? What is the right format for player Cookie in this case?
I've found an issue with jQuery configuration in my Backbone application.
Adding
$.ajaxSetup {
...
xhrFields: {
withCredentials: true
}
...
}
Solved the problem.
Related
I'm trying to set cookie in client browser while redirecting from my Spring rest api controller to app home page (hosted somewhere else) by specifying URI of home page.
But it seems cookie coming in response headers but not getting set in cookie database.
and here are the values of domain and path;
domain = localhost
path = /
isSecure = false/true based on env.
I've tried lot of things to make it work, few of them are below;
domain = localhost:8080 [ as my ui code running on 8080 port ]
domain = < ip >:8080
domain = xyz.com [ i've mention an entry in my host file with 127.0.0.1:8080 xyz.com
Any one pls help, its been stuck quite a while.
#RequestMapping(value = "/login", method = RequestMethod.GET)
public ResponseEntity<?> ssoLoginAndFetchUserInfo(#RequestParam(value = "code", required = true) String code,
#RequestParam(value = "state", required = true) String state, HttpServletResponse response) {
try {
normalLog.info("sso/login api invoked with code {} and state {}", code, state);
final SSOUserInfoHostInfoWrapper info = ssoServices.ssoFetchUserInformation(code, state);
normalLog.info("info fetched {}", info);
response.addCookie(CommonUtil.createCookie(SSOContants.UserInfoConstants.IDENTITY_TOKEN,
info.getUserInfo().getTokenInfo().getId_token(), info.getHostInfo().getHostname(),
info.getUserInfo().getTokenInfo().getExpires_in(), IDENTITY_COOKIE_NAME, "/",
info.getHostInfo().isSecure()));
response.addCookie(
CommonUtil.createCookie(SSOContants.UserInfoConstants.USER_NAME, info.getUserInfo().getUserName(),
info.getHostInfo().getHostname(), info.getUserInfo().getTokenInfo().getExpires_in(),
USERNAME_COOKIE_NAME, "/", info.getHostInfo().isSecure()));
response.addCookie(
CommonUtil.createCookie(SSOContants.UserInfoConstants.USER_ID, info.getUserInfo().getUserId(),
info.getHostInfo().getHostname(), info.getUserInfo().getTokenInfo().getExpires_in(),
USERNAME_COOKIE_ID, "/", info.getHostInfo().isSecure()));
response.addCookie(
CommonUtil.createCookie("authentication_token", "sdfsdfsdf",
info.getHostInfo().getHostname(), info.getUserInfo().getTokenInfo().getExpires_in(),
"authentication_token", "/", info.getHostInfo().isSecure()));
// Redirect to app login page
response.setHeader("Location", info.getHostInfo().getAppHomePageURI());
return new ResponseEntity<>(HttpStatus.FOUND);
} catch (Exception e) {
return super.returnSpringError(e);
}
}
Utility method
public static Cookie createCookie(final String name, final String value, final String hostname, final int expiresIn,
final String comment, final String validToPath, final boolean isSecure) {
Cookie c = new Cookie(name, value);
c.setPath(validToPath);
c.setDomain(hostname);
c.setVersion(1);
c.setComment(comment);
c.setMaxAge(expiresIn);
c.setSecure(isSecure);
return c;
}
Few screenshots for what is heapping ;
The issue is fixed. From the day one i doubt its all due to "domain".
Don't know yet why putting "localhost" in domain does not working, probably DNS not getting resolved.
Here how i resolved it;
I made an entry in /etc/hosts file with below entry
127.0.0.1 xx.yy.zz-r.com
And then use domain as ".zz-r.com" and access all the ui page via
xx.yy.zz-r.com:8080/----------
and it worked.
Encountered same problem. My case was to pass redirect from backend to frontend with attached cookies. Within one main domain. There's no success with code
cookie.path = "/"
cookie.domain = "domain.com"
cookie.maxAge = 60
cookie.isHttpOnly = false
response.addCookie(cookie)
But everything run when I changed to
response.setHeader("Set-Cookie", "customCookie=value; Path=/; Max-Age=60; Domain=domain.com")
We are a team working on azure web services. What we want to achieve is having a JavaScript frontend which can communicate with our Java API App hosted in Azure.
We are using Active Directory for Authentication. And we have configured CORS within the Azure portal.
We have created a Java backend with Swagger Editor as it is described in this article. We have just advanced this example to support our own data model. So also the ApiOriginFilter class is still unchanged:
#javax.annotation.Generated(value = "class io.swagger.codegen.languages.JavaJerseyServerCodegen", date = "2016-11-08T15:40:34.550Z")
public class ApiOriginFilter implements javax.servlet.Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
res.addHeader("Access-Control-Allow-Origin", "*");
res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
res.addHeader("Access-Control-Allow-Headers", "Content-Type");
chain.doFilter(request, response);
}
public void destroy() {}
public void init(FilterConfig filterConfig) throws ServletException {}
}
Our frontend runs on a local PC. So furthermore, we added our origin "http://localhost:8888" in the CORS in the azure portal.
The frontend JavaScript code looks like this:
var app = angular.module('demo', ['AdalAngular', 'ngRoute'])
.controller('GetDataController', ['$scope', '$http', '$timeout', 'adalAuthenticationService', function($scope, $http, $timeout, adalAuthenticationService) {
$scope.loggedIn = "Logged out";
$scope.responseData = "No Data";
$scope.loading = "";
$scope.loginAdal = function(){
adalAuthenticationService.login();
}
$scope.getData = function(){
$http.defaults.useXDomain = true;
delete $http.defaults.headers.common['X-Requested-With'];
$scope.loading = "loading...";
var erg = $http.get('http://<our-backend>.azurewebsites.net/api/contacts')
.success(function (data, status, headers, config) {
console.log("SUCCESS");
$scope.loading = "Succeeded";
$scope.loggedIn = "LOGGED IN";
$scope.responseData = data;
})
.error(function (data, status, header, config) {
console.log("ERROR");
$scope.loading = "Error";
console.log("data: ", data);
});
}
}]);
app.config(['$locationProvider', 'adalAuthenticationServiceProvider', '$httpProvider', '$routeProvider', function($locationProvider, adalAuthenticationServiceProvider, $httpProvider, $routeProvider) {
$locationProvider.html5Mode({
enabled: true,
requireBase: false,
hashPrefix: '!'
});
var theEndpoints = {
"https://<our-backend>.azurewebsites.net/api/contacts": "f0f91e4a-ad53-4a4a-ac91-431dd152f386",
};
adalAuthenticationServiceProvider.init(
{
anonymousEndpoints: [],
instance: 'https://login.microsoftonline.com/',
tenant: "<our-tenant>.onmicrosoft.com",
clientId: "f6a7ea99-f13b-4673-90b8-ef0c5de9822f",
endpoints: theEndpoints
},
$httpProvider
);
}]);
But calling the backend from the frontend we get the following error after logging in into our tenant:
XMLHttpRequest cannot load https://[our-backend].azurewebsites.net/api/contacts. Redirect from 'https://[our-backend].azurewebsites.net/api/contacts' to
'https://login.windows.net/12141bed-36f0-4fc6-b70c-
43483f616eb7/oauth2/autho…
%2Fapi%2Fcontacts%23&nonce=7658b7c8155b4b278f2f1447d4b77e47_20161115124144'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin'
header is present on the requested resource. Origin 'null' is
therefore not allowed access.
In Chrome's developer console we see four requests to our contacts api. But all have the status code "302 Redirect". The first two entries contain the header "Access-Control-Allow-Origin:http://localhost:8888" but the other two entries do not contain this header.
EDIT: One of the first two entries is an XmlHttpRequest and one of the second entries is the same XmlHttpRequest but with https instead of http.
Based on this, we created a new filter in our backend to set the access-control-allow-origin field:
#Provider
public class CrossOriginFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext) throws IOException {
containerResponseContext.getHeaders().add("Access-Control-Allow-Origin", "*");
containerResponseContext.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization");
containerResponseContext.getHeaders().add("Access-Control-Allow-Credentials", "true");
containerResponseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
containerResponseContext.getHeaders().add("Access-Control-Max-Age", "1209600");
}
}
and deleted these three fields from ApiOriginFilter:
res.addHeader("Access-Control-Allow-Origin", "*");
res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
res.addHeader("Access-Control-Allow-Headers", "Content-Type");
Now if we run the backend locally, we see all these headers from the 2nd filter in Chrome's developer console.
But as soon as we deploy the backend to azure we loose this headers somehow and again have the same error when accessing the api from the frontend:
XMLHttpRequest cannot load https://[our-backend].azurewebsites.net/api/contacts. Redirect from 'https://[our-backend].azurewebsites.net/api/contacts' to
'https://login.windows.net/12141bed-36f0-4fc6-b70c-
43483f616eb7/oauth2/autho…
%2Fapi%2Fcontacts%23&nonce=7658b7c8155b4b278f2f1447d4b77e47_20161115124144'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin'
header is present on the requested resource. Origin 'null' is
therefore not allowed access.
EDIT: As I wrote there are two XmlHttpRequests made. And the second one is https. So If in the line
var erg = $http.get('http://<our.backend>.azurewebsites.net/api/contacts')
I change http to https we run into the error() callback function. And in the console we have the output:
data: User login is required
But as I wrote before. We are already logged in to our tenant. So what is going wrong?
var erg = $http.get('http://.azurewebsites.net/api/contacts')
Based on the code, you were request the service with HTTP protocol. I am able to reproduce this issue when I request the web API which protected by Azure AD with the HTTP. It seems that the Azure AD will redirect the HTTP to HTTPS.
After I change the service URL with HTTPS, the issue was fixed. Please let me know whether it is helpful.
I am trying to implent CSRF-protection with Spring Security (4.1.3) and Angular 2.0.1
There are many sources to related topics but I cannot find a clear instruction. Some of the statements even contradict each other.
I read about springs way of doing it (though the guide describes the Angular 1 way) Spring Security Guide with Angular
IT implies, that with
.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
everything should work "out of the box".
Even further the angular guide to security describes the CSRF-protection as build in.
In my enviroment the POST looks like this:
There is an OPTIONS-call which returns POST, 200 OK and a XSRF-TOKEN - cookie.
my http.post adds an authorization-header and adds the RequestOption "withCredentials"
It sends three cookies, two JSessionID's and an XSRF-TOKEN that is different from the one recieved by the OPTIONS-call, no XSRF-header.
Debugging into the Spring CsrfFilter shows me that it looks for a header named X-XSRF-TOKEN and compares it to the token in the cookie named XSRF-TOKEN.
Why doesn't Angular send the header, too?
How is this secure if Spring only checks the provided cookie and the provided header with no serverside action whatsoever?
There are some similar questions like this but the only answer with 0 upvotes seems (to me) plain wrong, as CSRF, from my understanding, has to have a serverside check for the cookie validation.
This question only provides information on how to change the cookie or header name as explained here
What am I missing here? I doubt there is a mistake in the Spring Security implementation but I cannot quite get it to work.
Any ideas?
POST-call
login(account: Account): Promise<Account> {
let headers = new Headers({ 'Content-Type': 'application/json' });
headers.append('X-TENANT-ID', '1');
headers.append('Authorization', 'Basic ' + btoa(account.userName + ':' + account.password));
let options = new RequestOptions({ headers: headers, withCredentials:true });
return this.http.post(this.loginUrl, account, options).toPromise()
.then(this.extractData)
.catch(this.handleError)
}
Spring Security Config
[] csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
The problem was the application path. Spring has the option to set the cookie-path in its pipeline but it is not released yet.
I had to write my own implementation for the CsrfTokenRepository which would accept a different cookie path.
Those are the relevant bits:
public final class CookieCsrfTokenRepository implements CsrfTokenRepository
private String cookiePath;
#Override
public void saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response) {
String tokenValue = token == null ? "" : token.getToken();
Cookie cookie = new Cookie(this.cookieName, tokenValue);
cookie.setSecure(request.isSecure());
// cookie.setPath(getCookiePath(request));
if (this.cookiePath != null && !this.cookiePath.isEmpty()) {
cookie.setPath(this.cookiePath);
} else {
cookie.setPath(getRequestContext(request));
}
if (token == null) {
cookie.setMaxAge(0);
} else {
cookie.setMaxAge(-1);
}
if (cookieHttpOnly && setHttpOnlyMethod != null) {
ReflectionUtils.invokeMethod(setHttpOnlyMethod, cookie, Boolean.TRUE);
}
response.addCookie(cookie);
}
public void setCookiePath(String path) {
this.cookiePath = path;
}
public String getCookiePath() {
return this.cookiePath;
}
In Restlet 2.3 I am using a ChallengeAuthenticator with ChallengeScheme.HTTP_BASIC to protect application resources. When the server receives an incorrect set of credentials the server correctly returns a 401 Unauthorized response. Also correctly it adds the following header:
WWW-Authenticate → Basic realm="My security Realm"
The problem is when that response goes back to a browser rather than a server (as is the case with the AngularJS application GUI), the browser natively interprets that 401 response and launches an 'Authentication Required' modal.
What I would like to try and achieve is to read the request headers (easily done) and if the X-Requested-With: XMLHttpRequest header is present I would like to suppress the WWW-Authenticate header in the '401' response.
Currently the WWW-Authenticate header is automatically set so my question is how can I override this default header being set and handle it manually?
In your case, you should use a filter to remove the header WWW-Authenticate from the response. This header corresponds to a challenge request in the response.
Here is the content of the filter:
public class SecurityPostProcessingFilter extends Filter {
public SecurityPostProcessingFilter(
Context context, Restlet next) {
super(context, next);
}
#Override
protected void afterHandle(Request request, Response response) {
String requestedWith
= request.getHeaders().getFirstValue("X-Requested-With");
if ("XMLHttpRequest".equals(requestedWith)) {
response.getChallengeRequests().clear();
}
}
}
You need to add it within the createInboundRoot method of your Restlet application, as described below
public class RestletApplication extends Application {
(...)
#Override
public Restlet createInboundRoot() {
Router router = new Router(getContext());
(...)
ChallengeAuthenticator guard = new ChallengeAuthenticator(
null, ChallengeScheme.HTTP_BASIC, "testRealm");
(...)
guard.setNext(router);
Filter filter = new SecurityPostProcessingFilter(
getContext(), guard);
return filter;
}
}
This will remove the header WWW-Authenticate from the response when the value of the header X-Requested-From is equals to XMLHttpRequest in the request.
FYI, there is a page on the Restlet web site that describes the mapping between HTTP headers and the Restlet API: http://restlet.com/technical-resources/restlet-framework/guide/2.2/core/http-headers-mapping.
Hope it helps you,
Thierry
Another way is to override the ChallengeAuthenticator#challenge method.
By default it set the response status and add a challengeRequest:
ChallengeAuthenticator guard = new ChallengeAuthenticator(getContext(), ChallengeScheme.HTTP_BASIC, "realm") {
public void challenge(org.restlet.Response response, boolean stale) {
String requestedFrom = response.getRequest().getHeaders().getFirstValue("X-Requested-With");
if (!"XMLHttpRequest".equals(requestedFrom)) {
super.challenge(response, stale);
} else {
response.setStatus(Status.CLIENT_ERROR_UNAUTHORIZED);
}
};
};
I am working on spring security implementation module and i need to set and get few cookies. i have tried creating cookies by using (javax.servlet.http.Cookie) and (javax.ws.rs.core.NewCookie) both are working fine for setting cookies, i can see the cookies in the browser but when i am trying to access them, it does give me only JSESSIONID, i need to access other cookies also.
This is how i am setting the cookies, and in both ways i will save the cookies successfully on the browser:
Cookie cookieOne = new Cookie("SERVLETCOOKIE", "TESTING COOKIES");
NewCookie cookieTwo = new NewCookie("WSRSCOOKIE", "TESTING COOKIES");
when i try to access the cookies, i have tried both #Autowired and #Context as below, but i can only get JSESSIONID cookie.
#Autowired HttpServletRequest request;
and
#Context HttpServletRequest request;
and i am trying to access the cookies as below :
Cookie[] cookieList = request.getCookies();
if(cookieList !=null && cookieList.length >0){
for(int i=0;i<cookieList.length;i++){
Cookie cookie = cookieList[i];
if(cookie.getName().equals("SERVLETCOOKIE")){
String value1 = cookie.getValue();
logger.info("cookie found. value ="+value1 );
}
if(cookie.getName().equals("WSRSCOOKIE")){
String value2 = cookie.getValue();
logger.info("cookie found. value ="+value2 );
}
}
}
It would be great if someone help me point out the way i can get all the other cookies.
The question is a few years old. However today I had a similar problem.
The solution was: If you are using the Apache http connector then the jersey cookie api seems not to be supported.
Try to get the cookies over the Apache api:
public static Cookie getCookie(final ClientRequestContext clientRequestContext, final String key){
final CookieStore cookieStore = ApacheConnectorProvider.getCookieStore(clientRequestContext.getClient());
return cookieStore.getCookies().stream().filter(c->c.getName().equals(key)).collect(CollectorUtil.singletonOrNullCollector());
}