Browser Permissions

November 27, 2024
Chapter 2: Browser Elements

This will be the last post (for now) on the browser-side before we move into a discussion of the four specific APIs that make up advertising-focused elements of the Google Privacy Sandbox: Topics, Protected Audiences, Attribution Reporting, and Private Aggregation.  The topic is browser permissions.  Permissions come in two indirectly-related core specifications: the Permissions API specification and the Permissions Policy specification.  

  • The Permissions API Specification defines the mechanism by which users and/or the user agent can set permissions for which features in their browser web applications can access.  
  • The Permission Policy Specification is a server-side mechanism.  It allows web site owners to set access rules for which browser features and functionalities (e.g. geolocation) third- parties embedded on a page (e.g., an iframe with an ad from www.thirdparty.com) can have access to.  Permission Policy was previously known as the Feature Policy Specification.  On the whole, that history is not relevant to this discussion.  However there is one case, the Feature Policy JavaScript API, which is relevant since it has not been updated. 

Most browser features are delivered through some type of API.  Historically different APIs were inconsistent in handling their own permissions,  For example, the Notifications API provided its own methods for requesting permissions and checking permission status, whereas the Geolocation API did not. The Permissions API evolved so that developers could have a consistent user experience for working with permissions.

There is also the fact that publishers and end users may have different requirements.  While an end user or a user agent may allow access to geolocation information because they want a more customized experience, a publisher’s terms and conditions may say they will not access geolocation information for any reason.  The publisher wants a way to ensure a mistake cannot happen and they somehow get geolocation information from that specific browser.  How then does the browser rationalize these two important but conflicting priorities?  

The Permissions API effectively aggregates all security restrictions for the context, including any requirement for an API to be used in a secured manner, Permissions Policy Specification restrictions from the publisher applied to the document, requirements for user interaction, and user prompts. So, for example, if an API is restricted by a server-side permissions policy, the returned permission would be denied and the user (client-side) would not be prompted for access.  The two APIs are thus two sides of a single coin and work together to allow both users and top-level domain owners to set the permissions they wish in a non-conflicting manner.

Permissions API Specification

Web browser owners are continuously enhancing the functionalities of their browsers to provide  better experiences, or a wider range of experiences, to their users. Users may not want to allow web sites they visit to have access to one or more of these features. The Permissions API Specification solves this problem.  It defines the concept of a powerful feature.  A powerful feature is a browser-side feature for which a user gives express permission for web sites they visit to use/access.  Powerful features, except for a few notable exceptions, are also policy-controlled features which are also specified by website owners under the Permissions Policy Specification.    

A permission for a powerful feature can have one of three states:

  • Denied. The user, or their user agent on their behalf, has denied access to the feature.  The caller cannot use it.  Features which are denied by default include geolocation capabilities, camera access, or microphone access.  Access to many of these “denied” features can be changed through prompts to the user (see Figure 1).
  • Granted.  The user, or the user agent on the user's behalf, has given express permission to use the feature. The caller can use the feature without having the user agent asking the user's permission.  Examples of features granted by default are storage access where websites can store data locally,  or script execution, which allows websites to execute JavaScript code.
  • Prompt(ed).  The user must provide express permission.  The user agent will prompt the user for the express permission when a specific top-level domain asks to use it. 

Figure 1 - Examples of Prompted Features

To be clear, even those features that are denied by default may actually have their default permissions state as “prompt”.  This setting allows for a user to be prompted to provide express permission for that feature to be used.  You can see this by clicking settings widget on the left-hand side of the browser address bar and clicking into site settings.  This displays the current status of permissions for each powerful feature on that specific web site (Figure 2).  This interface allows the user to manually set their preferences.

Figure 2 - Examples of Permission Settings in Chrome for a W3c.org Site

Developers can also use Chrome developer tools to examine permissions for any given frame on a specific page to ensure that permissions are handled the way the developer intends (Figure 3)

Figure 3 - Permissions as Shown in Chrome Developer Tools (under the applications tab)

Every permission has a lifetime, which is the duration for which a particular permission remains "granted" before it reverts back to its default state. A lifetime could be for a specific amount of time, until a particular top-level browsing context is destroyed, or it could be infinite. The exact lifetime is set when the user gives expression permission to use the feature.  It can often be set by the user via a browser interface.  Alternatively, it can also be hard-wired into the browser itself by the browser manufacturer.

