Safari cookie in iframe - cookies

Latest safari update block 3rd-party cookies in iframe. ( https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ )
They offer several solutions to resolve it. I have tried to implement Storage Access API solution, but do like the result.
Can anyone give a hint or a sample flow without technical details on how to implement this:
Option 1: OAuth 2.0 Authorization with which the authenticating domain (in your case, the third-party
that expects cookies) forwards an authorization token to your website which you consume and use to
establish a first-party login session with a server-set Secure and HttpOnly cookie.
firstparty.com has 3rdparty.com in iframe. According to option 1 3rdparty.com authorize via OAuth, receive token. But what does it mean to "forward token to your website to establish login session"? Should 3rdparty.com run login routine as first-party in separate window?

what they are referring to is still having the cookie be a first party (on your website). for example:
user follows this flow:
go to website.com
redirected to oauthSite.com for authentication
ouathSite.com redirects back to website.com with token (or code)
website.com sets token locally on server side code
user now has secure (https only) and HttpOnly (inaccessible to the JavaScript) cookie token that can be used for stateless interactions with website.com
This site seems to go thought the flows quite well:
https://medium.com/#darutk/diagrams-and-movies-of-all-the-oauth-2-0-flows-194f3c3ade85

Related

Cookie on same domain (First party) inside iframe not sending or saving

I have a SPA which uses a session token stored in a cookie for authentication with an API.
The SPA is on spa.domain.com, and the API is on api.domain.com; they share a common TLD.
The SPA sends a request CSRF token to the API, then sends a login request with the CSRF token and credentials to authenticate and create the cookie which is sent with subsequent requests.
This all works fine.
The problem I'm facing is that the SPA has an iframe, to which the src points back at a separate section of the SPA (The need for this is not the point of my question, i know it's convoluted but needs must).
The document loaded in the iframe has the same subdomain as the parent, i.e. spa.domain.com loads an iframe of spa.domain.com/iframecontents.
The page within the iframe skips cookies in Chrome and FF (Safari sends them an it works fine). I've looked at various threads about SameSite and Secure cookies and 3rd party vs first party but it is my understanding that this should simply be a first party cookie, i own the domains etc. (Although I have just realised locally the API is on one port and the SPA is on another port so that might account for different domains... just did a bit more reading, port is not included just the hostname)
It seems the cookies it already has for that domain are not being sent with the request
This cookie was blocked because it had the "SameSite=Lax"" attribute and the request was made from a different site and was not initiated by a top-level navigation.
and the cookies it receives to replace the ones the server thought were missing appear to be being ignored too
This attempt to set a cookie via a Set-Cookie header was blocked because it had the "SameSite=Lax" attribute but came from a cross-site response which was not the response to a top-level navigation.
The cookies look like this
path=/; domain=localhost; secure; httponly; samesite=lax
If I change samesite to none then it does work, but then I assume that means I'm just opening up my session cookies to being stolen by third parties in xss attacks? Seems nonsensical to me.
Why is an iframe on the same domain not working with lax and how might I work around this issue?

OAuth Refresh Token Architecture

I have multiple web applications that I want to use our oauth provider for. All web applications live on different domains. For example, a.com b.com and c.com.
My oauth provider when the token end point is accessed returns a refresh token via a http only cookie. This is fine in theory, but the browser will not accept the cookie due to it being a cross domain cookie. (I have tried just about everyhting to allow this. See below.)
Cookie Settings
Same Site: none
secure: true
domain: null
http only: true
I have tried setting the cors of the web app to allow the auth api end point
CORS settings:
WithOrigins: "the url of the api"
WithMethods : "GET, POST"
WithHeaders: "Content-Type, *"
AllowCredentials : true
How can my my oauth provider send back a refresh token that I can securely store on the client?
The usual solution is that the OAuth Provider (Authorization Server) issues tokens to your back end, then applications issue their own first party cookies for the browser, in the Same Site as the web origin and therefore are not impacted by browser cross domain cookie restrictions.
The easiest to manage option can be to store tokens in AES256 encrypted HTTP Only SameSite=strict cookies, whose decryption key is only known to the server.
TOKEN HANDLER PATTERN
One interesting variation in this is for an API to issue secure cookies used by a Single Page Application. This involves hosting that uses related domains. Cookies issued by the API are then in the Same Site as the web origin and not cross domain:
Web origin = https://b.com
API origin - https://api.b.com
See this Curity Code Example for further info on an SPA solution. If you are using a website technology stack, you will not be using an API but the same cookie principles will apply.

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.

OIDC js library reponse cookies are not stored and not attaching for subsequent requests

I am using authcodeflow with PKCE.
Using OIDC js library in the frontend, making calls to adfs getting an auth code and then calling my backend api. The backend api which calls adfs server get the access token and the backend api returns the token as a cookie to the frontend. I can see the cookie in response headers. but That cookie is not stored in browser and not getting added for subsequent requests. I have tried with samesite with all modes -> Lax, None,Strict and not setting.
Is this an issue with OIDC js library or is it blocking the cookies to store in browser?
Update:
Below are the observation with my analysis
Since the OIdc-client-js does not have an option to set flag "withCredentials" to true for the requests. There are no cookies send in the request and response cookies are ignored for the cross origin requests.This changes are marked as enhancement and still not completed in thier github repo.
https://github.com/IdentityModel/oidc-client-js/issues/1062
Is there any way to achieve with this library? or any other libraries for OIDC js
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials
So you are issuing a cookie from an API domain that is a sibling of the WEB domain:
web.mycompany.com
api.mycompany.com
Cookie domain = .mycompany.com
POSSIBLE CAUSES FOR COOKIE BEING DROPPED
Maybe it is the withCredentials flag or maybe due to a lack of user gesture, since the user has not done anything explicit to navigate to api.mycompany.com, such as a browser navigation or clicking a link?
FORCING WITHCREDENTIALS
You can override the prototype like this in order to add the withCredentials property. This is a little hacky but you could limit usage based on the URL and it should let you know whether setting withCredentials resolves your problem:
let open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url) {
open.apply(this, arguments);
this.withCredentials = true;
}
PROXYING VIA WEB DOMAIN WILL HAVE FEWER COOKIE ISSUES
In my blog post I do something similar to proxy messages containing a refresh token. I use the web's exact domain though, rather than using an API subdomain. This will never be impacted by browser restrictions.

SameSite attribute in cookies

I have a website a.com that has third party app point to apps.b.com. When I login to a.com, I'm also authenticated to apps.b.com in the background using the same credentials. This is so the users do not have to login to access apps.b.com. I understand that browser sends all the cookies to apps.b.com when making the request to it. This is how it works now. Reading the article https://web.dev/samesite-cookies-explained/ in regards to SameSite attribute, it appears apps.b.com is third party site.
Now do I have to configure web server on a.com to set the cookie to SameSite=none;Secure OR do I have to set the SameSite=none;Secure on web server on apps.b.com?
Any time you are making a cross-site request that needs cookies, then those cookies need to be marked SameSite=None; Secure.
So, for example if the user is on a.com and you have an <iframe> or fetch() to apps.b.com that expects cookies, then the apps.b.com cookies need SameSite=None; Secure.
Vice versa, if the user is on apps.b.com and you are making requests to a.com to check their auth status by relying on the a.com cookies, then those cookies need SameSite=None; Secure.
Essentially the pattern you're looking for is when the site in the browser location bar is different to the site that needs the cookies, then those are the cookies that need marking. So, depending on your set up, it may be one or both.