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

Active or passive storage access after explicit user opt-in #2

Closed
johnwilander opened this issue Feb 5, 2020 · 31 comments
Closed

Active or passive storage access after explicit user opt-in #2

johnwilander opened this issue Feb 5, 2020 · 31 comments
Assignees
Labels
interop Tracking differences in existing implementations

Comments

@johnwilander
Copy link
Collaborator

Mozilla's documentation on differences between Safari's and Firefox's implementation covers the difference explained below.

In this case, we assume that the user has explicitly opted in to storage access for third-party social.example under first-party blog.example.

Safari: iframes from social.example under blog.example always need to get user interaction and call document.requestStorageAccess() on that interaction to get cookie access. This means a mere page load of blog.example with an iframe from social.example does not grant the social.example iframe storage access.

Firefox: iframes from social.example under blog.example do not need to get user interaction or call document.requestStorageAccess() to get cookie access for 30 days after the explicit user opt-in. This means a mere page load of blog.example with an iframe from social.example grants the social.example iframe storage access.

Safari is unlikely to allow what we refer to as "passive tracking" and we think user interaction with the authenticated embed should be required for storage access. Hence the title of this issue "Active or passive storage access."

However, we're open to making this behavior optional. To reach consensus on how to spec this, it would be helpful to get Mozilla's use cases and motivations.

@johnwilander johnwilander added the interop Tracking differences in existing implementations label Feb 5, 2020
@Brandr0id
Copy link
Member

When Edge has been looking at this API the decision to persist explicit access across page loads seemed beneficial to unblock embedded content not within an iframe during subsequent navigations as well as improve the user experience when navigating within the same top level site.

This may also tie in #3 as unblocking embedded content outside of an iframe would also imply that any grants are scoped to the page not the individual frame.

@johnwilander
Copy link
Collaborator Author

Hi!

When Edge has been looking at this API the decision to persist explicit access across page loads seemed beneficial to unblock embedded content not within an iframe during subsequent navigations as well as improve the user experience when navigating within the same top level site.

Do you have concrete examples of this benefit? I understand that third-party content that expects to have cookie access "works better" if it has cookie access but I'm looking for concrete user benefits.

Any thoughts on the passive tracking I mention? That was a key thing in our original thinking — the user should be able to load a new webpage and not be identified cross-site unless they engage with content from that cross-site provider.

This may also tie in #3 as unblocking embedded content outside of an iframe would also imply that any grants are scoped to the page not the individual frame.

Let's discuss that part in the dedicated issue since at least for us they are very different. Thanks!

@Brandr0id
Copy link
Member

Do you have concrete examples of this benefit? I understand that third-party content that expects to have cookie access "works better" if it has cookie access but I'm looking for concrete user benefits.

Hopefully this is more concrete :). One of the key aspects we looked at for this difference was if a user is on blog.example that has that embedded social.example content. If the content isn't fully functional because social.example doesn't have access to storage it will present differently; perhaps show them as not logged in or able to comment/view later.

The content can be unblocked by interacting with social.example, requestStorageAccess() is called and the content now "works" from the perspective of the user.

Now they reload blog.example or perform a navigation within the domain and there is the same or more content from social.example (perhaps even a tab reload or favourite navigation). If they are now signed out and the content no longer works it is a sub-optimal experience. Showing intent to use the content is great, key even, but this helps balance the impact so that the user can be in control of what they want to access but also not feel like things are broken or the site/browser is "forgetting" them and they have to "sign in, everytime".

Any thoughts on the passive tracking I mention? That was a key thing in our original thinking — the user should be able to load a new webpage and not be identified cross-site unless they engage with content from that cross-site provider.

I think a balance is required here. Once the user has granted access to the third party content they can be tracked by that entity in relation to the top level site. Does the third party get any more or meaningful information upon a refresh of the page vs the inconvenience of the user who expected the content to still be present/working? Trying to identify that boundary where the intended interaction ends seems to be key here and engagement with blog.example and/or social.example would be a good indicator.

Perhaps this also comes down to the scope of the grants. I believe Firefox has the notion of ephemeral grants as well which for the most part is what are dealt with. These only last 24h or the browser session vs the more permanent 30d grants. This would decrease the scope of how long social.example has access to storage when loaded on blog.example while also increasing the users experience.

For the longer grants I agree we want to be cautious here. An unfortunate scenario would be someone going to blog.example during one session, granting social.example access, then 25d later re-visiting it and having content allowed. This is a smaller set of cases but an important one. One thing we've been looking at is to potentially tie the extended grants to site-engagement of top level site, blog.example (or potentially even the embedded content social.example).

