Search code examples
iframeanchorcontent-security-policyw3c-geolocationhttp-permissions-policy

Geolocation denied for HTML embedded site - anchor tag feature policy


The iframe with allow="geolocation" works great. But what if I want to load a link which calls getCurrentPosition() from a html anchor tag?

Example : In iframe, I'll use

<iframe src="https://siteWhichCallsGetGeolocation.com" allow='geolocation'></iframe>

I have a use case where a html embed link like

<a href="https://siteWhichCallsGetGeolocation.com">Click here to get geolocation</a>

is embedded in a parent site. The parent site loads the child link inside it's own iframe

It is to be noted that The parent site's iframe has the allow="geolocation" attribute .

In this case the request is silently denied with error

errorCode :1

errorMessage: "Geolocation has been disabled in this document by permissions policy."

The feature policy document point to iframe but what about anchor tag embeds?

I understand that I can use a iframe instead of anchor tag embed but the parent site only allows anchor tag's.

PS - This is my first question here. Please assist.

Note :

  1. I also tried a random shot by including allow="geolocation" for anchor tag, but it doesn't work.
  2. This seems to work on Safari (which I'm assuming is because Safari hasn't yet implemented feature policy for cross origin site requests)

Edit 1 :

I tried my use case in jsfiddle and notice that all scripts i give (iframe or anchor tag) are loaded inside jsfiddle's iframe

<iframe name="result" allow="midi; geolocation; microphone; camera; display-capture; encrypted-media;" sandbox="allow-modals allow-forms allow-scripts allow-same-origin allow-popups allow-top-navigation-by-user-activation allow-downloads" allowfullscreen="" allowpaymentrequest="" frameborder="0" src=""> </iframe>

If i load my iframe inside this, it works perfectly !(perfectly balanced, as all things should be...)

But if I load my anchor tag inside this, i get the error. As per granty's answer

the jsfiddle's iframe with allow='geolocation' should set jsfiddle site's feature policy that all scripts loaded inside that iframe can access geolocation right ? Why does my iframe inside jsfiddle's iframe work but anchor tag doesnt ?

Note - I have no control over the top level site. I only code the scripts for https://siteWhichCallsGetGeolocation.com

SOLUTION (if you have control over top document's feature policy or control over any intermediate <iframe> which have geolocation permission) (derived from granty's answer)

I tried the suggested solution by changing the jsfiddle's iframe attribute via inspect element to

<iframe name="result" allow="midi; geolocation https://siteWhichCallsGetGeolocation.com ; microphone; camera; display-capture; encrypted-media;" sandbox="allow-modals allow-forms allow-scripts allow-same-origin allow-popups allow-top-navigation-by-user-activation allow-downloads" allowfullscreen="" allowpaymentrequest="" frameborder="0"></iframe>

To highlight, I added my site next to geolocation in allowlist like

allow="midi; geolocation https://siteWhichCallsGetGeolocation.com ;

and then loaded my anchor tag. When the getCurrentPosition() was triggered from script loaded from https://siteWhichCallsGetGeolocation.com, I got the browser prompt to Allow/Deny location sharing. I allowed and got the coordinates!

Wow! If only it could be as simple as editing the top level document's feature/permissions policy via inspect element.

But alas,I have no control over the top level document's feature policy. At least I understood what's going on under the hood.


Solution

  • Briefly, you have to publish

    Feature-Policy: geolocation https://siteWhichCallsGetGeolocation.com;
    

    HTTP response header, when the server sends page into browser.

    TL; DR or What's going on under the hood
    The allow="geolocation" attribute is used to set Feature Policy (now it's renamed to Permissions Policy) inside iframe.
    You use the geolocation directive with an emplty list of origins, therefore a default policy geolocation 'src' is applied. This means that inside iframe is allowed Geolocation API for scripts loaded from the same location as iframe - https://siteWhichCallsGetGeolocation.com.

    Pls note that for allow="geolocation" default policy is 'src'(the origin from iframe's src=). But if you use directive Feature-Policy: geolocation;, the default policy for empty list is 'self'.

    To set Feature Policy in the main page (top level document) is intended the Feature-Policy header. If it didn't set, the default policy geolocation 'self' is applied. This forbids using Geolocation API for scripts loaded from https://siteWhichCallsGetGeolocation.com origin.

    Therefore use

    Feature-Policy: geolocation https://siteWhichCallsGetGeolocation.com;
    

    or

    Feature-Policy: geolocation 'self' https://siteWhichCallsGetGeolocation.com;
    

    if your own page uses geolocation too.

    Ref EDIT 1
    Jsfiddle's <iframe allow="geolocation;" means <iframe allow="geolocation 'src';". It means only scripts loaded from the same origin(Url) as jsfiddle iframe can access Geolocation API. Therefore it does not allow your anchor tag from the https://siteWhichCallsGetGeolocation.com origin.

    BUT passing permissions of parent page into nested iframe perform a tricky way. If some browsing context has some permission, it can grant it to any nested contexts independantly of its origin (if it creates this context by self).

    So jsfiddle's iframe has permission for geolocation. Inside jsfiddle's iframe is placed your nested iframe having allow='geolocation 'src'.
    Jsfiddle's iframe delegate its permission for geolocation to your iframe, of course the allowed origin is changed when permission passing into another iframe.

    Yes, scripts loaded from third-party Urls can create iframe too, but they can't grant geolocation permission, because they do not have it - since loaded from origins for which geolocation is not allowed.

    the jsfiddle's iframe with allow='geolocation' should set jsfiddle site's feature policy that all scripts loaded inside that iframe can access geolocation right?

    Not "loaded inside that iframe", but loaded from the same Url, as that iframe.
    Otherwise, what's the point of specifying a list of allowed origins like allow="camera 'self' https://www.youtube.com", if any not listed script can have access to your camera.

    It's a bad practice to use hidden default, therefore you was mislead. Instead of:

    <iframe allow="midi; geolocation; microphone; camera; display-capture; encrypted-media;"
    

    should be:

    <iframe allow="midi 'src'; geolocation 'src'; microphone 'src'; camera 'src';
      display-capture 'src'; encrypted-media 'src';"