All permissions are stored locally on the device in a permission store.  For WIndows 11 this file is “Local State” and it can be found in the Chrome subdirectories:  

C:\Users\<user_name>\AppData\Local\Google\Chrome\User Data

Each permission store entry is a key-value tuple consisting of  permission descriptor, permission key, and state

Permissions Policies

Permissions Policies allows web developers to selectively block or delegate access to certain browser features when a user agent is viewing a page from their domain.   We have already been exposed to Permissions Policies, in particular the Permissions Policy header, in the prior post on Client Hints Infrastructure.  Client Hints, however, are only one aspect of browser behavior or features that can be controlled by permissions policies.  The full standard list is shown in the resources section here.  

This list, however, is always growing, as more specifications for more features are developed and W3C working groups design their specifications/technical approaches to meet the design goals for security and privacy as discussed in RFC 6973 - Privacy Considerations for Internet Protocols, which we discussed previously.  The Google Privacy Sandbox is an excellent example of this, which is explored in a later section.

Permissions policies are implemented at two layers:

  1. The response header layer (as we saw for Client Hints)
  2. The embedded frame layer - mostly around iFrames and, in terms of the Sandbox technologies, Fenced Frames.  

The header layer sets global policies for the specific user agent.  The embedded frame layer is the more fine-grained.  It inherits the settings from the response header layer, and its settings  for the specific origin it (the iframe) controls supersede the permissions it inherits .   

We’ll start by discussing the two mechanics for permission policies and then show how they interact.  After that we will discuss the alternate Feature Policy Javascript implementation.

Permissions Syntax

Before we delve into the mechanics of permissions policies, there are some nomenclature definitions we need to understand.  These are shown in Figure 4.  You can come back to this reference as we discuss the mechanics until you are comfortable with the way policies are specified under Permissions Policies.  

Figure 4 - Syntactic Elements of the Permissions Policy Specification

Element Meaning Example(s)
Permissions- Policy:

The response header to set permissions.

The default structure is:

Permissions-Policy: <directive>=<allowlist>

Permissions-Policy: camera=*

means set camera permissions to be accessible to this domain and all origins represented on the page

  • camera is the directive

  • * is the allowlist.

Directive (also token or feature) A specific feature that is to have permissions set in the allowlist in the Permissions-Policy header

camera

geolocation

picture-in-picture

* Policy to be applied to all origins

geolocation=*

means geolocation is to be allowed for this domain and all origins represented on the page

self

The current top-level domain

means the feature will be allowed in this document, and in all nested browsing contexts (<iframe>s) in the same origin only.

Permissions-Policy: geolocation=(self)

means set geolocation feature to be available to the top-level domain and no other and in all nested browsing contexts (<iframe>s) in the same origin only

()

An empty allowlist

means no permission globally in header version

Permissions-Policy: camera=()

means camera access is denied globally to the top-level domain and any other domains embedded in the page

none No permission globally in frame version

<iframe src=”https://example.com” allow=”geolocation ‘none’”> </iframe>

means geolocation service is denied globally to the top-level domain and any other domains embedded in the page

allow Sets permissions in the embedded frame version

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

sets the geolocation feature permission to denied for all origins including the top-level domain

src In the frame version: the feature is allowed in this <iframe>, as long as the document loaded into it comes from the same origin as the URL in its src attribute. This value is only used in the <iframe> allow attribute, and is the default allowlist value in <iframe>s. See allow example
“<origin>”

In the frame version: the feature is allowed for specific origins (for example, "https://a.example.com").

Multiple origins in a single <iframe> should be separated by spaces. 

See allow example

 

Response Header Syntax

The response header permission settings are the global default across any and all features and frames on a given page. They are the primary set of permissions used when there are no more specific policies put in place at the frame level.

The default structure of header permissions is relatively simple:

Permissions-Policy: <directive>=<allowlist>

The directive is the feature which needs permissioning and the allowlist is the set of domains and subdomains to which permissions will be given.  Let’s take some examples to show the range of options.  It is not my intention to go exhaustively through the grammar and how it works.  The main point is to show you generally how you set permissions in different ways.

Let’s use the top level domain of https://www.example .com.  To block all access to the geolocation directive (feature) use the following:

Permissions-Policy: geolocation=()

To allow access to a subset of origins, use the following:

Permissions-Policy: geolocation=(self "https://a.example.com" "https://b.example.com")