Potentially the logic could be something along the lines of "If explicit access to storage was previously granted via requestStorageAccess(), is still valid (time limit), and the user has recently visited/interacted with the top level page then grant on load".

@englehardt
Copy link

Our motivation for implementing the long-lived permission in Firefox was webcompat, ease of user experience for long-lived third-party integrations, and a question of whether we can hope to prevent long-lived passive tracking after the first time access is granted. Ultimately we'll need to decide whether we want to support passive third-party integrations. If so we'll also need to accept the risk of tracking. If not, we'll have to enforce quite a few constraints to provide meaningful protection against long-lived passive tracking, and it's not clear to me that such constraints can be enforced without severely limiting third-party integrations.

The "sign in everytime" example in #2 (comment) was also a concern for us. But we encountered login flows that were completely broken if third-party storage access was removed on navigation. Consider the case where example.com embeds a "Login with Social Provider" button from social.example. The user clicks the login button, social.example requests storage access and access is granted, the user approves the login on social.examples site via a popup, and the popup sends back an auth token.

In practice many sites will complete the login procedure immediately, but we observed some sites that would redirect after social.example's popup was closed. Those sites expected to be able to access social.example's API on future navigations. Removing storage access after navigation would cause the account setup procedure to break, leading to redirect loops or leaving profiles in a wonky state. IIRC Login with Facebook on IMDB was an example at the time (not sure if it still is).

We could consider telling sites that such an integration is no longer supported. This means that every time the site wants to interact with the third party's API the user will first need to click on an embedded iframe from the third party so the party can request storage access. Passive third-party integrations would be completely broken or require the site to constantly annoy the user (e.g., "click here to sync state with third party").

Let's say that we're comfortable killing such integrations and choose to require third parties to re-ask for storage access upon every new page visit. We want to be sure we are meaningfully protecting the user. As discussed in #3 (comment) many trackers already have first-party script access. So social.example requests storage access one time on example.com, access is granted, and they postMessage back their tracking identifier and have their script store it in first-party storage. They never need to ask for storage access again and can continue to passively track the user until first-party storage is cleared.

We could further defend against this by imposing restrictions on third-party scripts embedded in the first-party context (we have some ideas on how to do this). This is a hard problem to get right, but let's say we do. Are we sure that we can both support meaningful third-party integrations and prevent long-term passive tracking? Third-party integrations are almost certainly going to need share data with the main context of the page for benign reasons, so preventing that seem limiting. I could also imagine periodically clearing first-party storage when we detect such data sharing across contexts, but again that is will likely be quite limiting as benign sharing is common.

On a separate note, we are concerned that our long-lived permission is vulnerable to the types of attacks explored in this paper and are currently thinking through the practical implications.

@johnwilander johnwilander added the agenda+ Request to add this issue to the agenda of our next telcon or F2F label Mar 10, 2020
@hober hober removed the agenda+ Request to add this issue to the agenda of our next telcon or F2F label Mar 24, 2020
@wyattjoh
Copy link

At Coral, we're attempting to work around issues posed by recent changes in Safari, namely the blocking of all third party cookies by default.

The issue raised in #2 (comment) mirrors our currently situation directly. We have explored the postMessage method described in #2 (comment) for syncing authentication state to the first-party context, but would rather use this new API to provide a convenient and seamless experience for our users that respects privacy and security.

