TL;DR
How to run a script in an iframe on an ⚡️AMP page so that it reads an attribute in the parent window.
I'm trying to run a script in an iframe on an Accelerated Mobile Pages page but I'm getting an error in Google Chrome:
disqus-v1.0.10.html:27 Uncaught DOMException: Blocked a frame
with origin "https://media.toledano.org" from accessing a cross-origin
frame. at https://media.toledano.org/assets/js/disqus-v1.0.10.html:27:40
In Safari the error is this:
SecurityError (DOM Exception 18): Blocked a frame with origin
"https://media.toledano.org" from accessing a frame with origin
"https://yo.toledano.org". Protocols, domains, and ports must match.
The line with the problem looks for the value of an attribute on a specific element:
window.parent.document.getElementById('content').attributes["data-idx"].value}
The idea is that I load the iframe into a page and the script that contains the iframe looks for a line like this:
<main id="content" role="main" class="" data-idx="d09a0882-74a2-4d66-97d8-63e37172a1b4">
If I only use document.getElementById('content').attributes["data-idx"].value}
the result is null, because the iframe refers to itself (ie media
), that's why I refer to parent
(ie yo
).
Note
I don't understand on which side this CORS works so on both sides I added the permissions headers.
According to me, the headers work... I make a request asking for the headers and I can see that they work.
Test 1.- Origin: me - Destination: media ( yo
-> media
)
> $ curl -I -s -X GET -H "Origin: yo.toledano.org" https://media.toledano.org
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 0
Connection: keep-alive
Date: Tue, 01 Aug 2017 16:50:37 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, PUT, POST, DELETE, HEAD
Access-Control-Max-Age: 3000
Last-Modified: Fri, 14 Aug 2015 22:03:34 GMT
ETag: "d41d8cd98f00b204e9800998ecf8427e"
Accept-Ranges: bytes
Server: AmazonS3
Vary: Origin
Age: 2632
X-Cache: Hit from cloudfront
Via: 1.1 64184371bdc0c4545cde799b5949d17c.cloudfront.net (CloudFront)
X-Amz-Cf-Id: 0TmdSyBCGXHdRhFoykbzGJiOG6TN1GbdLkGFn13fUdtDOTc3GIMCZA==
The header Access-Control-Allow-Origin
is present, as expected.
Test 2.- Media origin, destination yo ( media
-> yo
)
$ curl -I -s -X GET -H "Origin: media.toledano.org" https://yo.toledano.org
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 01 Aug 2017 17:36:26 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 19630
Connection: keep-alive
Vary: Accept-Encoding
Expires: Tue, 01 Aug 2017 17:38:23 GMT
Cache-Control: max-age=600
X-Frame-Options: SAMEORIGIN
Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: toledano.org
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With
The header Access-Control-Allow-Origin
is present, as expected.
Exhibit 3, Witness. From Google to me and half ( google.com
-> yo
; google.com
-> media
)
$ curl -I -s -X GET -H "Origin: google.com" https://yo.toledano.org
HTTP/1.1 200 OK
# bla bla bla
X-Frame-Options: SAMEORIGIN
Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: toledano.org
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With
There are the headers... according to me they shouldn't appear... ?
$ curl -I -s -X GET -H "Origin: google.com" https://media.toledano.org
HTTP/1.1 200 OK
# bla bla bla
X-Cache: Hit from cloudfront
Via: 1.1 a658139699129536c4b1341e7df68ce6.cloudfront.net (CloudFront)
X-Amz-Cf-Id: qJUhJ8VxTKvX3yU8toi87DNae41F35_HZKB4M7qwUxky_-Aq0Wrdcg==
They are not here because CloudFront only allows the toledano.org
.
Considerations
- The blog is made in Django, with the package
django-cors-header
and configuration:CORS_ORIGIN_ALLOW_ALL = True
. - I use Nginx as a web server and add this header to the responses:
add_header 'Access-Control-Allow-Origin' 'toledano.org';
- The script is hosted on Amazon S3+Cloudfront and the bucket has the broadest permissions:
<AllowedOrigin>*</AllowedOrigin>
and the distribution behavior only allows thetoledano.org
.
What do I have to do to run the script that loads into an iframe on an AMP page to get the value I'm looking for?
Bonuses:- What am I doing wrong? What is my mistake?
- How does CORS work?
Edit: this is a test page: https://yo.toledano.org/desarrollo/grupar-resultados-con-mysql/amp/
Attempt Log
- I followed the suggestion from @fredyfx's comment and it didn't work.
Since I have the idea that the problem is with Nginx, I did this test: a) check if the origin is in the white list; b) if you are activating a variable; c) if the variable is active, activate the headers...
- I made a regex to accept my subdomains:
https?://(.*\.toledano\.org)(:[0-9]+)?
and if it works . I add this code to the Nginx configuration:
set $cors ''; if ($http_origin ~ 'https?://(localhost|127\.0\.0\.1|.*\.toledano\.org)(:[0-9]+)?') { set $cors '1'; }
and add some test headers...
add_header 'nSpaces-Origen' "$http_origin"; add_header 'nSPaces-CORS' "$cors";
and the test is correct, I see the header
nSpaces-CORS
...$ curl -I -s -X GET -H "Origin: https://media.toledano.org" https://yo.toledano.org HTTP/1.1 200 OK # bla bla bla Access-Control-Allow-Origin: * nSpaces-Origen: https://media.toledano.org nSPaces-CORS: 1
So I add this snippet, to activate the rest of the headers...
if ($cors = '1') { add_header 'Access-Control-Allow-Origin' "$http_origin"; add_header 'Access-Control-Allow-Credentials' 'true'; add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With'; }
and the test no longer works! (The result is the same as 2.1).
- I made a regex to accept my subdomains:
I still can't figure out what the problem is,
Access-Control-Allow-Origin
but there is a way to run the comment script, which solves the problem, but not as expected, so this is more of a workaround.The problem is solved by passing the script all the parameters it needs in the URL and having it read them from there, because it's its own URL and not the parent window's.
on the blog
So first we see that Disqus needs four parameters:
shortname
- which is the name of the site or forum as they call it.url
- which is the URL of the page or entry or post or articletitle
- which is the title of the pageidentifier
- which is a unique identifier (in my case it's a UUID, but it can be the slug or theid
or any unique field).All this data is encoded, in Django with the filter
urlencode:''
or with theurlencode()
PHP function.With these parameters I call the Disqus page. In Django it looks like this:
Which transforms into:
in the script
Now in the script underwent some modifications. The official documentation says that we need the object
disqus_config
to be formed like this:But since we don't have access to that information, we'll use a helper.
This function takes the second substring from the location, on the line
and places it in the variable
query
:On the next line:
creates an array by dividing the string
query
by the ampersand .In the cycle
for
, this array is traversed to find the value that contains thevariable
indicated one.Come back
false
if you don't find what you're looking for.Now the object
disqus_config
is formed by calling this helper function with the variable that is required in each case:The solution is on a GitHub case with a full example for use with the Automatic AMP plugin , if you're interested.
It's not really an en problem
CORS
,Ngnix
but rather a security measure implemented by browsers known asSame-Origin Policy
The solution provided by browsers to allow communication between documents of different is
Window.postMessage()
.Example:
Parent document:
Child document: