Access cookies set by an api using javascript - javascript

I am doing development in reactjs on my localhost. I have an api written in nodejs deployed at https://bianca-service-dev0.cfapps.io
Using react i am calling an auth endpoint in the api to login my user. The auth endpoint returns a token to me in response body and a cookie name CSRF-TOKEN. When i try to view document.cookie i only see the cookies set by my localhost and dont see cookies set by the api.
Can I access cookies set by a different domain?

Not directly.
JavaScript running in the browser can only read cookies associated with the page that the JS is running on.
Requests to other origins (if sent withCredentials) will include the cookies transparently though so you can still use them to access content on the origin that set them.

Related

Why don't browsers allow [::1]:8080 to set a cookie with Domain=127.0.0.1?

I am creating a project for school, and I need to create a backend in spring boot and a frontend in vanilla JS +HTML+ CSS. Since both are on localhost but on different Origins namely on port 8080/5500 respectively ,I am having some issues with CORS in that my cookies are not saved in the browser so no session is created between backend and frontend. This leads to the problem that after login in I will not be able to access securised endpoints because it will require me to log in again since no session is established.
What solution do I have for making this work / saving the cookies?
Further I will give several details and images about the issue that I have.
The backend uses MySQL, spring boot and RestControllers. It basically is a simple CRUD that I also added spring security to. Now this is how my Spring Config looks like
and this is how my CORS config looks like
and this is how my fetch request looks like in the frontend.
Also the request to /save
The flow is as follows : I make a request to /login, than I should be able to use the /save endpoint. This works flawlessly on postman since accessing /save works only after /login was successfully otherwise I get unauthorized, but it does not work in the browser because the cookie that is send with the first response is not saved as below. In the following image the response provides the JSESSION cookie after successfully authentication.
But then when I check cookies section, the cookie is not there.
And when I make a request to the /save endpoint I get the following issue
I also tried the answer over here How to set cookie domain and path with Spring boot but cookies are still not saved.
The browser refuses to allow a URL whose host is [::1] to set a cookie with a Domain attribute of 127.0.0.1. Why? Simply because, even though [::1] is the IPv6 equivalent of 127.0.0.1, the latter doesn't domain-match the former.
Anyway, why would you want to set a cookie with an IP address for the Domain attribute? You seem to be misunderstanding the purpose of that attribute; read what the MDN Web Docs page entitled Using HTTP Cookies has to say about it:
The Domain attribute specifies which hosts can receive a cookie. If unspecified, the attribute defaults to the same host that set the cookie, excluding subdomains. If Domain is specified, then subdomains are always included. Therefore, specifying Domain is less restrictive than omitting it. However, it can be helpful when subdomains need to share information about a user.
In your case, the host of the URL that sets the cookie is an IP address and not a domain. Therefore, specifying a Domain attribute for that cookie is pointless, simply because an IP address doesn't have subdomains.

Browser not sending cookies to APIs within same domain

I have APIs deployed in 2 separate namespaces, admin.abc.com (original) and api.admin.abc.com (new). Upon completing login, a jwt cookie is set by the admin.abc.com site for .admin.abc.com. The cookie is for the Session, Secure and HttpOnly and it does not have any SameSite restrictions set. This cookie works well for all API calls to admin.abc.com.
Due to some new changes requiring some services to deployed separately, there are now services also deployed in the namespace api.admin.abc.com. I have a status page that gives me information about the services, memory, etc. and it works fine for the original namespace. But when I add in a status call to the second namespace, no cookies are passed along and the call fails authentication. However, if I open the URL in another tab in my browser, the cookies are passed to the backend and the call succeeds.
I've looked into setting my own cookies in the header during the request after retrieving the jwt's value. This fails for 2 reasons: 1) I don't believe I can access the jwt's value as the cookie is HttpOnly, and 2) I can't set a cookie in the HttpHeader options. I get a "Refused to Set Unsafe Header" error in the console if I try. If I turn on 'withAuthorization:true', it does set it (but still prints the error) and then actually gets a CORS error.
If I manually disable the HttpOnly flag on the cookie using EditThisCookie extension, the calls work successfully. I don't really think it's an option for me with my company's security, but it technically works.
I know some suggest sending the jwt as an Authorization: Bearer token, which I would do, but because the cookie is HttpOnly, I can't retrieve the value and programmatically set it. Additionally there is another site-wide cookie I'd like to send over at times that I can't really switch to some custom header.
Thoughts on what I can do? Is it not possible? My only other option is that all requests to the new namespace have to be routed through an API built in the old namespace.

