AngularJS - Does $resource requests send cookies automatically? - angularjs

I am using a $resource in my angularJS app. Does it send automatically my cookies? I am doing requests on the same domain.

Browser will always send a cookie along with the request (no matter if it's an XHR request or not) as long as all assumptions are met (same domain, matching path, matching port, same protocol, not expired, etc.).
Since $resource service is just a simple Ajax wrapper your cookies will/should be sent (if everything's in place).

No. But if you want to send cookies, then you can try $cookies service to get the cookie and send with API either in the payload or included in the header.
You can also set the cookie in a default header (with $cookies service injected) so you don't have to specify it in all API calls.
var cookie = $cookies.myCookie; // suppose you already set $cookies.myCookie= 'xxx';
$http.defaults.headers.post.Cookies = cookie;

Note that running different applications on the same domain but on different ports might also be a reason for why cookies are not sent.
Cookies should not be port specific (regarding SOP), but CORS definitely is. Also see Are HTTP cookies port specific?
In my experience no current Browser (FF 47, Chrome 51, IE11) sends cookies for example from localhost:3000 to localhost:8080 in a XHR request.

Related

HTTP Requests with cookies on chrome extension

I have been several days trying to understand how a chrome extension is working when a HTTP request is made.
I am using YARC (Yet Another Rest Client) Chrome extension. But I guess it works same for all. Even Postman.
First thing I see is when I make the request if I am using an http traffic viewer like fiddler, I can see the host is the same I am making the request (Like www.google.com) and if I make an Ajax request or a php request the host is the same i have the script (like localhost).
The other thing is that I am making a POST request to a site to make a login that set a cookie. If I make it with the chrome extension, the cookie is set on my browser and then I could navigate normally on that page and the cookie is set and I am logged in. If I make this post with Ajax or PHP i´ts impossible to set this cookie because my host is in a different domain (localhost).
I can see I could make a submit post for this, but then I got redirected after the submit and it´s impossible to avoid that. I would like to manage the response like the extension as it was an Ajax call.
The main thing I see here is that thy host is always on same domain and this could avoid all this problems. But HOW? Looking for YARC code I can see they make this request as a regular http angularjs, it means Ajax I am almost sure. Anywhay not even trying with angularjs http I can get this to work.
What I actually would need to do is how this Chrome extension could make this and how to set this cookie when I make this POST, I mean, the Host set the cookie on their own domain, cause I can get the cookie but not to set it and I know it is impossible from a different domain.
Thanks in advance for all your help.

Why is the TLS client certificate not being included in preflight request on most browsers?

I'm having an issue with a web app I'm building. The web app consists of an angular 4 frontend and a dotnet core RESTful api backend. One of the requirements is that requests to the backend need to be authenticated using SSL mutual authentication; i.e., client certificates.
Currently I'm hosting both the frontend and the backend as Azure app services and they are on separate subdomains.
The backend is set up to require client certificates by following this guide which I believe is the only way to do it for Azure app services:
https://learn.microsoft.com/en-us/azure/app-service/app-service-web-configure-tls-mutual-auth
When the frontend makes requests to the backend, I set withCredentials to true — which, [according to the documentation][1], should also work with client certificates.
The XMLHttpRequest.withCredentials property is a Boolean that indicates whether or not cross-site Access-Control requests should be made using credentials such as cookies, authorization headers or TLS client certificates. Setting withCredentials has no effect on same-site requests.
Relevant code from the frontend:
const headers = new Headers({ 'Content-Type': 'application/json' });
const options = new RequestOptions({ headers, withCredentials: true });
let apiEndpoint = environment.secureApiEndpoint + '/api/transactions/stored-transactions/';
return this.authHttp.get(apiEndpoint, JSON.stringify(transactionSearchModel), options)
.map((response: Response) => {
return response.json();
})
.catch(this.handleErrorObservable);
On Chrome this works, when a request is made the browser prompts the user for a certificate and it gets included in the preflight request and everything works.
For all the other main browsers however this is not the case. Firefox, Edge and Safari all fail the preflight request because the server shuts the connection when they don't include a client certificate in the request.
Browsing directly to an api endpoint makes every browser prompt the user for a certificate, so I'm pretty sure this is explicitly relevant to how most browsers handle preflight requests with client certificates.
Am doing something wrong? Or are the other browsers doing the wrong thing by not prompting for a certificate when making requests?
I need to support other browsers than Chrome so I need to solve this somehow.
I've seen similar issues being solved by having the backend allow rather than require certificates. The only problem is that I haven't found a way to actually do that with Azure app services. It's either require or not require.
Does anyone have any suggestions on how I can move on?
See https://bugzilla.mozilla.org/show_bug.cgi?id=1019603 and my comment in the answer at CORS with client https certificates (I had forgotten I’d seen this same problem reported before…).
The gist of all that is, the cause of the difference you’re seeing is a bug in Chrome. I’ve filed a bug for it at https://bugs.chromium.org/p/chromium/issues/detail?id=775438.
The problem is that Chrome doesn’t follow the spec requirements on this, which mandate that the browser not send TLS client certificates in preflight requests; so Chrome instead does send your TLS client certificate in the preflight.
Firefox/Edge/Safari follow the spec requirements and don’t send the TLS client cert in the preflight.
Update: The Chrome screen capture added in an edit to the question shows an OPTIONS request for a GET request, and a subsequent GET request — not the POST request from your code. So perhaps the problem is that the server forbids POST requests.
The request shown in https://i.stack.imgur.com/GD8iG.png is a CORS preflight OPTIONS request the browser automatically sends on its own before trying the POST request in your code.
The Content-Type: application/json request header your code adds is what triggers the browser to make that preflight OPTIONS request.
It’s important to understand the browser never includes any credentials in that preflight OPTIONS request — so the server the request is being sent to must be configured to not require any credentials/authentication for OPTIONS requests to /api/transactions/own-transactions/.
However, from https://i.stack.imgur.com/GD8iG.png it appears that server is forbidding OPTIONS requests to that /api/transactions/own-transactions/. Maybe that’s because the request lacks the credentials the server expects or maybe it’s instead because the server is configured to forbid all OPTIONS requests, regardless.
So the result of that is, the browser concludes the preflight was unsuccessful, and so it stops right there and never moves on to trying the POST request from your code.
Given what’s shown in https://i.stack.imgur.com/GD8iG.png it’s hard to understand how this could actually be working as expected in Chrome — especially given that no browsers ever send credentials of any kind in the preflight requests, so any possible browsers differences in handling of credentials would make no difference as far as the preflight goes.

Will Spring Security CSRF Token Repository Cookies Work for all Ajax Requests Automatically?

I'm going through the following security tutorial and it configures a CsrfTokenRepository like this:
.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
Is that all that is required to get Ajax requests working across all libraries? The Angular documentation for $http says that Angular reads the CSRF cookie that Spring provides and sets a corresponding a header when it makes requests. So I'm assuming it does this because the cookie will not automatically be included when sending Ajax requests?
[Update]
I read the article again and it says that the CSRF protection is provided by the header. So if I interpret that the right way it's the fact that the client is sending back the cookie value in a unique way that is different than it was sent in the first place that provides the CSRF protection. In other words the client receives the cookie and changes the way it is sent back, so that the server knows that the client is indeed in control of the cookie?
CSRF protection with Spring CookieCsrfTokenRepository works as follows:
Client makes a GET request to Server (Spring backend), e.g. request for the main page
Spring sends the response for GET request along with Set-cookie header which contains securely generated XSRF Token
Browser sets the cookie with XSRF Token
While sending state changing request (e.g. POST) the client (Angular) copies the cookie value to the HTTP request header
The request is sent with both header and cookie (browser attaches the cookie automaticaly)
Spring compares the header and the cookie values, if they are the same the request is accepted, otherwise 403 is returned to the client
Note that only state changing requests (POST, PUT, DELETE) are CSRF protected by default and only these need to be protected when API is properly designed (i.e. GET requests don't have side effects and modify the state of the app for example).
The method withHttpOnlyFalse allows angular to read XSRF cookie. Make sure that Angular makes XHR request with withCreddentials flag set to true.

how to implement csrf protection for cross domain requests

I have two web apps, one for the Web UI in AngularJS and one for the REST webservices in Java. Both are deployed on separate domains.
The applications uses cookie for authentication. Whenever user enters a valid username and password, server returns a http only cookie back containing the token and that cookie is passed across all requests. I have enabled CORS on both apps, thats why the session cookie is working properly.
Now, I am trying to add CSRF protection for this. I was trying to use the csrf cookie where in the server will send the csrf cookie(not httponly) as part of REST response and the UI will read the value from the cookie and pass that in a csrf token header for the other REST calls.
The problem with this approach I am facing is that since the server is in different domain, I cannot read the cookie using $cookies in AngularJs. Is there a way to read a value of that cookie?
If not, then can I implement CSRF in some other way?
I also tried to implement the creation of the csrf cookie on the Web UI itself in the browser but the browser does not send the cookie to the webservice as its in different domain.
So, my question is how to implement csrf protection for this kind of situation?
You were on the right track with this:
I also tried to implement the creation of the csrf cookie on the Web UI itself in the browser but the browser does not send the cookie to the webservice as its in different domain.
The CSRF cookie isn't meant to be "sent" to the server, it is meant to be read by the client and then supplied in a custom HTTP request header. Forged GET requests (triggered by HTML tags such as <img src="">) from other domains cannot set custom headers, so this is how you assert that the request is coming from a javascript client on your domain.
Here is how you can implement the idea you were working on, imagine you have api.domain.com and ui.domain.com:
1) User loads the Angular client from ui.domain.com
2) User posts authentication information from Angular client to api.domain.com
2) Sever replies with an HttpOnly authentication cookie, called authCookie, and a custom header e.g. X-Auth-Cookie, where the value of this header is a unique value that is linked to the session that is identified by the authCookie
3) The Angular client reads the X-Auth-Cookie header value and stores that value in a XSRF-TOKEN cookie on its domain, ui.domain.com
So now you have:
XSRF-TOKEN cookie on ui.domain.com
authCookie cookie on api.domain.com
4) User makes a request of a protected resource on api.domain.com. The browser will automatically supply the authCookie value, and Angular will automatically send the X-XSRF-TOKEN header, and will send the value that it reads from the XSRF-TOKEN cookie
5) Your server asserts that the value of X-XSRF-TOKEN is linked to the same session that is identified by the value of the authCookie
I hope this helps! I've also written about token authentication for Angular, Token Based Authentication for Single Page Apps (SPAs) (Disclaimer: I work at at Stormpath)
Angularjs has built-in support for CSRF but unfortunately it doesn't work cross domain, so you have to build your own.
I managed to get it working by first returning a random token in the headers and cookies on the first request. In order to read the header you need to add it to Access-Control-Expose-Headers. This is then added to all posts
$http.get('url').
success(function(data, status, headers) {
$http.defaults.headers.post['X-XSRF-TOKEN'] = headers('XSRF-TOKEN');
});
Then on the server you can compare the cookie value with the value in the header to ensure they are the same.
$http docs : Angular provides a mechanism to counter XSRF. When performing XHR requests, but will not be set for cross-domain requests.
This is a small lib put together might help you https://github.com/pasupulaphani/angular-csrf-cross-domain

