Skip to content

Instantly share code, notes, and snippets.

@slightlyoff
Last active August 29, 2015 14:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save slightlyoff/43cd8c2f64a0719358fe to your computer and use it in GitHub Desktop.
Save slightlyoff/43cd8c2f64a0719358fe to your computer and use it in GitHub Desktop.
Async Permissions (EARLY DRAFT)

A Plan For Permission-Requesting APIs

It's often the case that the web platform is compared to native platforms and found wanting when it comes to the variety and richness of available device APIs. This is frequently coupled with worries of an "infobarpocalypse"; the tendency for browsers to prompt users early-and-often when exposing sensitive APIs. The worry emerges that this ask-and-ye-shall-find model is confusing to both users and developers.

Thoughtful voices have likewise noted the downsides of up-front permission grants in application platforms like Windows (implicitly) and Android (explicitly). Upgrades to these applications become hostage-taking scenarios: if you like the app and want to keep using it, your only alternative is to grant it ever-more permission and access to sensitive data over time -- with no hope of revocation. User recourse is limited to app removal and the only ecosystem-level fix is through revocations via relatively-closed app stores.

How, then, can the web ever hope to expand to grant more capability, safely, without devolving into a high-friction world of app stores, packaging, and signing?

Review and classification of the permissions landscape of native ecosystems by Aaron Boodman, Rafael Weinstein, and Adrienne Porter-Felt of the Google Chrome team points to two broad classes of permissions: those that can operate within the Origin Model of web security and those which circumvent it.

For instance, a Raw Socket API that allowed only connections to be initiated to a web application's origin host & port is likely origin-preserving. A Raw Socket API that allowed connection to arbitrary hosts, or even multiple ports on the originating host, clearly breaks the origin model. For the purposes of the rest of this post, we'll consider only the origin preserving subset of capabilities.

Existing infobar-prompting permissions can be an answer to the up-front grant model, but as some have pointed out, it gets messy when applied as a general answer. Getting agreement between vendors about what UI is appropriate is a hill on which many previous efforts have died. Making seems incompatible with forging UA-level agreement about UI.

An important axis for considering these questions is permission management. Having APIs which enable users to revoke permissions later is the dual of asking users to grant permissions at runtime (not up-front).

Extracting this value for users requires that application developers must handle failure and that permission-requiring APIs be asynchronous.

A General Pattern

The constraints previously outlined inform a pattern we have begun to adopt in the design of new features like Background Notifications, Background Synchronization, and Push Messaging. Extracting this pattern and describing it can improve overall platform consistency. We see this with Promises where existing APIs which previously took callbacks and returned nothing can be easily retrofitted to make callback arguments optional and return Promises. This enables them to work nicely along-side new platform APIs that support only the more modern Promise style.

The overall constraints on the design space are:

  • Requests for permissions must be asynchronous
  • It must be possible for a permission request to fail
  • It must be possible to (asynchronously) detect if a permission is already available in order to enable adaptive UI
  • Any general pattern should be complementary, or at least compatible, with existing asynchronous permission request APIs in the platform (to allow them to be retrofitted, as many callback APIs have been with Promises)

Lets examine an example API for a hypothetical permission that controls whether or not the site is allowed to frobulate:

// https://example.com/frobulator.js

// Boilerplate setup
var onerror = console.error.bind(console);
var frobulateButton = document.getElementById("frobulateButton");

// Feature detect
if (!navigator.requestFrobulatePermission) {
  // This runtime doesn't have the feature, so hide the button or show an error
  frobulateButton.style.display = "none";
} else {
  // Set up the button to request permission in-context:
  frobulateButton.onclick = function() {
    navigator.hasFrobulatePermission().then(
      function(disposition) {
        // The "has*Permission" methods return "default", "granted", or "denied"
        switch (disposition) {
          case "default": // We haven't requested the permission...
          case "granted": // ...or we know we have it, so request API access:
            navigator.requestFrobulatePermission().then(
              function(forbulateAPI) {
                // No UI is shown to user if previously granted, whereas user
                // may be prompted about frobulation if disposition is
                // "default".
                forbulateAPI.frobulate();
              },
              onerror
            );
          case "denied":
            // Disable our UI for the action.
            frobulateButton.disabled = true;
            onerror("unable to frobulate; permission denied");
            break;
        }
      }
    );
  }
}

Note that the app might also call navigator.hasFrobulatePermission() at page load time to determine if the button should be enabled at all. This is a good way to provide the user with meaningful options.

The basic formula is:

  • A request*Permission() function that asynchronously returns the actual API
  • A has*Permission() function that allows the app to determine the current state without creating prompt UI when users might not be expecting it.
  • The ability to feature-detect the presence of the API

This pattern can be applied broadly and is flexible both to the needs of developers but also those of UAs and the users they work to protect.

Privacy Considerations