Should the OAuth2 Redirect URL be to the frontend or backend?

I'm setting up OAuth2 in my app using the Authorization Grant flow. I am also using create-react-app, such that I'm developing on localhost:3000, which proxies to my app server backend on localhost:8080.
Everything mostly works, except for the fact that I cannot get the CSRF token working.
I realized it was because I was having the OAuth2 Redirect URL set to the backend, and as a result it was not sending the private encrypted csrf_state cookie along, because the request was originating from google instead of my app.
I don't think this will be a problem in production, because there won't be a proxy server. Instead, both the backend and frontend will be served from the same mydomain.com
So, should I just not have this work in development? Or should I have the OAuth2 redirect URL set to my frontend (localhost:3000), which then automatically redirects to the backend (localhost:8080), such that it can send the private encrypted CSRF token along?
Or is there a way to have the cookie originate from google, without having the multiple redirects? Or should I just not bother with CSRF, since SameSite has such large support amongst browsers now?
The OAuth2.0 Authorization Code grant includes CSRF protection using the state parameter. Use this instead of relying on cookies.
state
RECOMMENDED. An opaque value used by the client to maintain
state between the request and callback. The authorization
server includes this value when redirecting the user-agent back
to the client. The parameter SHOULD be used for preventing
cross-site request forgery as described in Section 10.12.
Source: https://www.rfc-editor.org/rfc/rfc6749#section-4.1
Ahmad is right - and here is some more context on standard usage for react apps and APIs:
If you're using React then you have an SPA that should redirect directly to Google during logins
So your redirect url should be localhost:3000
Your SPA should be entirely cookieless - and much simpler - which is one of the benefits of SPAs - also you can turn off CSRF checks in the API
Your SPA will then send an access token to your API and the API will need to validate the token rather than cookies
My tutorial and code sample may help you understand the moving parts:
https://authguidance.com/2017/09/24/basicspa-overview/

How to handle httpOnly cookie authentication in next.js with apollo client

In my usual experience all single page apps I worked on used JWT as authentication mechanism. I came across api that uses httpOnly cookies for this.
Since we can't access such cookie via javascript to know if it is present or not, how does one handle this in react app?
My initial idea was to track this by setting some sessionStorage upon successful sign in and removing it if I receive an error related to authentication.
But this doesn't work well with next.js server side rendering I believe? We have it set up with apollo client which allows setting custom headers and cache.
Is there a common way to handle this authentication process with set up above?
httpOnly just means that the value can't be read by JavaScript.
So you make an HTTP request to the server and it will return a response with a Set-Cookie header.
Then any future requests will automatically include the cookie.
(Just make sure that you set withCredentials or the equivalent.)

Query about accessing HttpOnly Cookie & Secure cookie

I am working on RESTful SPA app using angularJS. Currently initial REST call is setting a "token" cookie on xyz.com ( secured response cookie) after successful user login. I am not able to read this cookie in Javascript/angular as I am working on localhost.
What I understood here , unless I run this app from xyz.com , i wont be able to access this cookie OR do I need a secured connection ?
Is my Understanding correct ?
Secondly, my understanding about "httponly" cookie is that , it wont be accessible from javascript even though you are on same host.
Please correct my understanding.
As the author of a website:
You cannot read a cookie for a different site (ever)
You cannot read an HTTP Only cookie with JavaScript
You cannot read a Secure cookie unless it is served over HTTPS
That's three separate conditions, with independent effects, and none, some or all of them can apply to any given cookie.
Therefore if a cookie is secure and for a different site then you can't read it no matter if you use HTTPS or not (since different site blocks you even if secure does not).

Categories