I want to test the new Firefox Storage Access API to allow 1st party storage (cookie, local storage, indexeddb, ...) to an iframe of a different domain (but still under my control).
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Parent Domain</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-cookie/2.2.0/js.cookie.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jschannel/1.0.0-git-commit1-8c4f7eb/jschannel.min.js"></script>
</head>
<body>
<div>
Cookies: <ul class="cookie-data"></ul>
</div>
<iframe
id="rpc-gateway"
src="http://child.local:8080/iframe-firefox.html"
sandbox="allow-storage-access-by-user-activation allow-scripts allow-same-origin"></iframe>
<script type="text/javascript">
var chan = Channel.build({
window: document.getElementById("rpc-gateway").contentWindow,
origin: "*",
scope: "testScope"
});
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Child Domain</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-cookie/2.2.0/js.cookie.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jschannel/1.0.0-git-commit1-8c4f7eb/jschannel.min.js"></script>
</head>
<body>
<button onClick="onLoginClick()">Login</button>
<script type="text/javascript">
var chan = Channel.build({
window: window.parent,
origin: "*",
scope: "testScope"
});
let onLoginClick = function(trans, params) {
document.hasStorageAccess().then(hasAccess => {
if (!hasAccess) {
console.log("no access - requesting access");
return document.requestStorageAccess();
}
}).then(_ => {
document.hasStorageAccess().then(hasAccess => {
console.log("hasAccess:", hasAccess);
window.localStorage.setItem('foo', 'bar');
})
}).catch((err) => {
console.log("hasStorageAccess() failed", err);
});
};
</script>
</body>
</html>
When clicking on the "Login" button from the Child Iframe, the following log output is generated:
no access - requesting access # iframe-firefox.html:22:25
hasAccess: true # iframe-firefox.html:27:25
Request to access cookie or storage on “http://child.local:8080/iframe-firefox.html” was blocked because we are blocking all third-party storage access requests and content blocking is enabled. # iframe-firefox.html:28:24
The visible conclusion is:
What do I do wrong?
More Info's:
The Storage Access API provides a way for embedded, cross-origin content to gain unrestricted access to storage that it would normally only have access to in a first-party context (we refer to this as an origin's first-party storage).
If the iFrame shows an extension page, then it should be possible. Otherwise, nope, you will have to pass a message to the extension and the extension will access localStorage.
What went wrong? A request to access cookies or storage was blocked because the browser identified it as coming from a tracker and content blocking is enabled. If the blocked resource doesn't need authentication, you can fix the warning message by adding a crossorigin="anonymous" attribute to the relevant element.
This seems to be a bug in the version of Firefox you're using. I set up a test locally of what you have and in Firefox 69.0.1 (64 bit), I get no error and the value is stored to local storage. When I took the sandbox flag allow-storage-access-by-user-activation
out of the parent iframe, the child failed to get permission for local storage, so that confirms that my setup was actually working properly. Here's what I did:
Created a Node.js/Express server for the parent:
const express = require('express');
const cors = require('cors');
const path = require('path');
const server = express();
server.use(cors());
server.use(express.static(path.resolve('./public')));
server.listen(8080, function() {
console.log('listening on *:8080');
});
Created a Node.js/Express server for the child (with different port to trigger same origin policy):
const express = require('express');
const cors = require('cors');
const path = require('path');
const server = express();
server.use(cors());
server.use(express.static(path.resolve('./public')));
server.listen(8081, function() {
console.log('listening on *:8081');
});
Created an index.html for the parent (pretty much the same as yours):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Parent Domain</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-cookie/2.2.0/js.cookie.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jschannel/1.0.0-git-commit1-8c4f7eb/jschannel.min.js"></script>
</head>
<body>
<div>
Cookies: <ul class="cookie-data"></ul>
</div>
<iframe
id="rpc-gateway"
src="http://127.0.0.1:8081/iframe-firefox.html"
sandbox="allow-storage-access-by-user-activation allow-scripts allow-same-origin"></iframe>
<script type="text/javascript">
var chan = Channel.build({
window: document.getElementById("rpc-gateway").contentWindow,
origin: "*",
scope: "testScope"
});
// Added this to try out the JSChannel
chan.call({
method: "reverse",
params: "hello world!",
success: function(v) {
console.log(v);
}
});
</script>
</body>
</html>
And created iframe-firefox.html for the child:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Child Domain</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-cookie/2.2.0/js.cookie.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jschannel/1.0.0-git-commit1-8c4f7eb/jschannel.min.js"></script>
</head>
<body>
<button onClick="onLoginClick()">Login</button>
<script type="text/javascript">
var chan = Channel.build({
window: window.parent,
origin: "*",
scope: "testScope"
});
// Other end of the JSChannel call
chan.bind("reverse", function(trans, s) {
return s.split("").reverse().join("");
});
let onLoginClick = function(trans, params) {
document.hasStorageAccess().then(hasAccess => {
if (!hasAccess) {
console.log("no access - requesting access");
return document.requestStorageAccess();
}
}).then(_ => {
document.hasStorageAccess().then(hasAccess => {
console.log("hasAccess:", hasAccess);
window.localStorage.setItem('foo', 'bar');
})
}).catch((err) => {
console.log("hasStorageAccess() failed", err);
});
};
</script>
</body>
</html>
And everything worked as expected... So I'm feeling pretty sure that the issue is with the specific version of Firefox Developer Edition that you're using.
Also, here's a link to a zip of my setup if you want to give it a try on your end and see if this works differently than what you have: server.zip
Let me know if there's anything else I can do to help.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With