In this example, we are allowing geolocation feature access to the top level domain (“self”, or https://www.example.com), and two of its subdomains, a.example.com and b.example.com.  Note that the two full URLs are input in quotes with only spaces between.and the allowlist is enclosed in parentheses.  

Permissions can be concatenated on a single line or broken out separately.  The two examples below are equivalent:

Permissions-Policy: picture-in-picture=(), geolocation=(self "https://example.com"), camera=*;

Is the same as:

Permissions-Policy: picture-in-picture=()
Permissions-Policy: geolocation=(self "https://example.com")
Permissions-Policy: camera=*

The list of powerful features that can be controlled both by the header form and the embedded syntax form are shown here.

What happens if there is no Permissions Policy header on the page?  In that case, every feature policy defaults to * - that is, all origins and subdomains have access to the feature.

Embedded Frame Syntax

Let’s say a publisher’s page, https://www.exaomple.com/home contains both a third-party iFrame embedded in a page for a payment widget as well as an iframe that contains an ad.  The two iFrames are from different vendors, and the publisher wants to differentially give access to these two vendors to different powerful features.  Only the payment widget should have access to the user’s identity credentials but the advertiser should not. At the same time, only the advertiser should have access to the geolocation feature as a way to know which ad to serve, but definitely for security reasons should never have access to the user’s identity credentials.  How do they accomplish that?

This is where the embedded frame layer comes in.  The embedded frame layer allows for more fine-grained and differential control of permission delegation than the header layer can provide.  It allows the developer to set permissions at the frame level that may supersede those from the header layer.

The basic syntax of the embedded frame approach is as follows:

<iframe src="<origin>" allow="<directive> <allowlist>"></iframe>

The src is the top level domain (or origin)..  The allow="<directive> <allowlist>" sets the permission for the specific feature and identifies which third-party domains or origin subdomains have access.  

One very important note to this approach is that once a permission is passed to a third-party, that third-party can pass the same permission on to other third-parties it does business with.  The assumption is that if the third-party is trusted, then can be relied on to only share these permissions with parties that are also trusted.

So now let’s show how this would be implemented in our example.

Here is the header permission setting for the top-level domain on this page

Permissions-Policy: identity-credentials-get=(self)
Permissions-Policy: geolocation=(self)
Permissions-Policy: camera=*

Now let’s show the embedded frame settings for the payments provider iFrame:

<iframe src="https://www.example.com" allow="identity-credentials 'self' https://www.payment_provider.com"></iframe>

And here are the embedded frame settings for the ad provider’s iFrame:

<iframe src="https://www.example.com" allow="geolocation 'self' https://www.ad_provider.com"></iframe>

Figure 5 shows how these sets of permissions interact to give the correct accesses to the origin as well as the two third-parties.

Figure 5 - Resulting Permissions Policies for https://www.example.com/home

Again, the point here is not to drill deeply into the various combinations of syntactic patterns for either of these layers.  The main concept to take away from this discussion is how the header layer and the embedded layer interact to provide fine-grained control of policies for the various parties that operate on any given webpage.  

Alternate Feature Policy API Javascript-Based Mechanic

We previously mentioned that Privacy Policy evolved from another standard called Feature Policy.  Feature Policy was subject to some generic design weaknesses of HTTP headers that were resolved as part of a more general update to header structures called Structured Fields. However, there was a Javascript-based approach to permissions under Feature Policy that has yet to be updated.  So the alternate Javascript mechanic, the Feature Policy API, is the way permissions are handled using Javascript for Permissions Policy for now.  A proposal to update the API into Permissions Policy exists, but not much has happened with it since early 2022.  So it is not clear when or if these updates will be made.

The API consists of four endpoints that allow developers to to set or examine the allow condition for powerful features across either a document or an iFrame, depending on context, although the most common use is using the API to set permissions within the context of an iFrame. Figure 6 lists these four endpoints and describes what they do.

Figure 6 - Feature Policy API Endpoints

Endpoint Purpose

FeaturePolicy.allowsFeature(feature)

or

FeaturePolicy.allowsFeature(feature, origin)

Returns a Boolean that indicates whether or not a particular feature is enabled in the specified context. True = enabled.

If the (feature, origin) form is used, the endpoint returns true if the feature is allowed for the specified origin

FeaturePolicy.features() Returns a list of names of all features supported by the User Agent. Features that appear in the list might not be allowed by the Permissions Policy of the current execution context and/or might be restricted by user-granted permissions.
FeaturePolicy.allowedFeatures() Returns a list of names of all features supported by the User Agent and allowed by the Permissions Policy. Note that features appearing on this list might still be behind a user permission.
FeaturePolicy.GetAllowlistForFeature Returns the status of the allow for the specified feature.

There are subtle differences between the implementation of these features at the document and iFrame level.  For example, if the featurePolicy.allowsFeature(feature, origin) is called at the document level, the method tells you that it's possible for the feature to be allowed to that origin. The developer would still need to conduct an additional check for the allow attribute on the iframe element to determine if the feature is allowed for that element’s third-party origin.  Those who wish to drill further into the API syntax and usage can see this MDN article on FeaturePolicy.

Permissions Policy and the Google Privacy Sandbox

Since this blog is all about the Privacy Sandbox, I would be remiss if I didn’t discuss exactly how Permissions Policy applies to the APIs specific to the Sandbox.  There are two aspects to Permissions Policy to discuss in regards to the Privacy Sandbox:

  1. Permissions directives/features which apply to the various Privacy Sandbox APIs
  2. Embedded layer permissions in fenced frames

Permissions Features for Privacy Sandbox APIs

Figure 7 shows the features around Privacy Sandbox which can be controlled via the Permissions Policy Specification.

Figure 7 - Privacy Sandbox APIs Subject to Permissions Policy

API Directive Description Default allowlist

Attribution Reporting

(Guide / Spec)
attribution-reporting Allows the usage of the Attribution Reporting API *

Federated Credential Management

(Guide / Spec)
identity-credentials-get Allows obtaining a credential object self

Private Aggregation

(Guide /Spec)
private-aggregation Allows reporting using Private Aggregation *

Private State Tokens

(Guide/Spec)
Private-state-token- issuance Allows requesting a token

* from Chrome 132 and later

self in previous versions

Private-state-token- redemption Allows redeeming a token and sending a redemption record

* from Chrome 132 and later

self in previous versions

Protected Audience

(Guide / Spec)
join-ad-interest-group Allows adding user to an interest group for the site

* during testing

self in the future
run-ad-auction Allows running an ad auction *

Shared Storage

(Spec)
shared-storage Allows reading and writing with Shared Storage *
shared-storage-select-url Allows the URL Selection operation to be executed *

Storage Access

(Guide / Spec)
storage-access Allows access to the Storage Access API *

requestStorageAccessFor

(Guide / Spec)
top-level-storage-access Allows access to the top-level access via requestStorageAccessFor() for sites grouped in a Related Website Set *

Topics

(Guide / Spec)
browsing-topics Allows generating topics for the user, and reading the generated topics *

User-Agent Client Hints

(Guide / Spec)
See the guide for the full list of headers Allows the specified client hint to be available to the requester See the spec for the full list of default allowlist values

Permissions Policy in Fenced Frames

Fenced Frames were discussed in a post at the beginning of Chapter 2.  Fenced frames are an evolution of iFrames that provide more native privacy features and address other shortcomings of iFrames. The core design goal of fenced frames is to ensure that a user’s identity/information from the advertiser cannot be correlated or mixed with user information from the publisher’s site when an ad is served. Fenced frames have numerous restrictions relative to iFrames to ensure that such cross-site information sharing cannot occur.

These limitations, however, create a challenge for permissions policy.  A set of permissions delegated from permissions headers to a fenced frame could potentially allow access to features that could be used as a communication channel between origins, thus opening the way for cross-site information sharing.  

As a result, standard web features whose access is typically controlled via Permissions Policy (for example, camera or geolocation) are not available within fenced frames.  The only features that can be enabled by a policy inside fenced frames are the specific features designed to be used inside fenced frames:

Protected Audience API

  • attribution-reporting
  • private-aggregation
  • shared-storage
  • Shared-storage-select-url

Shared Storage API

  • attribution-reporting
  • private-aggregation
  • shared-storage
  • shared-storage-select-url

Currently these permissions are always enabled inside fenced frames. In the future, which ones are enabled will be controllable using the <fencedframe> allow directive.  Blocking privacy sandbox features in this manner will also block the fenced frame from loading — there will be no communication channel at all.

So we come to the end of Chapter 2 for now.  I may decide to expand it later to include discussions of CORS, CORB and other security standards.  But we’ll leave it here for now and begin the move into the server side elements of the Privacy Sandbox.