How can you access cookies on a static S3 site behind a CloudFront distribution? - amazon-web-services

So I'm trying to deploy my website which works well when in a local environment, but when it is deployed to Cloudfront, it can't seem to access cookies.
My frontend tech stack is as follows: Angular site hosted on S3, cloudfront distribution in front of it, custom domain name with a valid ssl certificate.
When the user navigates to the login page, they can successfully submit the forum, and the server responds with a JWT token in the Set-Cookie header.
After this though, in the angular site it says that the access-token cookie does not exist. The strange part here is that on subsequent requests, the access-token cookie is in fact forwarded back to the backend. (In the image below, the login button was pressed again, so the response cookie is the same as the request cookie.)
I've ensured that HttpOnly is not set, and that the frontend and backend are both hosted under the same root domain frontend.root.com and api.root.com.
Cloudfront has been configured to forward the access-token cookie:
cache policy:
origin request policy (note that it still did not work when I had this set to forward all cookies and not just the access token):
Response headers settings:
So in my angular site, after the /login api call resolves, I use the ngx-cookie-service to check and try to retrieve the cookie.
this.cookieService.check('access-token'); // checks if it exists, returns false
this.cookieService.get('access-token'); // returns '' meaning the cookie does not exist
Any ideas on how to resolve this issue and access the cookies from within my angular site? I can provide more information on my configurations if needed. Thanks!

As you can barely make out in the screenshot the Cookies have the domain set as something starting with a suggesting that it is api.root.com, most importantly it is not frontend.root.com and not root.com.
The server needs to set the domain of the cookie to root.com for it to be available to all subdomains of it.

Related

Send cookies to EBS backend from CloudFront S3 frontend

I am pretty inexperienced with AWS and I have an app that uses a JWT token stored in a cookie to log in users. On page load, a GET request is made to the backend, the backend verifies the token and redirects the user to the dashboard page, which can only be accessed with a valid token. If there's no token, the backend returns a 400 error and the user stays on the home page. This works flawlessly on my local machine but not when I host the project on AWS. I believe there are no problems with how it's hosted because the backend does receive the GET request from the frontend, just without cookies, and I am adding credentials with it. The documentation talks about a Forward Cookies option and so does this video by AWS but the console has since changed and this option is no longer available. The second answer in this post suggests that the right way to do it is via custom cache and origin request policies in a distribution behavior but the example given doesn't match my use case and I haven't been able to get it working. I have tried editing the distribution behaviour and both setting "Cookies" to "All" in the legacy cache settings and using custom cache and origin request policies with the same setting but nothing works.
Axios GET request:
axios
.get(`${backendURL}/isUser`, {
withCredentials: true,
})
.then(() => router.push("/dashboard"))
.catch((error: AxiosError) => console.error(error))
Development (left) and production (right) requests
Distribution behavior unchanged (just HTTP to HTTPS redirection)
This has nothing to do with AWS and everything to do with how you are setting your cookie. You can't set a cookie from your "backend", so that your "front-end" will return it, unless they are on the same subdomain, and the cookie domain setting is set correctly.
I had some similar issues with cookies. #Warren is actually correct here.
If you want to access cookies, you'll have to setup same subdomains for your client and server applications.
However, I tried something earlier and this may work (not sure)
Map the S3 link (client) and server to cloudfront domains. This will make both the domains secure with https. (select a CF certificate, the default one). Now, set the following thing on the server side while setting cookies:
httpOnly: true
sameSite: none
secure: true
This should work I guess, give it a try. Other cloudfront setting you can change has been attached. (That is what I did)
I didn't mention on my post that I was setting the cookies on the frontend of my app, hosted at https://abcdef1234.cloudfront.net/, and trying to send the cookies to my backend, at https://api.mydomain.com/. I didn't think this was an issue but it turns out it is. To get it working, I have had to change my CloudFront distribution to use https://myapp.mydomain.com/ and the backend to set the cookie itself.

Using JWT authentication with Django/DRF and Storing JWTs in HttpOnly Cookies

I am trying to build a web app using Django and DRF at the back-end and ReactJs at the front end and I want to keep them separate (i.e. avoid Server Side Rendering).For authentication purposes, I want to employ JWT and I am using djangorestframework-jwt for that. I have read it at several places that it is not secure to store JWTs in the local storage so I am trying to use HttpOnly cookies for that. One can achieve that by configuring the django server to send HttpOnly by overriding the following default settings of the drf-jwt package in the settings.py file of your project JWT_AUTH = { 'JWT_AUTH_COOKIE': '<cookie name>', } which is set to none by default. The server sends the httpOnly cookie as anticipated but there are a few issues I am facing:
1.Same Domain Restraint
I am aware that httpOnly cookies wont be attached to the request headers unless the request is being made to the server which is hosted on the some domain. In my case I am using localhost:8000 for django and localhost:3000 for my react project so the browser doesnt attach the cookie as the request is made to a different port. I tried running both app on port 3000 simultaneously, and the browser did attach the cookie in the header and I did get the a 302 response from the server. However, it opened door to all sorts of problems due domain clash. I reckon I can solve this problem using nginx reverse proxy or something like that but I am not sure about it. Do guide me how can I serve both apps on the same host during the development.
2. Token Refresh Problem
When I refer to the view setup to refresh the token, I run into a bad request error even when the browser does attach the cookie along the request header. This is the server response in the browser
{"token":["This field is required."]}
Thanks if for reading it all the way down here!
In order for things to be secure:
You need CORS (Quickstart: CORS_ALLOWED_HOSTS=["http://localhost:3000"], CORS_ALLOW_CREDENTIALS=True)
The short-lived token (session) cookie (5-15mins), should NOT have HTTP-ONLY setting
The refresh token cookie SHALL have HTTP-ONLY setting
Then your basic flow is:
On login Django creates session token and sends it
Your SPA reads the cookie and adds its value to the authorization header (Authorization: JWT ...token...)
Any request to Django should be made with that Authorization header
The refresh flow is:
Send a request to the refresh token endpoint following the documentation of the library you use
Django then reads the HTTP-ONLY cookie and verifies it
If valid, Django sends a new refresh token as HTTP-ONLY cookie along with a new short-lived token session cookie
Once the refresh token has expired, you log the user out.
An article here goes into detail using GraphQL, but the cookie part and handling of most of the frontend code you should be able to adapt to REST.