If anyone from this group would like any input from a third party social comment provider (https://github.com/coralproject/talk) feel free to reach out to us!

@johnwilander
Copy link
Collaborator Author

At Coral, we're attempting to work around issues posed by recent changes in Safari, namely the blocking of all third party cookies by default.

The issue raised in #2 (comment) mirrors our currently situation directly. We have explored the postMessage method described in #2 (comment) for syncing authentication state to the first-party context, but would rather use this new API to provide a convenient and seamless experience for our users that respects privacy and security.

If anyone from this group would like any input from a third party social comment provider (https://github.com/coralproject/talk) feel free to reach out to us!

Hi! In what way does per-page access stop your commenting service from working? I assume the user has to engage with the commenting widget to comment, at which point requestStorageAccess() can be called.

@hober hober added the agenda+F2F Request to add this issue or PR to the agenda for our upcoming F2F. label Apr 27, 2020
@wyattjoh
Copy link

Once a user has interacted with the widget by logging in to comment, a top level navigation causes the problem. Because storage access is revoked, we cannot show the user that they are still "logged in" when they get to the new page because we need to request storage access again.

To the user, this appears as showing the interface "logged out" and requiring them to click "Sign in" again even though they just signed in before.

Imagine the user has logged in, and chooses to view a comment on another story, which causes a navigation to occur. Instead of being able to react/comment immediately, they now have to click "Sign in" first before we can even determine if the user is already signed in.

@jameshartig
Copy link

Yeah, we're storing a top-level session cookie to workaround this problem. So that once you "log in" inside of the iframe, we store that state on top-level origin instead of on the third-party iframe origin. Then if you refresh/navigate we use the top-level login state instead of using the third-party state.

@johnwilander
Copy link
Collaborator Author

Once a user has interacted with the widget by logging in to comment, a top level navigation causes the problem. Because storage access is revoked, we cannot show the user that they are still "logged in" when they get to the new page because we need to request storage access again.

To the user, this appears as showing the interface "logged out" and requiring them to click "Sign in" again even though they just signed in before.

Imagine the user has logged in, and chooses to view a comment on another story, which causes a navigation to occur. Instead of being able to react/comment immediately, they now have to click "Sign in" first before we can even determine if the user is already signed in.

This is not the case, at least not in WebKit’s implementation.

When a page is loaded, the third-party doesn’t know if the user is logged in or not. That does not mean it has to show “Sign In.” Instead it can show its commenting field and as soon as the user taps/clicks it, document.requestStorageAccess() is called.

From that point four things can happen:

  1. Access is immediately denied because the user is not logged in (has not interacted with the third-party as first party). In this case, the user gesture is preserved, allowing the widget to take the user to their website for login.
  2. Access is explicitly denied by the user. The user clearly doesn’t want to use the widget. The gesture is consumed.
  3. Access is explicitly granted by the user. Cookie access is opened and the widget can check the login status. The gesture is preserved and so if the cookie status shows the user not being logged in, the widget can take them to their website for login. If the user is logged in already, they can go ahead and comment.
  4. Access is immediately granted because of prior explicit granting. This is where your example ends up after that page navigation. Cookie access is immediately opened and the widget can check the login status. The gesture is preserved and so if the cookie status shows the user not being logged in, the widget can take them to their website for login. If the user is logged in already, they can go ahead and comment.

@wyattjoh
Copy link

wyattjoh commented Apr 28, 2020

When a page is loaded, the third-party doesn’t know if the user is logged in or not. That does not mean it has to show “Sign In.” Instead it can show its commenting field and as soon as the user taps/clicks it, document.requestStorageAccess() is called.

Maybe this is related to our current UI implementation, but this still doesn't solve the issue at hand. Our current implementation shows a "Signed in as {{ username }}" along with automatically hiding users that the logged in user has "ignored". We also present the "Respected" badges on comments that the current user has already clicked "Respect". The user might navigate, or refresh, just to see the interaction they left being wiped away, driving some user confusion.

Without being granted access to the storage after a top level navigation, we cannot present the user with an experience that shows that the user is still signed in.

This is our particular case, and maybe that's just a problem for us to solve. I expect however that it is more common than you think that if a user has granted access before (and signed in), after a navigation, the inability to display a logged in state would be detrimental to the user experience.

@jackfrankland
Copy link

I wonder if scenarios like @wyattjoh's will encourage third parties to store more data in the first party. I would hope that it wouldn't get to the point that implementations stored auth tokens, but I can see user preferences and maybe more personal information like a username/email address getting stored, for the sake of a nicer UX. This would be a privacy issue itself of course, as it opens the data up to other parties.

@johnwilander
Copy link
Collaborator Author

johnwilander commented Apr 28, 2020

When a page is loaded, the third-party doesn’t know if the user is logged in or not. That does not mean it has to show “Sign In.” Instead it can show its commenting field and as soon as the user taps/clicks it, document.requestStorageAccess() is called.

Maybe this is related to our current UI implementation, but this still doesn't solve the issue at hand. Our current implementation shows a "Signed in as {{ username }}" along with automatically hiding users that the logged in user has "ignored". We also present the "Respected" badges on comments that the current user has already clicked "Respect". The user might navigate, or refresh, just to see the interaction they left being wiped away, driving some user confusion.

Without being granted access to the storage after a top level navigation, we cannot present the user with an experience that shows that the user is still signed in.

This is our particular case, and maybe that's just a problem for us to solve. I expect however that it is more common than you think that if a user has granted access before (and signed in), after a navigation, the inability to display a logged in state would be detrimental to the user experience.

I think this comes down to old world view vs new world view. The "Show the Sign In button when we have zero cookies" UI was most probably built under the assumption that if the user is logged in, the third-party iframe will know because third-parties have access to cookies. If the assumption instead is that third-parties don't have access to cookies, one would have to build the iframe's UI accordingly, i.e. "We won't know whether or not the user is logged in until they interact with our iframe."

Even if storage access were to be persisted over some period of time, say an hour, or for some scope like the life time of the tab, the iframe would still end up in this situation over and over. No cookies, what to show?

We've discussed this as part of WebKit's IsLoggedIn proposal:

navigator.isLoggedIn() –> Promise<bool>

This last API could potentially be allowed to be called by third-party iframes that do not currently have access to their cookies and website data. The iframes may want to render differently depending on whether the user is one of their logged in customers or not.

However, this opens up for both fingerprinting and even a super cookie built with a navigator.isLoggedIn() bit vector similar to HSTS super cookies.

@englehardt
Copy link

englehardt commented Apr 29, 2020

Yeah, we're storing a top-level session cookie to workaround this problem. So that once you "log in" inside of the iframe, we store that state on top-level origin instead of on the third-party iframe origin. Then if you refresh/navigate we use the top-level login state instead of using the third-party state.

@fastest963 would you mind to elaborate on why you use a top-level cookie instead of partitioned third-party DOM storage (which, as I understand it, isn't blocked in Safari even when storage access has not been granted)? It seems cleaner/safer to use the partitioned storage since it won't be accessible to other scripts in the first-party context. I'm wondering if there are other considerations I'm not seeing.

I wonder if scenarios like @wyattjoh's will encourage third parties to store more data in the first party. I would hope that it wouldn't get to the point that implementations stored auth tokens, but I can see user preferences and maybe more personal information like a username/email address getting stored, for the sake of a nicer UX. This would be a privacy issue itself of course, as it opens the data up to other parties.

This is exactly my concern. We want less third-party scripts embedded into the main context of the page, and less third parties using first-party storage. There's a real risk of incentivizing that if we go with short-lived storage. And for what benefit? Like I discussed in #2 (comment) a motivated third party can continue to track users after their storage access grant has expired.

@johnwilander the only way I see short-lived access providing a privacy benefit is if we have future plans to make all storage less permanent (first-party and partitioned third-party). I know Safari has reduced script-writable storage down to 7 days under certain conditions, but this still means trackers can use this storage to passively track users for 7 days after receiving a short-lived storage access grant.

Thus, it seems like extending the lifetime of a storage access grant to match the lifetime of the storage locations that can otherwise be used to trivially circumvent the short requestStorageAccess lifetime provides both the same level of protection and a much nicer UX for legit embedded applications.

@jameshartig
Copy link

@fastest963 would you mind to elaborate on why you use a top-level cookie instead of partitioned third-party DOM storage (which, as I understand it, isn't blocked in Safari even when storage access has not been granted)? It seems cleaner/safer to use the partitioned storage since it won't be accessible to other scripts in the first-party context. I'm wondering if there are other considerations I'm not seeing.

Our product provides a way for publishers to add subscriptions to their site with just a script tag. When a visitor to first-party does not have a subscription we would show a paywall with a button to subscribe via iframe (or popup). In that third-party context we call requestStorageAccess so we can check to see if the user is already logged into our origin, and if they are, use their saved payment information to complete the transaction. It would be unacceptable for the user to see that same paywall on every single refresh of first-party, so we store a cookie on the first-party that our JS can check to see if the user has a subscription.

Ideally we could use partitioned storage so other top-level scripts wouldn't have access to our cookie but I'm not sure how that would work. We would always need to make some sort of "stub" iframe so we can get access to the partitioned storage. Additionally, if the third-party iframe was granted storage access then we'd lose access to the partitioned storage so after they subscribe I'm not sure how we'd store something in the partitioned storage for later use.

This is exactly my concern. We want less third-party scripts embedded into the main context of the page, and less third parties using first-party storage. There's a real risk of incentivizing that if we go with short-lived storage.

I agree with this statement but it's not just a problem with the lifetime of the storage.

@johnwilander
Copy link
Collaborator Author

johnwilander commented Apr 29, 2020

Yeah, we're storing a top-level session cookie to workaround this problem. So that once you "log in" inside of the iframe, we store that state on top-level origin instead of on the third-party iframe origin. Then if you refresh/navigate we use the top-level login state instead of using the third-party state.

@fastest963 would you mind to elaborate on why you use a top-level cookie instead of partitioned third-party DOM storage (which, as I understand it, isn't blocked in Safari even when storage access has not been granted)? It seems cleaner/safer to use the partitioned storage since it won't be accessible to other scripts in the first-party context. I'm wondering if there are other considerations I'm not seeing.

I wonder if scenarios like @wyattjoh's will encourage third parties to store more data in the first party. I would hope that it wouldn't get to the point that implementations stored auth tokens, but I can see user preferences and maybe more personal information like a username/email address getting stored, for the sake of a nicer UX. This would be a privacy issue itself of course, as it opens the data up to other parties.

This is exactly my concern. We want less third-party scripts embedded into the main context of the page, and less third parties using first-party storage. There's a real risk of incentivizing that if we go with short-lived storage. And for what benefit? Like I discussed in #2 (comment) a motivated third party can continue to track users after their storage access grant has expired.

@johnwilander the only way I see short-lived access providing a privacy benefit is if we have future plans to make all storage less permanent (first-party and partitioned third-party). I know Safari has reduced script-writable storage down to 7 days under certain conditions, but this still means trackers can use this storage to passively track users for 7 days after receiving a short-lived storage access grant.

Thus, it seems like extending the lifetime of a storage access grant to match the lifetime of the storage locations that can otherwise be used to trivially circumvent the short requestStorageAccess lifetime provides both the same level of protection and a much nicer UX for legit embedded applications.

This was absolutely considered back when we decided to scope granted access to the lifetime of the requesting iframe (as long as it stays same-site). There are two conflicting goals here.

One is that the user is engaging with a specific piece of embedded third-party content, like a video or a commenting widget from social media, and granting that content access to cross-site identifiers. That grant does not imply that the user now wants the video or social media provider to re-identify them on all pages from this website for one hour, one day, or the life time of the tab. Such re-identifying is what we sometimes refer to as "passive cross-site tracking" — the user just loads a new webpage and a third-party identifies them. We want to get rid of all passive cross-site tracking and put the user in control and therefore implemented the storage access scope accordingly.

The other one involves the site owner giving the embedded third-party further powers over their website, such as script running in the first party context. In such a case, the third-party can migrate a user identity into the first-party context and achieve passive cross-site tracking across page loads, tabs, and browser restarts. That is an existing problem, not tied to the Storage Access API.

The conflict is between explicitly allowing passive cross-site tracking through the Storage Access API on the one hand, and incentivizing more third-party scripts in the first party context + more cross-site identities in first party storage on the other.

They are different in nature. The first one is about designing a new API and meeting user expectations (a positive, potentially naive world view). The second one is about looking reality in the eye and not incentivizing something bad (a negative, potentially defeatist world view).

It's a tough nut to crack. Maybe the prompt for storage access needs to offer three things: Don't allow, Allow for this page, Allow for this browsing session. Of course formulated by someone with UI skills.

@awicklander
Copy link

A number of our customers, and their customers, have been negatively affected with the release of the latest version of Safari. After reading through this thread, I wanted to highlight the issues we're seeing, make the case that the current implementation is too restrictive, and I would argue, causes harm to "the independent web".

We (Tula Software) are a software application that yoga studios use to run their businesses. We handle class registrations and bookings, payments, and general account management for a studio's students.

One of our key principles is to not get in between our customers and their customers. We are not a marketplace, nor do we ever want to become a marketplace. We want to help independent yoga studio owners maintain their brands, maintain the relationship with their customers, all while providing them with access to the most modern internet tools available.

One of the primary ways we do this is with widgets, which our customers install on their websites. They embed calendar, payment form and student account widgets, each into an individual page on their sites. This provides their students with an app-like experience of being logged in when registering for class, checking account information, making payments, etc.

The latest updates break all of this core functionality.

Reading Data Is User Interaction
One of the arguments I'm reading in support of the current implementation is that third-party cookies can be accessed with user interaction. It is correct that for some interactions, like registering for a class, this can be easily achieved. But this leaves out the fact that often times users interact with an application simply by reading data in the application.

Reading data is user interaction, and the current implementation does not take this reality into account.

On the calendar where people register, we display the number of credits remaining on their accounts. This is useful and valuable information before taking the registration action, when you're thinking about what classes to register for for the week ahead.

Likewise, if I'm on a yoga studio website, and I visit the "my account" page, I expect to see my account information, which will contain my past attendances, past purchases, and upcoming registrations, especially if I just previously logged in on the calendar page.

I suppose when reading data is the way the user is interacting with the application, one option is to regularly disrupt the end user's experience, and ask them to press a "load my account" button or similar action. But this creates a situation where we cannot create for our merchants an experience that is as good as fully logged in all the time marketplace experience.

As an example, imagine if every time you went to book an airbnb, you had to press a "load my account" button on any page where you wanted to see your payment information, view your purchase history, view your booking history, or read through your message thread with a host.

This is the situation the latest version of safari creates for our customer's students.

Ultimately, the problem is that the latest updates give large marketplaces a competitive User Experience/User Interface advantage over platforms like ours that help independent businesses thrive, in an environment where big tech is already trying to commoditize them as much as possible.

I appreciate the work you are doing, and am grateful for the opportunity to comment about this.

Thank you.

@johnwilander
Copy link
Collaborator Author

A number of our customers, and their customers, have been negatively affected with the release of the latest version of Safari. After reading through this thread, I wanted to highlight the issues we're seeing, make the case that the current implementation is too restrictive, and I would argue, causes harm to "the independent web".

We (Tula Software) are a software application that yoga studios use to run their businesses. We handle class registrations and bookings, payments, and general account management for a studio's students.

One of our key principles is to not get in between our customers and their customers. We are not a marketplace, nor do we ever want to become a marketplace. We want to help independent yoga studio owners maintain their brands, maintain the relationship with their customers, all while providing them with access to the most modern internet tools available.

One of the primary ways we do this is with widgets, which our customers install on their websites. They embed calendar, payment form and student account widgets, each into an individual page on their sites. This provides their students with an app-like experience of being logged in when registering for class, checking account information, making payments, etc.

The latest updates break all of this core functionality.

Reading Data Is User Interaction
One of the arguments I'm reading in support of the current implementation is that third-party cookies can be accessed with user interaction. It is correct that for some interactions, like registering for a class, this can be easily achieved. But this leaves out the fact that often times users interact with an application simply by reading data in the application.

Reading data is user interaction, and the current implementation does not take this reality into account.

On the calendar where people register, we display the number of credits remaining on their accounts. This is useful and valuable information before taking the registration action, when you're thinking about what classes to register for for the week ahead.

Likewise, if I'm on a yoga studio website, and I visit the "my account" page, I expect to see my account information, which will contain my past attendances, past purchases, and upcoming registrations, especially if I just previously logged in on the calendar page.

I suppose when reading data is the way the user is interacting with the application, one option is to regularly disrupt the end user's experience, and ask them to press a "load my account" button or similar action. But this creates a situation where we cannot create for our merchants an experience that is as good as fully logged in all the time marketplace experience.

As an example, imagine if every time you went to book an airbnb, you had to press a "load my account" button on any page where you wanted to see your payment information, view your purchase history, view your booking history, or read through your message thread with a host.

This is the situation the latest version of safari creates for our customer's students.

Ultimately, the problem is that the latest updates give large marketplaces a competitive User Experience/User Interface advantage over platforms like ours that help independent businesses thrive, in an environment where big tech is already trying to commoditize them as much as possible.

I appreciate the work you are doing, and am grateful for the opportunity to comment about this.

Thank you.

Thanks for your comments! We appreciate hearing about more use cases.

It is always useful to keep both good and bad use of technical capabilities in mind. While looking at a single website case, it may seem like users obviously want a particular third-party to have access to storage. But things become very different if you consider all the websites that use this third-party and were to present users with the fact that this third-party can follow them for instance when they are comparing competing businesses, and leverage that information to sway them in a particular direction, rig pricing, or deduce things about their lives (Are they moving since this is a new location they're looking at? Have they gotten a raise given the higher price of this new service they're looking at? Have they become parents given that the new place has a child care service?).

For a specific first party website, the issue tracked here is whether or not to extend the storage access for a third-party beyond the lifetime of the iframe that was granted storage access. I think we will land in some optionality for browsers here but the closer browsers can get to each other, the easier for developers.

In your use case, what would be the minimal reasonable scope to resolve your issue? In various discussions, these options have been mentioned:

  • For one hour.
  • For 24 hours.
  • For the lifetime of the browser tab.
  • Until browser restart.
  • For 30 days (current Firefox behavior AFAIK).

@awicklander
Copy link

But things become very different if you consider all the websites that use this third-party and were to present users with the fact that this third-party can follow them for instance when they are comparing competing businesses, and leverage that information to sway them in a particular direction, rig pricing, or deduce things about their lives

I agree and fully understand, and finding balance is important indeed. Currently, the implementation suggests there is no such thing as a trusted third party, and that no third party can be trusted. I do not believe this is the case in the real world.

In your use case, what would be the minimal reasonable scope to resolve your issue?

Thanks for asking!

  • One hour time-frame would at least fix the "this is broken" feeling we have now.
  • 24 hours would certainly be sufficient
  • My personal preference would be 30 days

Appreciate the dialog very much, thanks again.

@johnwilander
Copy link
Collaborator Author

But things become very different if you consider all the websites that use this third-party and were to present users with the fact that this third-party can follow them for instance when they are comparing competing businesses, and leverage that information to sway them in a particular direction, rig pricing, or deduce things about their lives

I agree and fully understand, and finding balance is important indeed. Currently, the implementation suggests there is no such thing as a trusted third party, and that no third party can be trusted. I do not believe this is the case in the real world.

The Storage Access API is about establishing trust, for some definition of trust. The prompt asks the user and the user gets to decide if they grant the third-party storage access. However, in the case of browsers blocking third-party storage access by default, there is no implicit trust in third parties.

In your use case, what would be the minimal reasonable scope to resolve your issue?

Thanks for asking!

* One hour time-frame would at least fix the "this is broken" feeling we have now.

Just so that I understand, are you in this broken state after you've made changes in light of third-party cookie blocking or is it broken because of the old assumption that you would always have third-party cookie access? I.e. have you adopted the Storage Access API and designed the UI for it or is that yet to be done?

* 24 hours would certainly be sufficient

* My personal preference would be 30 days

Would three options in the prompt work for your customers' users do you think? Kind of "Don't Allow," "Allow on this page," and "Allow for one day."

@jameshartig
Copy link

Would three options in the prompt work for your customers' users do you think? Kind of "Don't Allow," "Allow on this page," and "Allow for one day."

All 3 of those options only grants access to the combination of top-level+third-party, correct? Or does Allow for one day grant access across top-level origins?

Rather than this page I think this session might be better so if you leave the current page and go to another on the same site, you don't have a logged out widget. If you have this session it might remove the need for one day (given my assumption above is correct).

@jackfrankland
Copy link

Bringing back a suggestion I made in the other thread a while ago: #4 (comment).

To take it a little further, if:

  • permanently partitioned DOM Storage were available to the third party in an iframe (either before granting access, or only for, say, 1 day after granting storage access),
  • document.cookie is always blocked or read-only after storage access is granted,
  • granted storage access allows cookies to be read and set in network requests only, and still requires user action per iframe document

then a third party can use DOM storage to store UI / state data (making the experience nicer on subsequent loads), avoiding pollution on the first party. A user action will always be required per iframe for authenticated sessions to be resumed.

Would relaxing access to only partitioned DOM storage give trackers a reasonably better chance of tracking someone? This matches the current behaviour in Webkit I believe.

@awicklander
Copy link

Just so that I understand, are you in this broken state after you've made changes in light of third-party cookie blocking or is it broken because of the old assumption that you would always have third-party cookie access? I.e. have you adopted the Storage Access API and designed the UI for it or is that yet to be done?

We are in the broken state because of the old assumption that we would have third party access. Also, all efforts to update the UI to take into account the "new world" all lead to a sub-optimal, non-competitive user experience as previously outlined.

Would three options in the prompt work for your customers' users do you think? Kind of "Don't Allow," "Allow on this page," and "Allow for one day."

If these prompts were to become common and known behavior for internet users, yes.

@laughinghan
Copy link

@awicklander Hi, I don't mean this as a criticism, but hopefully as the start of a constructive conversation, but:

Ultimately, the problem is that the latest updates give large marketplaces a competitive User Experience/User Interface advantage over platforms like ours that help independent businesses thrive, in an environment where big tech is already trying to commoditize them as much as possible.

I don't think this is accurate. Shopify is also not a marketplace, they're a platform for independent businesses just like you, but they don't have this problem, do they?

I think the issue you're running into is related to a detail of your implementation. Not saying it's your fault, and I'm sorry this change broke your assumptions and creates a bunch of work for you, but it fixes an important privacy bug so I think it's here to stay.

It sounds like you allow your customers to embed your widgets in static sites? Then it sounds like you have three options:

  • have your customers run your code first-party, and just keep your server's session tokens in first-party storage
  • use an iframe and keep your server's session token in partitioned storage (@johnwilander this isn't going away, is it?)
  • require your customers to present a first-party interface to some of your server-side code, where you can manage first-party sessions for them. This has the advantage of allowing you to use HttpOnly cookies for better security
    • the easiest thing would be for them to CNAME a subdomain on their domain to your server, and then when they load your script on their page, pass that subdomain to your script somehow, whether by query parameter or attribute on the script element
      • (I'm assuming your customers have their own domain? If you control their domain, I assume you wouldn't be complaining here because this solution would've been obvious)
    • if some of your customers run their own servers, you could write a library in a few major languages for them to run server-side

Note that all of these will have the effect that a login session on one of your customer's sites will no longer translate to also being logged in on another customer's sites, because the whole point of all of this is for someone logged in on one of your customer's sites to be anonymous the first time they visit a different site. That doesn't sound like a problem for your use case though?

(I actually don't even use Safari, I use Chrome with third-party cookies blocked as my main browser, so your customers' sites are probably already broken for me. Please fix them, for people like me :) .)

@jackfrankland
Copy link

  • have your customers run your code first-party, and just keep your server's session tokens in first-party storage
  • use an iframe and keep your server's session token in partitioned storage

I think this represents the concern quite well that developers may choose to risk security to overcome limitations.

require your customers to present a first-party interface to some of your server-side code

In a lot of situations it is unrealistic due to the development cost and infosec required, and the nature of the embed. Even if the service were an integral part of the site, it is much more realistic that sites will be able to integrate server-side with Shopify, than it is a relatively less-known business with less reach, so the point about competitiveness is a valid one in my view.

@laughinghan
Copy link

developers may choose to risk security to overcome limitations

You really think this change makes a difference? You think people weren't already loading scripts from third-party sources, without subresource integrity, and instead were carefully segregating third-party widgets into iframes?

(And I did suggest staying with in the iframe and using partitioned storage, which I believe is still allowed inside hidden iframes, no user gesture required.)

In a lot of situations it is unrealistic due to the development cost and infosec required

A CNAME is really that onerous?

it is much more realistic that sites will be able to integrate server-side with Shopify, than it is a relatively less-known business with less reach, so the point about competitiveness is a valid one in my view

I don't see the relevance. Isn't it being easy to Shopify a counterexample, because Shopify also isn't a marketplace, it powers independent businesses? And why does being less-known or anything about Tula require them to be inherently harder to integrate-with than Shopify?

@hober hober removed the agenda+F2F Request to add this issue or PR to the agenda for our upcoming F2F. label Jul 6, 2020
@jameshartig
Copy link

We didn't get to discuss this during the meeting. If a browser only does active granting then it would be nice to see some resolution to #8. If you operate a widget that requires login to be functional, you might want to show an action button (Comment) if you know they won't see a dialog but if they will need to see a browser prompt in order to grant storage you might show something more informative (Login to comment) since it's their first time. The current "solution" presented in the other issue is that it should depend on isLoggedIn to know but it's not clear how that interacts with storage access and if/when that will be implemented/standardized.

@hober
Copy link
Member

hober commented Oct 8, 2021

It seems to me that, if a web developer follows the well-lit path (call hasStorageAccess() to figure out if you need to call requestStorageAccess(), and don't access cookies until you know you have been granted access), the current difference in behavior between WebKit and Gecko doesn't actually matter, and if they don't, they'll probably have other problems.

Maybe we can resolve this as-is, or maybe we should add some prose explaining there may be implementation differences here, but that they don't matter too much?

@johannhof
Copy link
Member

I talked about this with John and Tess and we agreed that we will mention in the spec that there may be implementation differences in how the user agent may choose to use the storage access map to decide whether a frame will have access to first-party storage and/or cookies. It may choose to always require a requestStorageAccess call or give access on load based on the existing entry in the storage access map.

For this reason it's important that developers use hasStorageAccess to check whether they already have storage access before requesting it.

This change will be made when updating the storage section as part of #31. I am hence (finally) closing this issue, thank you everyone.

@jameshartig
Copy link

jameshartig commented Oct 10, 2021

The original issue was discussing changes to Safari's implementation. John brought up potentially giving the user multiple options, "not now", "only once", "for a day", or something similar to address the passive vs active issue. I don't think the hasStorageAccess() path actually addresses the original issue. I think there is still a problem with various circumstances where forcing a user to click into the iframe on every pageview of a session won't be user-friendly. For instance, if you embed a third-party SaaS analytics application in your web app. As the user navigates the top-level site on every single page-load they would see something like "You're not logged in. Click here to login" which would be very frustrating. Another example is a chat widget like Intercom where you expect to have your chat conversation resumed despite top-level navigations or at least have the widget recognize a conversation is ongoing.

If I'm understanding Safari's implementation correctly, hasStorageAccess() will always report false until you call requestStorageAccess for that frame. This also doesn't address the problem outlined in #8 where an iframe might want to have 3 different paths: 1) never seen user before 2) seen before and previously granted 3) (firefox-only?) already seen and already granted. There's no way to distinguish between 1 and 2 with this API. Without a way to distinguish between them then a user will think something is broken if they're asked to login frequently and I don't think it's appropriate for a never-before-seen user to be immediately prompted to give storage access upon clicking into an iframe but that might be appreciate for a user that's previously granted a few minutes ago.

@johannhof
Copy link
Member

Right, I agree, and this is why Firefox differs in behavior here, but what I'm saying is that as editors we've decided that this isn't really a question for the spec and rather something that we will leave to individual browsers. In #31 we will add wording to that effect.

@ashubham
Copy link

ashubham commented May 4, 2022

I feel the spec should be a guiding principle for browsers to implement something which is consistent and makes app developer's life easier.

I concur what @jameshartig pointed out above. We are an embedded SaaS analytics application and for us it is impossible to work when 3rd party cookie access is blocked. It will be great if the spec at least laid out the use cases which are affected by this change, so that the browser implementers can make an informed choice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
interop Tracking differences in existing implementations
Projects
None yet
Development

No branches or pull requests