Setting cookie from WebApi via angularjs

I'm making a call to the WebApi service, which sets the cookie in the response object.
The call is made from angularjs via $resource
So this is the server code:
CookieHeaderValue cookie = new CookieHeaderValue("Token", "blah") { HttpOnly = true, Expires = DateTime.Now.AddYears(10), Path="/" };
response.Headers.AddCookies(new CookieHeaderValue[] { cookie });
This works, I can see the Set-Cookie header in a response, however the cookie is not being set.
A friend of mine had to set xhrFields' withCredentials to true when he was using jQuery, so I wonder if there's something that needs to be configured in angular as well ?
There could be a number of things going on.
First, since you are on separate domains, you may need to implement CORs (cross origin resource sharing), but it seems that the request is being made successfully. I'm not sure why that works, I would think that browsers would prevent it. In any case here's a jsfiddle that illustrates using CORs with angularjs to make both $http & $resource requests. The trick seems to be to configure the $http service:
$http.defaults.useXDomain = true;
Another thought is that cookies from one domain, can't be accessed by another domain. Here is another question on cookies with angularjs, but the request and server seem to be on the same domain. Here is a discussion on cookie domains, and how they are applied.
If it's possible I would try to get the cookie request/response working on the same domain, and then move the client to another domain.

Resources