privacycg / CHIPS

A proposal for a cookie attribute to partition cross-site cookies by top-level site
Other
116 stars 29 forks source link

OIDC authentication on vanity domains with partitioned cookies #80

Open jkosir opened 7 months ago

jkosir commented 7 months ago

Hi, my use case is a SaaS web app that allows clients to define vanity domains on which the app can be accessed.

Authentication is done with cookies and when our app is served on vanity domain (e.g. vanity.com) we set a (3rd party) cookie for our main domain, e.g. hub.ourapp.com/api, and all data is loaded with API calls to hub.ourapp.com/api. We do not want to set any cookies for vanity.com as the domain is not controlled by us. As far as I understand this is the same case CDN Load Balancing example and we are in fact able to make this work with partitioned cookies, we set a session cookie for hub.ourapp.com/ with partitionKey=vanity.com when our app is served on vanity.com.

The only issue we're having is in the login process which is done via OIDC, essentially after successful login on the identity provider the users are redirected to hub.ourapp.com/api/oAuthCallback?code=<>&state=<>, which (after successful code token exchange) drops a session cookie. Since here we have hub.ourapp.com as the top level context we are unable to drop a partitioned cookie.

We found a way to make this work by embedding an <iframe src="hub.ourapp.com/..."> inside vanity.com and do the OIDC process inside the iframe (or in a popup opened from said iframe), eventually the iframe gets redirected to a callback url (<iframe src="hub.ourapp.com/api/authCallback?code=<>&state=<>">) and the cookie dropped in response to /api/authCallback is correctly partitioned for vanity.com.

While this works it is quite cumbersome and can pose some security risks, if the OIDC flow is done in a popup a postMessage(codeToken, '<iframeOrigin>') must be used to share the code token with the iframe, potentially exposing it to some JS code. Alternatively if we actually do the entire OIDC flow inside the iframe we must disable embedding prevention (X-Frame-Options, Content-Security-Policy) which is something you generally want to have on an identity provider to prevent clickjacking attacks.

Is there a cleaner way to set a partitioned cookie in this example? It seems like quite a common use case (OIDC authentication and vanity domains) and it seems unlikely this would be the simplest way to handle it.

krgovind commented 6 months ago

Thanks for the question, @jkosir!

Note that, at least for the near-term, your OIDC/redirect-based flow may become exemption from third-party cookie blocking due to some heuristics that we are deploying in Chrome. I would recommend verifying this by following these testing instructions. You may also qualify to request additional time to migrate away from third-party cookies by following this process.

For the long-term, we are hoping to build out a solution that may or may not be based on CHIPS; so thank you for the details on your use-case. We had previously considered the possibility of a partitioning scheme that accounts for popup/redirects, but it would be really helpful if you could either advocate for such a solution on that thread, or open a new bug using https://goo.gle/report-3pc-broken.

UgoFantozzi commented 6 months ago

Thanks for the update @krgovind

Note that, at least for the near-term, your OIDC/redirect-based flow may become exemption from third-party cookie blocking due to some heuristics that we are deploying in Chrome.

Am I understanding correctly, that auth cases that involve a redirect will remain to be out-of-the-box supported, even if the user is part of the 1% that have third-party cookies deprecated? Can we rely on this exemption until Chrome exposes a dedicated reliable solution for such cases?

krgovind commented 6 months ago

Am I understanding correctly, that auth cases that involve a redirect will remain to be out-of-the-box supported, even if the user is part of the 1% that have third-party cookies deprecated? Can we rely on this exemption until Chrome exposes a dedicated reliable solution for such cases?

Yes, you can rely on this solution for auth cases for the 1% experiment group which will have third-party cookies blocked. However, I'd recommend that you test out the heuristics using these instructions to make sure that they do unbreak your user flow.

In terms of long-term solutions, note that we recently published some guidance for identity use-cases here. I was curious if you have explored using the FedCM API?

UgoFantozzi commented 6 months ago

@krgovind Thanks for the feedback. Is there anywhere official statement that such cases will remain to be supported, even if the user is part of the 1% with deprecated 3rd party cookies? We would like to be transparent with our customers and want to include this in our documentation.

In regards to the FedCM API - we have checked it with the dev team and here is the feedback.

Federated Credential Management API looks like a cool feature, but it is rather a feature for Identity Providers. It doesn’t have the flexibility that we need. One of the blockers for us is that upon Browser Implementation, in order to support FCM in example.com, one of the first steps is to add /.well-known/web-identity endpoint on the eTLD+1 of the IdP, thus, imagine we have test.example.com as a prod Example instance name, we would need to implement this endpoints for example.com and this is how it works. https://developers.google.com/privacy-sandbox/3pcd/fedcm-developer-guide#well-known-file

