Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

requestStorageAccess for fetch? #53

Closed
colinclerk opened this issue Jul 23, 2020 · 11 comments
Closed

requestStorageAccess for fetch? #53

colinclerk opened this issue Jul 23, 2020 · 11 comments
Labels
future Will consider for a future revision

Comments

@colinclerk
Copy link

I would be interested in a requestStorageAccess mechanism for fetch() calls made to third parties. We're building an embeddable javascript widget that would like to fetch() from a third party with {credentials: "include"}, but we don't use any iframes.

It looks like we can create an iframe, requestStorageAccess from within, then proxy to the iframe's instance of fetch() via postMessage, but it would be nice if we could shortcut that process with a requestStorageAccess mechanism for our embedder's instance of fetch().

For extra clarity:

  • embedder.com embeds service.com/widget.js
  • The code in widget.js makes a fetch() request to service.com/api/* with {credentials: "include"}

Third party cookies blockers prevent widget.com from receiving cookies, regardless of if the cookie has SameSite=None. We'd like to override the blocker with explicit user permission, so SameSite=None cookies are included with the request.

Please let me know if this isn't clear!

@johnwilander johnwilander self-assigned this Jul 23, 2020
@johnwilander
Copy link
Collaborator

Hi! Thanks for filing.

document.requestStorage() requires a user interaction in the frame that's making the request. This makes sure that there's no insta-prompting on page load and that there's a signal of user intent when the prompt for storage access is shown (the user interacted with the content that's requesting storage access). Did you have thoughts on how that should work with storage access for fetch?

@colinclerk
Copy link
Author

For our use case we're happy to maintain the requirement that requestStorage is tied to a user gesture event.

The event will occur in our embedder's document instead of ours, so semantically the "requestStorage" name wouldn't make sense any more, but I believe the requirements can hold.

@annevk
Copy link
Collaborator

annevk commented Jul 24, 2020

So the scenario is that A embeds B, but you want storage access for C? It's probably possible to devise a scheme, but it would have to involve explicit consent from C.

@jackfrankland
Copy link

@colinclerk If I understand the result of this issue correctly: #3, once storage access has been granted to a third party, fetch requests from the top-level document to the same third party will also have storage access. This means that you will not need to proxy the third party fetch requests through postMessaging the iframe in order for the fetch to have credentials.

To gain storage access though, I believe the main reason for why the user action needs to be from the third party's iframe, rather than the top-level document, is to verify that the request is being made by the third party. There is currently no trustworthy way of verifying the provenance of script running in a first party context.

To carry on the idea further though, is there a potential solution with response headers from the third party being able to trigger the storage access request, provided other conditions are met?

@colinclerk
Copy link
Author

So the scenario is that A embeds B, but you want storage access for C? It's probably possible to devise a scheme, but it would have to involve explicit consent from C.

@annevk There should only be A and B

A is the first party doing the embedding.
B is a third party API on a different origin.

"storage access" is a misnomer here, since A never gains access to B's cookies, and B never gains access to A's cookies. Instead, A wants to make fetch requests to B, and wants the browser to send and receive B's cookies when those fetch requests are made.

The traditional approach to this is:

  • A triggers a fetch request to B with the credentials: "include" option (or XHR with withCredentials: true)
  • B whitelabels A's origin and the use of credentials via CORS, as well as ensuring cookies are set to SameSite=None when they are dropped.

But "storage access" has rightfully called out that coordination between A and B in this manner allows A and B to link identities without the user's consent. I would like the User to be able to offer consent for A to make a request to B that includes cookies.

Does this make more sense?

@colinclerk
Copy link
Author

colinclerk commented Jul 24, 2020

To gain storage access though, I believe the main reason for why the user action needs to be from the third party's iframe, rather than the top-level document, is to verify that the request is being made by the third party. There is currently no trustworthy way of verifying the provenance of script running in a first party context.

To carry on the idea further though, is there a potential solution with response headers from the third party being able to trigger the storage access request, provided other conditions are met?

@jackfrankland please read above and see if that helps clarify the use case at all. I believe the trust mechanism is already there through SameSite and CORS, specifically:

  • The third party must set cookies using the SameSite=None directive. They can only do this in a first-party context, or in a third party context if they implement CORS.
  • Implementing CORS requires the third party to whitelist the first party Origin explicitly (no wildcard allowed for credentialed requests), and requires they return Access-Control-Allow-Credentials: true.

Are these conditions sufficient to build trust, versus leveraging an iframe?

@colinclerk
Copy link
Author

@jackfrankland Sorry I didn't to respond to this part:

If I understand the result of this issue correctly: #3, once storage access has been granted to a third party, fetch requests from the top-level document to the same third party will also have storage access. This means that you will not need to proxy the third party fetch requests through postMessaging the iframe in order for the fetch to have credentials

It's unclear to me whether #3 (and the fix) applies to fetch or just multiple frames on the same page. The resolution of "per-page" vs "per-frame" definitely trends that direction, but I'm not seeing any explicit reference to fetch or other resources. I might just need to dive deeper, which I will try to do this weekend.

That is certainly helpful if it's the case, since it would cut out the proxy logic as you suggest. However, I'm not sure if it's a resolution, particularly if CORS and SameSite provide enough confidence that a host is expecting third party requests. If so, requiring the widget provider to use an iframe still feels like an unnecessary step.

@jackfrankland
Copy link

@colinclerk yes it's unclear to me too, and it would be good to get confirmation on this, or for it to be firmed up in the spec if not - currently Firefox still sends cookies with cross-origin requests (after storage access is granted), while WebKit does not.

As for the response headers... this is what I was thinking also (if not a new header altogether), along with possibly a new request option in the fetch call to specifically allow for access request if required. The new request option would only be valid upon user activation, and its presence would consume the user activation. No credentials will be sent with this fetch request, and the access request could only be able to occur after the response has been received of course, which I imagine could make this undesirable due to the potential delay. Alternatively, if a separate response has already been received from the third party that has given permission for access to be requested, then perhaps it should be possible to request the access with a new API call.

Please note that it's only my own current understanding of why the user action must be within the third party iframe, and I would be just as interested as you are to see if any extra measures like these for cross-origin fetches would be be deemed acceptable.

@colinclerk
Copy link
Author

colinclerk commented Jul 24, 2020

@jackfrankland Actually, yes, on second thought I want to challenge this idea more fundamentally:

I believe the main reason for why the user action needs to be from the third party's iframe, rather than the top-level document, is to verify that the request is being made by the third party

Is there any reason the request needs to be made by the third party? Why can't the first party want to consume the third party's API directly? At a high level, I see Storage Access prompts as a way of asking the user: "Can firstparty.com share your identity with thirdparty.com?"

I don't believe there's an advantage to be gained from firstparty.com asking for permission to share identity with arbitrary third parties. I'm not sure I understand how forcing thirdparty.com to acknowledge that it wants its own cookies (by way of an early iframe or an early HTTP header) adds additional protection.

Perhaps there's a way this concept offers better CSRF protection than what's afforded by CORS and SameSite, but I'd really want to understand the attack it's protecting against that CORS and SameSite don't, especially in a world where SameSite defaults to Lax.

@annevk
Copy link
Collaborator

annevk commented Aug 3, 2020

The third party needs to be involved as it receives (part of) the blame and holds the authority over the data involved.

@hober hober added the future Will consider for a future revision label Feb 22, 2022
@annevk
Copy link
Collaborator

annevk commented Oct 7, 2022

Let's dupe this into #107.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
future Will consider for a future revision
Projects
None yet
Development

No branches or pull requests

5 participants