Setting up OIDC for a backend API + frontend SPA

I’ve got a project using a Django backend, with Django Rest Framework to serve an API, and a Vue.js frontend SPA to consume the API. I’m running into some kind of CORS issue during authentication.
I’ve been using mozilla-django-oidc to implement the Authorization Code flow with Okta. This works fine pretty much out of the box, and if I navigate to the API in my browser, I can login to Okta and I get a Django session. I’ve also enabled SessionAuthentication for DRF, which allows the same session cookies generated by Django to be accessible by the SPA (both SPA and API are on the same domain), provided I login first directly through the API. This all works fine until the id token expires. In Django, when the id token expires, I get a redirect to https://example.okta.com/oauth2/v1/authorize?..., the Authorization Code flow completes and I get sent on through to the originally requested page. Where things fail is in an ajax request from the SPA to the API with an expired id token. I get the same redirect, but this time it fails due to CORS.
Access to XMLHttpRequest at 'https://example.okta.com/oauth2/v1/authorize?response_type=code&client_id=X&redirect_uri=http%3A%2F%2F127.0.0.1%3A8000%2Foidc%2Fcallback%2F&state=X&scope=openid+email+profile&prompt=none&nonce=X' (redirected from 'http://127.0.0.1:8080/api/X') from origin 'http://127.0.0.1:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I’ve tried to identify why it’s failing.
On local development, I’m running my API on 127.0.0.1:8000 and my SPA on 127.0.0.1:8080, so clearly the origins don’t match. I have Vue setup with a proxy so it looks like requests are coming from 8080, but the redirect_uri in the request to Okta is still using 8000.
When I deploy to a test server, I’m using docker containers for the API and SPA and a reverse proxy to route requests and also for SSL. In this case, the API and SPA have the same origin (I think). Yet I still get the same error message.
Access to XMLHttpRequest at 'https://example.okta.com/oauth2/v1/authorize?response_type=code&client_id=X&redirect_uri=http%3A%2F%2Fexample.com%2Foidc%2Fcallback%2F&state=X&scope=openid+email+profile&prompt=none&nonce=X' (redirected from 'https://example.com/api/X') from origin 'https://example.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
If you notice, the redirect_uri is http, not https. I suspect that is why this is failing. Though I’m not entirely confident because if I navigate my browser to the API, I am on https, but the redirect_uri is still http, and it still successfully authenticates.
Any insight would be really helpful.
What am I doing wrong or missing here?
Am I approaching the authentication flow all wrong for an API+SPA app? Should I do authentication on the SPA instead? How does the API then know who’s logged in?
Edit: I have already tried adding the origins to the Security > API > Trusted Origins section in Okta configuration. No dice.
https://developer.okta.com/docs/guides/enable-cors/overview/
In Okta, CORS allows JavaScript hosted on your websites to make a request using XMLHttpRequest to the Okta API with the Okta session cookie. Every website origin must be explicitly permitted as a Trusted Origin.
So configure your Trusted Origins properly in the Okta client configuration.
Are you making an AJAX call to /authorize ? That could be the reason for the error.
As mentioned here, when making requests to the /authorize endpoint, the browser (user agent) should be redirected to the endpoint. You can't use AJAX with this endpoint.

why browser isnt storing a cookie created in a subdomain, in the referer subdomain?

i'm facing a few problems when i authenticate usign cookies to store the token, those cookies should be delivered between subdomains, for example, i have my auth code deployed in a subdomain named services.mydomain.co and my frontend in subdomain named apps.mydomain.co,
when someone authenticates, then my auth code creates a cookie with the tag Domain like this: Domain=.mydomain.co in order to enable it to be delivered between all subdomains of mydomain.co, but the cruel reallity is that it is not working, even the browser isn't storing the cookie.
the cookie it's successful delivered in response header after authenticate but the browser isn't storing it.
im creating the cookie this way:Set-Cookie: myKey=myKeyValue;path=/;httpOnly;Max-Age=1555520000;Domain=.mydomain.co;sameSite=none
hope someone could geve me an advice and sorry for bad english.
From your example, you are setting SameSite=None without Secure. This is invalid and browsers (Chrome, Edge, Firefox, etc.) will begin rejecting this.
However, you should check DevTools in Chrome on your site to see the specific error. You can check both the Console and individual requests in the Network tab to see issues with a given cookie.
You can find more detail on https://www.chromium.org/updates/same-site/test-debug

How is cookie handled for multiple CORS requests

Let's say I have a web app with domain myapp.com.
This web app will be mostly a client heavy app and will be making authenticated CORS requests (basically setting cookies) to multiple web sites, say abc.com and 1234.com.
Is there any way in current web standards to keep separate cookies for abc.com and 1234.com in client's browser?
The way I see it cookies are always set under myapp.com not to CORS requests.
The browser will never send cookies of domain A to domain B.
If you have js code on myapp.com which issues a CORS request to abc.com, only the cookies of abc.com will be sent (if withCredentials was set to true).
Otherwise it would be a violation of Same Origin Policy
If you want to completely prevent JS code to read cookies you might want to use HttpOnly flag