SAML logout request is not sending cookies to IdP - cookies

We are trying to implement SAML logout from a Service Provider/Relying Party to an IdP (in this case, AD FS 3.0). Our logout is not invalidating the session in AD FS. We have narrowed down that the ADFS auth cookies are not being sent in the HTTPS request for some reason, though we have no idea why. This is using HTTPS redirects for the SAML flow.
We have tried to get the request headers the same as the subsequent logon requests which successfully send the cookies, but to no avail.
I have a Fiddler trace that captured multiple logout attempts (and the automatic logins in between). This is in an isolated lab network with dummy data.
Fiddler trace
Any ideas what we could try to get those cookies sent to the AD FS server on the logout request?

The answer, as found by a colleague, was that the redirect request was an XHR request since it started in JavaScript and was not a ‘normal’ browser redirect. Cookies are only allowed to be included on cross-domain XHR requests if the destination server allows the requester access. So, the fix involved terminating the chain of redirects on the XHR request and doing a normal browser redirect. This allowed the browser to send cookies along with the redirect request to the new site.

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?

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.

Safari cookie in iframe

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

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