This explainer proposes a new scope_extensions
Web Application
Manifest member that extends the concept of
app scope.
The scope of an installed web app "restricts the URLs to which the manifest is applied and provides a means to 'deep link' into a web application from other applications." User agents apply metadata in the manifest to documents within scope and often apply differential UX treatments to make it obvious when an app window navigates to documents outside of scope.
Currently, the scope of an app can only include URLs from a single web origin. Web developers who are interested in creating a web app with contents located in multiple different web origins find themselves unable to do so. This leads to a poor user experience when viewing documents that are part of the app but are technically out-of-scope. Users may be confused by browser UI marking the page as out-of-scope, and some desired app-like features such as deep linking may not function at all for out-of-scope documents.
Apps that wish to define a scope spanning multiple different origins could do so
using the scope_extensions
manifest field detailed below.
For each example below, developers have expressed interest in publishing a single installable app instead of one per origin with distinct manifest ids.
An app can have content from multiple Top Level Domains representing different locales. Each site is usually structured identically but could have differences in policies, displayed language, etc. Example:
An app can share shortened/vanity URLs and want those URLs to be captured to app windows when navigated. Example:
- https://youtu.be/dQw4w9WgXcQ is the shortened URL for https://www.youtube.com/watch?v=dQw4w9WgXcQ.
An app can also use different sub-domains to represent different locales. Example:
Some sites are structured to use sub-domains to represent different groups or organizations but may want to publish a single installable app instead of one for each. Examples:
- https://chromium.slack.org, https://foo.slack.org, https://bar.slack.org
- https://foo.zoom.us, https://bar.zoom.us, ...
Addressing this case is considered a non-goal for this explainer because it is not possible to enumerate and validate all of the matching sub-domains at install time. This work is left to a future explainer.
In Chromium browsers, top-level app window navigation to an out-of-scope URL
creates a notification bar - this notification bar informs the user that they
have navigated outside of app scope or to a different origin altogether and
provides a control to return to the start_url
. This UI bar protects the user
by providing useful information about the change in browsing context. It is not
dismissable without navigating the page back to the start_url
. Other browser
implementations may include similar behavior.
The app window shown above has navigated to a url (https://airhorner.com
)
outside of its scope (https://diek.us/pwinter/
). The white bar above the web
contents informs the user of this difference. This feature is important as it
aims to keep the user aware of the content's security context.
A developer may intentionally want to include documents from different origins in app scope. In this case, an obtrusive out-of-scope bar is not necessary and could confuse users.
When a page in an app window navigates to an out-of-scope URL, some browser
implementations may handle it by opening a new tab or browsing context in a
regular browser window. This can appear jarring for users if they are unfamiliar
with how scope works and the content appears to still be part of the app.
Developers can use scope_extensions
to ensure appropriate navigations do not
leave the app window.
Navigation capturing is a behavior that captures suitable browser window page
navigations and opening them an app window when the target URL matches the scope
of an installed app. This behavior is common on mobile platforms and is in
development for desktop platforms
in Chromium. Navigation capturing relies on the scope of an app to decide if a
navigation is entering or leaving an app. Developers can use scope_extensions
to ensure appropriate browser navigations open in an app window.
Navigation capturing can be combined with
launch_handlers
to create a tailored app navigation experience.
As app scope is used as the boundary for the application of manifest metadata
such as
theme_color
,
documents intended to be part of the app but are excluded from app scope cannot
take part and are not presented in a consistent manner. Window Controls
Overlay
is another example - the overlay is not applied to the titlebar when the main
document in the app window is out-of-scope.
-
Allow developers to define an app scope using a list of origins known at install time.
-
Allow developers to specify a scoping path for each listed origin similar to how the manifest
scope
field works. -
Allow web apps to capture user navigations to sites they are affiliated with. E.g. "Productivity Suite" app capturing links navigations to presentations.productivity.com.
-
Allow developers to define an app scope that includes an unknown number of origins at install time, such as could be done using URL pattern matching or by including all origins of an entire site.
-
Allow developers to define scope in a single origin using more complicated URL filters, lists of filters, etc.
-
Allow developers to exclude URLs from app scope.
To extend app scope, a developer will:
-
Add a
scope_extensions
section to the web app manifest which lists one or more extensions. -
Host a
.well-known/web-app-origin-association
file at each additional origin. This file establishes a two-way handshake between a unique app and the origin owner. It also contains any fine-grain URL filters needed to control scope.
Example manifest located at https://example.com/manifest.webmanifest
:
{
"id": "https://example.com/app",
"name": "My App",
"display": "standalone",
"start_url": "/app/index.html",
"scope": "/app",
"scope_extensions": [
{ "type": "origin", "value": "https://example.co.uk" },
{ "type": "origin", "value": "https://help.example.com" }
]
}
The "Example" app has a regular scope of http://example.com/app
and is
extending its app scope to the origins https://example.co.uk
and
https://help.example.com
.
- Each entry in
scope_extensions
must contain bothtype
andvalue
string fields. type
must be"origin"
. Other types could be added in the future. One future addition could be asite
type which includes a dynamic number of sub-domain origins (stated above as a non-goal).value
must a valid URL. The URL is converted to an origin.
A web-app-origin-association
file must be served from
https://<associatedorigin>/.well-known/web-app-origin-association
. An app is
allowed to extend its scope to this origin if their manifest id is found in this
file.
Example association file located at
https://example.co.uk/.well-known/web-app-origin-association
:
{
"https://example.com/app": {
"scope": "/app" // Evaluates to https://example.co.uk/app
}
}
- Each dictionary key must be a validly formatted manifest id.
- Each dictionary value must be an object.
- Each dictionary value can optionally contain a
scope
string. If not provided,scope
defaults to/
. - This
scope
configures the extension scope each identified app is allowed to utilize. - This
scope
works the same way as thescope
member in the manifest and is relative to this origin.
If an origin A adds a web app B to its web-app-origin-association
file, A is
implicitly authorizing app B to intercept navigations to URLs in A. This implies
that app B can potentially spoof origin A and therefore it is advised that
origin A and web app B should be owned by the same entity.
User agents may perform link capturing for user navigations within a web app's extended scope and launch the web app instead of performing the navigation.
The [launch handler][launch-handler] proposal enables sites to reroute app launches into existing web app contexts.
The combination of link capturing, launch handler and scope extensions leads to the following attack vector:
- User installs the "TestApp" web app from app.com.
- TestApp's scope includes site.com with valid origin association.
- TestApp sets its
launch_handler
to{ "client_mode": "focus-existing" }
- User clicks on a link to site.com.
- Navigation is captured by an existing TestApp window that is brought into focus and has a LaunchParam is enqueued.
- TestApp is now aware that the user is navigating to site.com and could perform a fake navigation with the intention of duping the user into thinking they're on site.com.
-
More fine-grained scoping mechanisms such as include/exclude lists or URL patterns. These mechanisms could be reused in 3 difference places: in the association file, in
scope_extensions
in the manifest, at the top level in the manifest. -
Change the constraint on manifest URLs that are bound by scope (except for
start_url
) to instead be bound by the extended scope. Validation of the associated origins is not required for these URLs to be part of a valid manifest. Prior to validation the URLs must be treated as if they were not specified. -
Add an
"authorize"
field toweb-app-origin-association
e.g.:
{
"web_apps": [{
"web_app_identity": "https://example.org",
"authorize": ["intercept-links"]
}]
}
This opt-in serves as a signal of trust from the associated origin to allow the web app to [capture navigations][link-capturing-from-another-origin] into the associated origin.
When an application uses scope_extensions
to expand its scope, each
additional origin's site permissions remain the same. Expanding scopes does
not imply any change in permissions.
For added security, app windows may want to implement more visible UI that displays the current URL being served, as well the privacy and permission information.
Domain, privacy, and permissions info in the app menu.In the implementation shown above, the domain, privacy, and permission information for the current origin can be found in the app menu. The discoverability of this information has room for improvement.
The scope_extensions
proposal is a replacement for part of the
url_handlers
proposal with the following changes:
- Re-orient the goal to be focused just on expanding the set of origins/URLs in the web app's scope. Remove the goal of registering web apps as URL handlers in the user's operating system. That behaviour will be covered by individual browsers optionally offering users the choice to capture link navigations as web app launches.
- Rename the new manifest field from
url_handlers
toscope_extensions
to reflect the change in goals. - Move the association file from "/web-app-origin-association.json" to "/.well-known/web-app-origin-association". This better conforms with RFC 8615.
- Change the association file entries to be keyed on the web app identifier rather than the web app's manifest URL (the former having been added to the Manifest spec in the interim).
- Replace
"paths"
withscope
in the association file entries. - Add an "authorize" field to the association file entries for the associated origin to provide explicit opt-in signals for security sensitive capabilities.