Open hpsin opened 3 years ago
Thanks for filing! We must consider misuse cases that are not login flows. Could social.example make all its outgoing links request cross-site storage access under the destination site and if the user doesn't grant access, deny navigation? That would be a case of forced cross-site tracking. Could this also enable a new form of bounce tracking where protections focused on URL parameters won't work?
A minimum level of protection would be to force the flow to start on the IDP website and have them explicitly opt-in to the subsequent storage access request. At that point, couldn't the third-party request storage access there and then instead? There might be remaining challenges around per-page storage access but that's a very different challenge than allowing requests for storage access under other websites than the current one.
Thanks John! That's great point about misuse, as it allows social.example to single-handedly, without coordination, create forced tracking. It'd be fairly obvious what they were doing, and I believe we use court of public opinion as a tempering vector here. Social.example would have headlines about forced tracking, presumably... That said - perhaps shielding the result of the promise from social.example could be possible? They get to know that the user closed the prompt but not what the result was.
I don't follow the bounce tracking concern - this would seem to negate certain bounce tracking techniques - it would either
In your minimum requirement, did you mean that the flow would need to start at the RP website with opt-in, before redirection to the IDP? This would be acceptable I believe- minimal updates required, that an SDK could do inline, rather than novel protocols. The requirement to build new auth protocols centered around Storage Access API that require UX and timing considerations is driving the issue here.
Would you consider a Firefox-esque automatic check, that the RP initiated the redirect to the IDP, as opt-in? __
Could social.example make all its outgoing links request cross-site storage access under the destination site and if the user doesn't grant access, deny navigation?
I'm not sure how this is different than what is possible today. In theory, a top-level application (first-party.com) could embed an experience (third-party.com) that requires storage access (e.g. 3p cookies), and the two could use frame messaging to communicate, such that first-party.com doesn't let the user proceed until they have granted storage access to third-party.com using the existing Storage Access API mechanics (document.requestStorageAccess
being invoked from third-party.com iframe inside first-party.com).
@jasonnutter that would require coordination between the two parties. The concern here is that social.example could block navigation through the web ecosystem on their own, without consent from news.example. Considering the number of sites very publicly opting out of FLoC, I expect the same desire to be present here.
@hpsin Sure, but would we consider that O(n+1)
or O(n^2)
, in terms of complexity? Needing those two parties to coordinate is not difficult, especially considering scenarios where an SDK from third-party.com is being used on first-party.com.
Thanks John! That's great point about misuse, as it allows social.example to single-handedly, without coordination, create forced tracking. It'd be fairly obvious what they were doing, and I believe we use court of public opinion as a tempering vector here. Social.example would have headlines about forced tracking, presumably...
There are many places in the world where we as browser vendors don't hear about the headlines, users don't know that a viable path is to go to media, users don't have a viable path to go to media, or users don't understand that they have a choice at all. The starting point for the web is "safe to roam" and that's what we need to maintain.
That said - perhaps shielding the result of the promise from social.example could be possible? They get to know that the user closed the prompt but not what the result was.
A similar misuse case happening in the wild and a similar discussion on remedies can be found here: https://github.com/privacycg/storage-access/issues/60
I don't follow the bounce tracking concern - this would seem to negate certain bounce tracking techniques - it would either
Allow storage access on an intermediate bounce tracker
- Useless, as it will not be a top level domain
- Very obvious (click a link to news.example, see a prompt asking if social.example can track you on tracker1.example)
- Require immediate navigation to news.example, bypassing bounce trackers.
Bounce tracking typically relies on link decoration or HTTP headers to transfer tracking information across websites. Browsers have an ability to step in and filter those things out. Being able to request storage access for SiteB under SiteA, while on SiteB, can make the subsequent navigation be tracking-free.
In your minimum requirement, did you mean that the flow would need to start at the RP website with opt-in, before redirection to the IDP? This would be acceptable I believe- minimal updates required, that an SDK could do inline, rather than novel protocols. The requirement to build new auth protocols centered around Storage Access API that require UX and timing considerations is driving the issue here.
Would you consider a Firefox-esque automatic check, that the RP initiated the redirect to the IDP, as opt-in? __
We typically don't discuss browser specific things here unless it's an active spec interop issue. Mozilla has chosen some specific compatibility measures. I don't know if they plan to keep them. WebKit has its popup compatibility measure which we have clearly said is going away. Apart from that, WebKit has not expressed a desire to add further, general compatibility measures.
This is starting to sound much more like our IsLoggedIn proposal for federated logins: https://github.com/privacycg/is-logged-in/issues/35. I do realize that you are talking about SSO, not federated logins, but that kind of a managed flow is what I'm after here. RP initiates, IDP authenticates+issues any tokens, RP confirms. If that was a flow managed by dedicated web APIs, we could look at making storage access smoother.
@hpsin Sure, but would we consider that
O(n+1)
orO(n^2)
, in terms of complexity? Needing those two parties to coordinate is not difficult, especially considering scenarios where an SDK from third-party.com is being used on first-party.com.
@johnwilander
The destination website may not want to coordinate. You may own a website that merely embeds an image from social.example and you never engage in any cross-site tracking. If social.example came and asked for coordination on tracking, you'd say no. You as a site owner would lose control of that if social.example could just do it themselves and force tracking on users who want to be able to follow a link to your website.
Fair points. What if this flow required explicit signal from both parties? For example, in addition to document.requestStorageAccessForRelyingParty()
mentioned above, there could be another new API , let's call it document.enableStorageAccessForThirdParty()
, which would be invoked by the relying party before redirecting to the IDP, which would be a requirement before invoking document.requestStorageAccessForRelyingParty
.
Example:
contoso.com
wants to sign into idp.com
, and knows it will need to potentially prompt the user for Storage Access for idp.com
.idp.com
, contoso.com
invokes document.enableStorageAccessForThirdParty('idp.com')
. This is a void method that sets a bit in the browser, which will be checked downstream in step 4.idp.com
, where they complete auth challenges.idp.com
invokes document.requestStorageAccessForRelyingParty('contoso.com')
. This API throws unless it is done as a direct result of user interaction and the origin provided has made the corresponding document.enableStorageAccessForThirdParty
call from step 2.idp.com
as if it was done from an iframe on contoso.com
.idp.com
receives a hanging/resolved/reject promise if the user declines, per #60.consotos.com
, and is able to load iframes for idp.com
that have access to the cookies set in first-party context.One idea for preventing abuse when a user rejects would be that document.requestStorageAccessForRelyingParty()
doesn't prompt the user until a redirect to the relying party is initiated, at which point the browser intercepts the request and injects the prompt, and regardless of the result, redirects the user. This gives the IDP the chance to provide context and set expectations with the user, but not the ability to prevent redirection if the user declines.
FWIW, to +1 a comment by @johnwilander, this has been chrome's position so far:
If that was a flow managed by dedicated web APIs, we could look at making storage access smoother.
In WebID's formulation, once the browser observes a login (e.g. via mediation or a permission prompt) the browser allows two APIs to be called:
navigator.credentials.logout()
(called from the IDP embedding RPs with cookies) andnavigator.credentials.refreshToken()
(called from the RP embedding the IDP with cookies)(API names largely TBD, but just to give you a sense of how we are approaching this problem)
So, to us, a lot of these problems (e.g. this one too) get a lot easier if the browser is able to observe a login, so most of our focus has been on finding ways to do that (and, like it was mentioned, the hard part is abuse).
Thank you for the proposal and the great discussion so far! I think this is a good approach to a real-world problem that SAA doesn't do very well at the moment. I'm trying to be careful with my initial optimism to leave time and space to consider possible abuse and other downsides of this proposal. I think John already identified two(?) valid concerns:
1) The API could allow for abuse by an "IDP" that consent-gates its outgoing links 2) By intention this proposal is targeted at a very specific use case (authentication) but its design does not make any restrictions to allow browsers to validate/observe that it's being used for authentication. Should this be part of SAA?
For 1), I think that the flow outlined in https://github.com/privacycg/storage-access/issues/83#issuecomment-861913160 should solve this, and it makes sense to require participation from both parties. I'd say we need to put some further thought into when exactly the user should be prompted in this scenario. Generally the only suitable async call happens when the IDP calls requestStorageAccessForRelyingParty
, but I can understand that we'd prefer to ensure a redirect actually happens before granting storage access.
For 2), Firefox already uses these heuristics (redirect/popups) in practice to deduce when authentication is happening. We would be happy to get rid of them eventually, but right now they're unavoidable. This proposal looks like it would be formalizing our heuristics into a web API, which will give us and our users more control over what's happening. It could ultimately reduce the number of sites we'd have to expose our heuristics to and give websites clear expectations and control over when storage access is granted.
This could also be solved by WebID, and the WebID flow is probably preferable in many cases. However, as we've said before, we're skeptical whether WebID will cover all use cases of SAA and also whether all providers will be able to adopt WebID or other types of browser-controlled auth flows. There are a lot of open questions that don't arise with generic access to cross-site cookies via user consent. As such, we want to work on evolving SAA to make it a pragmatic solution that can serve web developers today and in the future.
I think this approach is a step forward in that direction and if we can ensure participation from both sites and the ability for browsers to ask for user consent it doesn't seem to push the privacy boundaries of what is possible using iframes already, just makes the API more ergonomic to use. Again, I want to make sure we carefully consider this aspect.
Hey folks,
Thanks for the great discussions offline and at the CG. Recapping, we believe we've found two complementary ways of building this:
If two sites are in an FPS together, either one is allowed to request Storage Access directly. There's an implicit trust between them, so worries about maliciously blocking navigations between them should be reduced. Thus - IDP1 can call async requestStorageAccessForRelyingParty("rp1.example")
from a first party context, without rp1.example previously indicating it is allowed.
For sites not in a FPS, rp1.example must call sync allowStorageAccessRequestForThirdParty("idp.example")
prior to navigating to idp.example. This action tells the browser that the RP agrees with the prompt being shown.
Open questions still
requestStorageAccessForRelyingParty
Promise look like when the IDP is allowed to request access? I believe we can make it unblinded (return a reliable yes/no signal) since consent between the RP and IDP has been granted. requestStorageAccessForRelyingParty
Promise look like when the IDP is not allowed to request access? #60 likely directs this discussion. Other questions we should track? Naming and shape of API TBD of course.
Hi @hpsin, sorry for the delay here while I figured out our stance on this idea. From Mozilla side we're supportive of this idea overall (sans the FPS pieces, as FPS still seems to need to reach some consensus) and would be interested to get additional implementer support from WebKit/Blink folks.
Your suggestions for the "open questions" mostly make sense to me. This is up to the browser, but I think we'd customize our prompt to make it clear to the user that they will be redirected to the other site soon. So regarding 4), there's a bit of a breakage risk when the forward doesn't happen quickly enough if we make this time-based. It's a bit weird but I wonder if we should force a redirect to a specific URL after the promise was handled...
Declaring this in a privacy manifest would help it to be checkable/enforcable by regulators.
Clearing out the agenda+ as we discussed this in the last call. Please re-add if you want to discuss again.
Note: we are working on a complete API proposal at the Edge Explainers here: https://github.com/MicrosoftEdge/MSEdgeExplainers/pull/524
All feedback welcome, in particular on the shape of the API and the privacy implications of the response/availability of this API to the requesting site.
Any function like hasStorageAccessForSite(Url)
is risky since it would allow SiteA to read state that's under SiteB. Such access can a) leak data across sites, b) be leveraged for fingerprinting, and c) can be used to pressure or nudge the user to allow storage access on SiteB. SiteA should not have the power to read state under SiteB.
Proposal:
1) relyingParty.example
calls:
document.allowStorageAccessRequestOnSite(identityProvider.example);
2) relyingParty.example
waits for the promise to resolve.
3) relyingParty.example
navigates the user to identityProvider.example
.
4) identityProvider.example
calls:
document.requestStorageAccessUnderSite(relyingParty.example);
5) The user agent does what it wants to ensure that the user grants this permission, such as prompt the user or check for an existing opt-in.
6) identityProvider.example
navigates the user to relyingParty.example
.
7) The user agent either grants storage access for identityProvider.example
under relyingParty.example
automatically under its rules (for this browsing session, for N days, for this tab, etc) or requires identityProvider.example
to call document.requestStorageAccess();
in one of its iframes under relyingParty.example
after which storage access is granted without prompting the user.
If First Party Sets or something similar gets standardized, user agents are free to restrict the above flow to only sites within a single set.
We need to make sure that the calls to document.allowStorageAccessRequestOnSite(identityProvider.example)
and document.requestStorageAccessUnderSite(relyingParty.example)
are reasonably close in time. Otherwise identityProvider.example
can speculatively call document.allowStorageAccessRequestOnSite(identityProvider.example)
on all sites where it is running JavaScript in a first-party context and then set up the navigation gate/toll we discussed earlier. We may even have to make document.requestStorageAccessUnderSite(relyingParty.example)
not reveal to identityProvider.example
what the user's choice was. We absolutely don't want this to become a "give me third-party cookie access or you can't browse the web" vehicle.
Having the allowStirageAxxessRequestOnSite method creates an opportunity to give the user explanatory information inside any prompt which may result from the requestStorageAccessUnderSite, maybe specified via a plain text string parameter,
Because both sites take part it the behaviour, within a short period of time, there is less opportunity for users to be fooled with misleading text, and it will be helpful to for the user to know exactly what they are being asked to confirm.
Isn't FPS meant for sites with a common owner/controller? This is not usually the case with federated login interactions.
Having the allowStirageAxxessRequestOnSite method creates an opportunity to give the user explanatory information inside any prompt which may result from the requestStorageAccessUnderSite, maybe specified via a plain text string parameter,
Because both sites take part it the behaviour, within a short period of time, there is less opportunity for users to be fooled with misleading text, and it will be helpful to for the user to know exactly what they are being asked to confirm.
We've had several proposals to allow site-provided text in Storage Access API prompts, and always rejected them because of the misuse risk. There is no curation of the web beyond Safe Browsing and site-provided text in browser UI will not undergo any review or vetting. You can get all sorts of bad messaging in there such as "Your browser has been compromised please call (555)-GET-SAFE now!"
Isn't FPS meant for sites with a common owner/controller? This is not usually the case with federated login interactions.
That's true and I'm saying user agents should be free to limit this kind of forwarding to single sign-on and require other means for federated logins such as WebID (or what it's called nowadays). Linking user IDs across two sites in the same first-party set is very different from linking user IDs across two sites from different owners/controllers. It should be possible for user agents to have different requirements there.
Thanks @johnwilander , @michael-oneill!
You covered my question about 7 -
We may even have to make document.requestStorageAccessUnderSite(relyingParty.example) not reveal to identityProvider.example what the user's choice was.
Selfishly as the IDP owner, I'd like to be able to give that UX directly to the user during auth - provide the message that yes, the app really does need line of sight to the login server to function best. That said, this should be self healing - the SDK will attempt silent auth, and if no cookies are available, error and send the user back to the login domain, to try once more. We may need to keep a counter on the IDP domain to update the message to help explain to the user what went wrong.
We need to make sure that the calls to document.allowStorageAccessRequestOnSite(identityProvider.example) and document.requestStorageAccessUnderSite(relyingParty.example) are reasonably close in time.
My proposal is that this is of course browser specified, but a suggested default is {enough time for auth, MFA, and consent to occur} - about 8 minutes. We know that anything shorter than that causes some folks to lose out - they can't find their key in time, they just don't move that quickly, etc. We would prefer to call requestStorageAccessUnderSite
after auth, so that users can make the connection between authenticating and being able to access their website correctly.
Agreed on text.
limit this kind of forwarding to single sign-on and require other means for federated logins
To clarify for others and to confirm - do you draw the distinction between SSO and federated login based on whether the IDP and the apps are owned by the same organization? Does the identity also need to be owned by the IDP? Effectively being in the same FPS.
If First Party Sets or something similar gets standardized, user agents are free to restrict the above flow to only sites within a single set. such as WebID (or what it's called nowadays).
If FedCM is added as an option, we'd move to preferring that for all auth over this suggestion, most likely, not just auth to sites outside of our FPS. It's not clear to me that this proposal offers benefits to sites within an FPS that FedCM doesn't also provide and improve on - are there any you have in mind?
Thanks @johnwilander , @michael-oneill!
You covered my question about 7 -
We may even have to make document.requestStorageAccessUnderSite(relyingParty.example) not reveal to identityProvider.example what the user's choice was.
Selfishly as the IDP owner, I'd like to be able to give that UX directly to the user during auth - provide the message that yes, the app really does need line of sight to the login server to function best. That said, this should be self healing - the SDK will attempt silent auth, and if no cookies are available, error and send the user back to the login domain, to try once more. We may need to keep a counter on the IDP domain to update the message to help explain to the user what went wrong.
Yeah, I think the IDP has so much opportunity to explain to the user what's happening that this should be a non-issue for real, legitimate use cases.
We need to make sure that the calls to document.allowStorageAccessRequestOnSite(identityProvider.example) and document.requestStorageAccessUnderSite(relyingParty.example) are reasonably close in time.
My proposal is that this is of course browser specified, but a suggested default is {enough time for auth, MFA, and consent to occur} - about 8 minutes. We know that anything shorter than that causes some folks to lose out - they can't find their key in time, they just don't move that quickly, etc. We would prefer to call
requestStorageAccessUnderSite
after auth, so that users can make the connection between authenticating and being able to access their website correctly.
Thanks for the concrete proposal here. Eight minutes is a great starting point.
Agreed on text.
Was this a comment on something else that got dropped from your reply?
limit this kind of forwarding to single sign-on and require other means for federated logins
To clarify for others and to confirm - do you draw the distinction between SSO and federated login based on whether the IDP and the apps are owned by the same organization? Does the identity also need to be owned by the IDP? Effectively being in the same FPS.
I avoid talking about apps since that may confuse people. This is about the web and I'm talking about one site as the relyingParty.example
and another site as the identityProvider.example
.
In the case of SSO, both sites have the same owner a login at identityProvider.example
automatically logs the user into relyingParty.example
(or at least the owner wants that to happen).
In the case of federated login, the two sites have different owners and a login at identityProvider.example
does not automatically log the user into relyingParty.example
. Instead the user has to express and facilitate "Please log me into relyingParty.example
using my identity at identityProvider.example
."
If First Party Sets or something similar gets standardized, user agents are free to restrict the above flow to only sites within a single set. such as WebID (or what it's called nowadays).
If FedCM is added as an option, we'd move to preferring that for all auth over this suggestion, most likely, not just auth to sites outside of our FPS. It's not clear to me that this proposal offers benefits to sites within an FPS that FedCM doesn't also provide and improve on - are there any you have in mind?
Once we ship a web API, it's there and we will likely have a long tail of websites who want to keep using it over some new thing like FedCM (is that the new name?). I suggest we mention in the spec from the get-go that user agents are allowed to restrict Storage Access forwarding to SSO and disallow it for federated logins. Time will tell how such a transition may look.
I agree that removing the hasStorageAccessForSite(Url)
function is best to remove a RP reading IDP state. We also need to be careful about the definition of how allowStorageAccessRequestOnSite
should behave if the IDP already has storage access. If we immediately resolve the returned promise, then this gives an easy way to read that state anyway. I propose that allowStorageAccessRequestOnSite
should always wait for the identityProvider.example
to call requestStorageAccessUnderSite(relyingParty.example)
before resolving.
The function John calls requestStorageAccessUnderSite
, is another instance where information leakage is at risk. I want it to return a promise because I think it will make implementation in the browser much easier. Not to mention it is a requirement if we have user interaction to allow or deny the storage access in the IDP's context. I think a Promise<void>
would be okay here, but the IDP would have to operate identically whether or not it received storage access.
I also agree that the interaction of this API needs to be compatible with other solutions like FedCM and FPS for the long haul. However, adding the ability to constrain IDPs makes me worried about inappropriate use of First Party Sets by uninformed developers. Consider a well-meaning developer that wants to constrain which IDPs can be used. The first solution they find might add all of the supported IDPs to their FPS. I know this may be a stretch, but I think a function that accepts a list of potential IDP domains is a closer fit to the semantics we want and supports the finite federated login options case as well as the SSO case. E.g. void restrictAllowStorageAccessRequestsOnSiteTo(List<Url>)
.
Thanks @johnwilander -
Agreed on text.
Was this a comment on something else that got dropped from your reply?
Ah, apologies, this was a response to not configuring the text within the prompt. Agreed, IDP has enough space to provide the sensibility itself.
Once we ship a web API, it's there and we will likely have a long tail of websites who want to keep using it over some new thing like FedCM (is that the new name?). I suggest we mention in the spec from the get-go that user agents are allowed to restrict Storage Access forwarding to SSO and disallow it for federated logins. Time will tell how such a transition may look.
Agreed on the long tail of support here. It would be better to not have the API to support to begin with. If the forward-declared API does not unblock access for federated auth (as the original storage access API does), and it performs a pure subset of FedCM, I'd rather not ask people to spend time building this proposal at all and instead refocus efforts on implementation of FedCM. Does that line up with your view of the problems this was intended to solve?
I'd like to use the upcoming privacy CG sync to discuss a couple topics for this item and how it relates to FedCM - what do you think @johnwilander ?
In particular:
I'm in favor of closing this issue and focusing on a FedCM solution - it would allow us to implement a single solution in our IDP rather than one per browser. WDYT?
In terms of closing this issue and focusing on FedCM, I’d say that the Storage Access API is shipping in multiple engines and FedCM is still a proposal. The timelines for when you can expect have a solution will likely be very different for the two.
We'd be hard pressed to consider a solution that only works for our sites, but not our customers', to be fundable for adoption or complete. If either one of the FPS or forward declaration requirements were dropped, that would change the calculus. Without the FPS requirement, this would allow all apps to be supported. If the forward declaration requirement were dropped, it would allow us to "ship" this fix (via IDP-only code changes) for sites we own without requiring a custom SDK to be shipped and adopted.
As-is, however, it's hard to justify two rounds of updates for our sites, and two rounds of implementation on our IdP and SDK, if we can simply skip one of them (SAA) and get the same results in the fullness of time.
Circling back here: I agree we should move forward on standardizing something close to what John suggested above. Would everyone welcome a PR containing a change to the WG report with a proposal reflecting the discussions here and borrowing from @hpsin's Edge Explainer so we can talk specifics? If so, I can get that together.
Problem:
A common pattern in authentication is visiting the IDP in a first party context (authentication) and then being redirected to the relying party. The relying party then embeds iframes of the IDP to finish authentication (in the scenario where it requires multiple access tokens). It uses those iframes over the lifetime of the application running, potentially days, refreshing tokens every time they expire (usually every one hour). Example applications that are used all day: Outlook Online, Teams. This is known as the implicit flow problem, a general pattern broken by 3p cookie removal.
Applications can update to support bespoke, per-IDP protocols that trigger the storage access API, in order to fix this iframe-based auth. This introduces additional friction for the user and the developer - the user must finish signing in, be sent to the relying party, and then find and click another button within the application to "really finish" signing in.
Existing similar behaviors:
Firefox has a heuristic today that unblocks (temporarily -15 minutes) storage access if the embedded frame received interaction immediately prior to redirecting to the relying party. This heuristic allows an application to successfully complete authentication using iframes, but the app will break if they require tokens after 15 minutes. This creates an unfortunate experience for the user - their application navigates to the IDP every hour when tokens expire, disturbing their ability to use the app.
Proposal:
The IDP, today, knows if the relying party requires 3p cookie access to the IDP, and knows the redirect URI for the application. The IDP should be allowed to trigger a storage access prompt, indicating that it will need storage access while embedded on the domain of the application being signed into. As part of triggering this prompt, the IDP provides the redirect URI that it will navigate to next. After the user accepts the prompt, the IDP must navigate to the promised redirect URL. If it does not, the browser does not grant the storage access.
Note: it would be additional work and ossification to have the storage access prompt trigger the redirect. The redirect can be done via POST, JS, or 302. State tracking and redirect detection may be easier than providing an API that specifies exactly how that redirect occurs.
This allows identity ecosystems to self-repair after 3p cookie disappearance without updates to applications.
Example: ComplexApp.example uses multiple iframes, and embeds apps that also use iframes, to authenticate with idp.example. When a user visits complexApp for the first time, they are unauthenticated. The app redirects the user to idp.example, with the following query string:
?client_id=abcd&redirect_uri=https://complexapp.example/oauth2
.The IDP authenticates the user, and immediately prior to redirecting the user to complexApp, checks the app registration for app abcd. Because abcd is registered to use the implicit flow, the IDP knows to prompt for storage access.
The IDP makes a JS call to the Storage access API:
requestStorageAccessForRelyingParty("https://complexapp.example/oauth2");
The browser prompts the user, potentially using the scenario as a hint to inform the user that this is for login: Are you ok with idp.example tracking your visit to complexapp.example?
The browser does not immediately grant the storage access, but waits to see if the IDP redirects the user to the provided URL. If the IDP does, then storage access is granted. If not, then the Storage Access grant is not provided.