We are a SaaS company where B2B2C is the most common use case. As a result, we cannot affect a lot on parent applications in which our product is embedded, nor on their identity provider. Let me know if you need more details.

npm1 commented 6 months ago

I don't follow exactly what issues are. Can you expand either here or in a new issue in the FedCM repo? In particular, I'd be interested to know more details about what is the service you provide to users and what breaks when you cannot access third party cookies. Are you working with jkosir or is your use-case totally separate?

mefril commented 5 months ago

Hello @npm1. I am working with @UgoFantozzi and we have encountered a separate use-case, which I will try to explain:

We sell a product to our customers that includes a back-end, authentication management, and a web UI that can be embedded into their sites. Here is what happens when a user configures our app to use Single Sign-On (SSO):

Our product (ourProduct) is deployed by our customer at their desired location, 1 per customer (https://product.domain.com). Our customer, who uses our product, embeds it into their websites (customerSite, https://customer.site.com). The end-user uses the whole application, and each customer has a personal SSO identity provider (https://oidc.provider.com), configured by them and used within ourProduct for user authentication. With this setup, here is the login flow:

  1. The user tries to open https://customer.site.com, which attempts to load https://product.domain.com as third-party content.
  2. If the user is not authenticated, the entire browser page is redirected to the login page of the SSO provider: https://oidc.provider.com.
  3. After successful login at https://oidc.provider.com, the user is redirected to the oidc_callback endpoint: from https://oidc.provider.com to https://product.domain.com/oidc_callback.
  4. The oidc_callback endpoint validates the user in ourProduct and sends a 302 redirect to https://customer.site.com, setting cookies for https://product.domain.com. However, this poses a problem because these are third-party cookies, and CHIPS doesn't resolve this issue. When we add the "partitioned" attribute to the cookies, the browser sets the partition key to "https://product.domain.com", not "https://customer.site.com" as we expect. So one more time: we send a GET request to https://product.domain.com, and the response is a 302 redirect with the following headers:

Regarding FedCM: During the implementation of our Proof of Concept (POC), I attempted to use ourProduct as the "identity provider" as per the tutorial. This required implementing all the necessary endpoints according to the documentation. The first obstacle I encountered is that the /.well-known/web-identity endpoint needs to be provided on the eTLD+1 of the IDP, which is "https://domain.com" in the case of our app being deployed at "https://product.domain.com". This is a part we cannot control, thus we cannot use this solution.

npm1 commented 5 months ago

If I understand correctly, https://product.domain.com is not really a user-facing site, is that correct? It seems to me like this could potentially be solved by using FedCM from within an iframe:

A couple other comments:

cfredric commented 5 months ago

I'm not sure I follow the CHIPS issue so I'll let someone else chime in on that if needed.

Re: the CHIPS issue, my understanding of the observed behavior is that if a 302 redirect sets a partitioned cookie, the partition key is computed from the site that served the 302, rather than the site that is listed in the Location header (i.e. the destination of the redirect). @mefril is that accurate? This sounds like it is working-as-intended to me, but it would be nice if @DCtheTall could confirm.

It is not clear to us why you say you may not modify https://domain.com/. Why is your product hosted in an eTLD+1 that you cannot control? That seems strange.

This sounds like an "authenticated embed" use case to me. This is the kind of thing that the Storage Access API is meant for, when the iframe needs fully-unpartitioned cookies.

@mefril would the Storage Access API (SAA) be an option for you here? SAA has very different privacy properties than CHIPS (since SAA allows joining user identity across sites, and CHIPS doesn't provide that capability), so SAA involves a user permission prompt. But if you're currently relying on cross-site data sharing between vanity.com and hub.ourapp.com, it sounds like you need access to that data from your iframe, anyway.

mefril commented 4 months ago

if a 302 redirect sets a partitioned cookie, the partition key is computed from the site that served the 302, rather than the site that is listed in the Location header (i.e. the destination of the redirect). @mefril is that accurate

it is correct, maybe it is expected behaviour, I'm just trying to find any other way to manage 3rd-party cookies in a such case

would the Storage Access API (SAA) be an option for you here

Thanks for advice, did some investigation, looks like "requestStorageAccess" is only works with an iframe, which is not our case. I see there is a "requestStorageAccessFor" method that looks like just what I need, however I didn't manage it to work. Every time I am trying to get "requestStorageAccessFor" inside user's interaction, I am having "DOMException: requestStorageAccessFor not allowed" with no detailed explanation. Checked the documentation and looks like the only reason it may not work is "The top-level and embedded sites are not in the same Related website set" (In this step documentation is a bit misleading, as I understood it should show "prompt" in a general case and skip a prompt if websites are in the same "Related website set").

arichiv commented 2 months ago

Chrome is considering a new proposal which might help with this use case: https://github.com/arichiv/partitioned-popins