One possible concern with the hasFrobulatePemission() method is that it potentially leaks a previous decision.

Because of the async nature of this API style, UAs can mitigate against this leakage (to the extent they think this is a problem) by:

  • Only allowing calls to has*Permission() calls in response to user action (e.g., in the event handler for a non-synthetic click event).
  • Delaying the delivery of a value by a random time interval
  • Providing users visibility and control over if and when sites are allowed to request this data through system management and revocation UI

In general, it is good practice for UAs to enable users to audit and revoke the set of information that sites are able to collect. The amount of ambient information available to sites at load time is frequently enough to enable fingerprinting when a UA is not in a specific privacy-preserving mode.

The API pattern presented can function correctly in such a mode, always returning "default" to has*Permission() calls should UAs choose to implement.

Advantages and Opportunities

The above mitigation for privacy concerns is but one example of a very positive aspect of this emerging pattern: it allows UA's to make decisions asynchronously about when (and if) to show users UI for mediating various permission access.

Consider a situation in which a user interacts with a site frequently and the UA surfaces the use of frobulateAPI in secure UI which allows users to audit and revoke the capability on a per-site basis. In such a situation, a UA might possibly forego ever asking the user for explicit permission via infobar if the UA has knowledge that the user trusts the site in other ways -- for instance if the user visits frequently, has added a bookmark to their homescreen, and if the permission is not particularly sensitive but may be annoying (e.g., Notification).

Given the recent history of system UIs for vending and managing these sorts of capabilities, there is little reason to think that UAs will coalesce around a single UI treatment for all permissions; or that these treatments will be static over time.

The flexibility to change UI, to surface system use and capabilities in context, and to enable content to react to user preferences meaningfully are advantages to this API style. Experimentation and iteration are the crux of healthy UI. We hope this pattern can enable that sort of experimentation and provide a path away from the infobarpocalypse; not by depriving users of control, but by enabling UAs to do better.

@slightlyoff
Copy link
Author

Questions from TAG discussion:

  • what happens at runtime under revocation? UAs can reload apps/pages.
  • has*Permission() API example shouldn't be guarded by user interaction
  • I should discuss the downsides of requiring user interaction
  • would also be good to outline previous approaches and API styles (with examples)

@twirl
Copy link

twirl commented Oct 1, 2014

I think we also need to document all the use cases, especially those with no known solution (like accessing individual files or contacts).

@ClaesNilsson
Copy link

Thanks for this post Alex. I am editing the W3C SysApps TCP and the issues you discuss are very relevant for this API. I have created the issue sysapps/tcp-udp-sockets#81 proposing a method for web apps to get access to the API. The manner in which the permission is given is not defined (it could be based on ssl/tls, signed manifests, user consent, etc) and the ambition is to separate the API specification itself from the access/permission system that currently is not standardized.

My comments/questions on your post follow below:

  1. Do you envision a general Permission API supporting for example the ‘hasPermission’ and ‘requestPermission’ methods? Could for example this be an extension to the Permission API proposed by Mounir, http://lists.w3.org/Archives/Public/public-webapps/2014JulSep/0389.html? So for the TCP and UDP Socket API the method call could be:
    navigator.requestPermission(TCPUDPSocketAPI).then(…..?
  2. You state that the general pattern you are proposing only considers the origin preserving subset of capabilities. So with this restriction it may be possible to base a permission model for a TCP and UDP Socket API on user consent. However, there are use cases for a TCP and UDP Socket API that breaks the origin preserving model, for example multicast based service discovery of and communication with devices in local networks. So for this sensitive API we probably have to look at further standardized web security mechanisms.

Maybe this goes beyond the scope of your post but considering the point above we can look at the discussion on requiring authenticated origins, i.e. require secure https, for the Geolocation API. The discussion starts here Requiring Authenticated Origins for Geolocation API's. This means that if the user trusts the domain of the web application requiring access to the Geolocation API the user is protected from attacks from other network entities through server authentication, integrity protection and encryption. This means for example that injection of malicious Javascript and listening to sensitive data provided by APIs are made more difficult.

The problem with above is that any evil site can get a certificate and still cause damage to users. Root certificate pinning through pre-loaded or dynamically downloaded pins to define a set of trusted CAs helps but does not still guarantee that a CA is not compromised.

The 3rd level could be a signed manifest. In the manifest a “Content Security Policy” field defines which domains content can be downloaded from and to which domains access can be done and a “permissions” field defines which APIs the web app is allowed to access. A standard manifest is under specification by W3C, http://w3c.github.io/manifest/. However, signing manifests probably requires pre-loaded keys in the device, which is problematic for the open web.

Going back to the original proposal for a general pattern for “permission requesting APIs” the question is if we could create general “API access permission API” that hides the details of the permission giving system irrespective of whether this permission giving system is based on user consent, existing standardized security mechanisms or a combination of both?

WDYT?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment