diff --git a/caip-browser_extension_provider_communication.md b/caip-browser_extension_provider_communication.md new file mode 100644 index 00000000..99db9c75 --- /dev/null +++ b/caip-browser_extension_provider_communication.md @@ -0,0 +1,145 @@ +--- +# Every document starts with a front matter in YAML enclosed by triple dashes. +# See https://jekyllrb.com/docs/front-matter/ to learn more about this concept. +caip: +title: Web extension provider communication +author: Mark Stacey (@Gudahtt), Jiexi Luan (@jiexi) +discussions-to: +status: Draft +type: Standard +created: 2022-11-28 +--- + + + +## Simple Summary + +This CAIP discusses the motivation, specification, and rationale for a proposal aimed at improving how web extension wallets interact with websites. It outlines the current method of injecting JavaScript provider APIs into websites, its advantages, and its numerous disadvantages, such as security concerns, performance issues, and the risk of breaking websites. An alternative strategy is proposed that specifies a standard communication specification over a new transport layer which enables websites to be able to embed their own provider as a library, addressing the disadvantages of injecting providers into websites and improving web extension interoperability as a whole. + +## Abstract + +In the current web extension wallet ecosystem, most wallets communicate with websites by injecting a JavaScript provider API (`window.ethereum`) directly into webpages as a global variable. This method offers simplicity for web developers and ensures compatibility across extensions but raises significant concerns regarding security, performance, and potential disruption of website functionality due to the need for extensive permissions for the injection of additional code that have the potential to cause webpage breakage. An alternative approach involves websites embedding their own provider libraries, which could mitigate these issues by reducing required permissions, enhancing performance, and providing developers with greater control over provider integration. + +This proposal addresses the challenge of maintaining interoperability between web extensions and websites by standardizing an extensible communication protocol through the `externally_connectable` interface. Using the `externally_connectable` interface is less intrusive and more performant than the current pattern of injecting an inpage provider as a global variable. + +This proposal is not yet applicable to Firefox due to lack of support for `externally_connectable`, but they are considering implementing it. + +## Motivation + +Web extension wallets today will typically inject a JavaScript provider API into websites as a global variable. For example, in the Ethereum ecosystem this provider API is standardized in EIP-1193, and is conventionally injected as `window.ethereum`. + +This injected API strategy has some advantages: +* For websites developers, using a global variable is simple and requires no effort on their part to setup. +* It allows websites to support any web extension following this standard with no additional effort. + +However, the injected API strategy has many disadvantages: +* It depends upon the web extension having read and write access to every website the user visits, which is a scary permission that web extension authors might otherwise be able to avoid asking for. +* It slows down every website by injecting additional code to be parsed and executed. It even slows down the initial page load in most cases, because many web extensions inject the provider synchronously to maintain compatibility with websites that expect it to be available immediately. +* In some cases, injecting code into a webpage may break its original intended behaviors. +* It provides no way for web extension authors to safely make breaking changes to their provider API without having to also inject extra code for the purposes of maintaining backwards compability for websites that may still rely on those legacy APIs. + +An alternative strategy would be for a website to embed its own provider. A provider could be offered as a library, to be embedded by the website author. This strategy can address all disadvantages of the injected provider approach: +* If this strategy became widespread enough, it would allow some web extensions to stop asking for write access to all pages. + * NOTE: Should mention discovery somewhere since that will require injection still +* The website author can control when the provider is initialized, and fine-tune performance. +* No code needs to be injected, so websites would no longer be broken by injected code. +* The provider library can be published with breaking changes, allowing changes to the API without needing to embed legacy code in each website. + +A provider library can be similarly easy to use for website authors as well, requiring nothing more than a single script tag to import a library and get an equivalent experience to using an the injected provider. + +Web extension inter-operability is a challenge for embedded providers though. That is what this proposal means to address. Today there is no way to write a provider such that it is compatible with any web extension. Web extensions differ today in how they communicate with wallets, from the messaging system used to the messaging format. These details often aren't publicly documented or treated as a public-facing API, so they can change without notice, making it risky even to embed support for popular conventions used today. + +A standard method for providers to communicate with web extensions would allow website authors to embed their own providers without losing web extension inter-operability. + +## Specification + + +### Language + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", +"SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" written in +uppercase in this document are to be interpreted as described in [RFC +2119](https://www.ietf.org/rfc/rfc2119.txt) + +### Summary + +Web extensions should expose a standard interface over [`externally_connectable`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/externally_connectable) to enable the inter-operability of embedded providers. + +### Message format + +```json +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "/caip-x/schemas/provider-request.schema.json", + "title": "Provider / Web Extension message", + "description": "A request sent between a provider and a web extension.", + "type": "object", + "properties": { + "type": { + "description": "The message type, used to identify this as a CAIP-X message", + "const": "caip-x" + }, + "data": { + "description": "A CAIP-25/27/285/312/319 JSON-RPC message", + "type": "object" + } + }, + "required": ["type", "data"] +} +``` + +### `externally_connectable` + +A web extension can use the `externally_connectable` manifest field to accept messages from websites and other extensions. This permission can be configured to limit which sites and other extensions can send messages to the web extension. How this permission is configured is out-of-scope for this proposal; this proposal only concerns sites that the web extension allows messages from. + + +The web extension can: +* handle a connection from the website or other extensions by using `chrome.runtime.onConnectExternal.addListener((port) => {...})` +* send messages by using `port.postMessage()` +* receive messages by using `port.onMessage.addListener()` + * incoming messages should be validated according to the message format specification above + +The webpage embedded provider can: +* initiate a connection with the web extension by using `port = browser.runtime.connect()` +* send messages by using `port.postMessage()` +* receive messages by using `port.onMessage.addListener()` + +### Caveats +Currently Firefox does not support `externally_connectable` yet, but they are [considering implementing it](https://bugzilla.mozilla.org/show_bug.cgi?id=1319168). Meanwhile, extension wallets on Firefox will need to continue injecting an inpage provider for the website. + +## Rationale + +The current reliance of extension wallets on content scripts is highly unideal as it is overly permissive in regards to what the wallet is ultimately trying to achieve which is to provide an entrypoint for a website to the wallet. This paradigm exists because of website reliance on inpage injected providers. + +A better pattern would be for dapps to be able to connect directly to the wallet without the wallet needing to inject and run code on the webpage at all. This can be achieved by using the the `externally_connectable` transport layer, which allows website to communicate with wallets without requiring code injection. This reduces permissions required by extensions and improves website performance. Additionally, this simplifies the complicated situtation website maintainers may find themselves in when it comes to building and designing around code being forceably injected into their pages. Not to mention the headache around debugging, monitoring, and triaging bug reports that may not have originated from their application logic but is being caught by monitoring tools. + +Unfortunately, using `externally_connectable` by itself will not be sufficient as dapps will still need to know how to actually communicate with the wallet, but if each wallet has a differently shaped API then this approach cannot scale. Because of that, this CAIP also proposes an extensible message format to use in conjunction with `externally_connectable`. Using a standardized message format allows websites and wallets to be inter-operable in the same way they are today with EIP-1193. It is backwards compatible as it allows the website and extension to disambiguate the newly proposed messages from any existing messages that may already be sent on this interface. Finally, it is forwards compatible it also enables extensibility for different formats in the future. + +Web extension inter-operability is the key to enabling generalized provider implementations. Generalized provider implementations allows for convenient adoption by websites, leading to more wide spread adoption of the standard as a result. + +## Test Cases + + +## Security Considerations + +Inclusion of `externally_connectable` does not require the user to also give the wallet extension permission to use `content_scripts`. Content scripts are a higher-privileged permission with ability to run scripts on any webpage. `externally_connectable` simple enables a way for webpages to directly communicate with extensions. + +## Privacy Considerations + +Exposing the wallet API over `externally_connectable` opens a migration path towards a bring-your-own provider model in which web extensions can reduce their fingerprint by no longer having to inject their own provider into every webpage. + +It should be noted however that this API is still fingerprintable based on the return values from a CAIP-25 request. A malicious actor could make several CAIP-25 requests with a single scope and/or account ID to determine what scopes and/or accounts the wallet supports based on whether that request returns immediately with an unsupported error or not. This can be mostly mitigated by rate liming and disallowing concurrent CAIP-25 requests from the same origin. It is no worse than the current status quo with EVM methods. + +## Backwards Compatibility + +`externally_connectable` does not conflict with `content_scripts` which currently enable injected providers. This CAIP does not require changes to an extension wallet's existing injected providers pattern. + +The message format used by this CAIP allows extensions that already use `externally_connectable` to serve requests to continue doing so. This is because the new message format introduced by this CAIP is easily identifiable and can be easily ignored/filtered by any pre-existing handlers. Additionally, the message format is also flexible enough to be compatible with future APIs that may also share the same `externally_connectable` entrypoint. + +## Links + +* [externally_connectable](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/externally_connectable) +* [Mozilla bug report: "Implement externally_connectable from a website"](https://bugzilla.mozilla.org/show_bug.cgi?id=1319168) + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE).