EIP-1102: Opt-in provider access

I like the proposal,

Thinking about it, could the request ask for a specific network too ?

window.postMessage({ type: 'WEB3_API_REQUEST', id: 'METAMASK', networkId: 4 });
1 Like

Yes, this proposal is deliberately forward-extensible, with an eye for requesting networks, and all kinds of specificity.

Another privacy enabling feature that would be cool would be that when the dapp asks the provider for the user’s address, the provider returns a randomly generated 20-byte number. The provider remembers this for the session and returns the same one each time. Anytime the dapp makes a request of the provider, the provider will search and replace that 20-byte number with the user’s actual address before submitting to the blockchain or returning a result to the dapp.

This would allow users to use dapps without giving up their actual address to the dapp.

1 Like

sounds like a good idea, but in practice, the address will be detectable through transaction/message being signed.
Also if the provider replace the number with the actual address for every web3 call, you would just need a contract with a public method that return the msg.sender to get back the actual address. If it does not replace the number, then you forbid the dapp to actually get info about that address.

Hmm, good point that a dapp could get around this by creating a contract that returns the address encrypted, and the website has the decryption key. Then MetaMask wouldn’t know it needs to swap anything out, and thus return the address to the dapp.

After more discussion on the EIP pull request and valid points by @MicahZoltu and @Arachnid, this proposal shifted from defining an abstract platform-agnostic protocol to defining a concrete DOM-specific protocol for user-approved web3 access. All dapp browsers that currently expose web3 also expose a DOM, so it makes sense to make an immediately-actionable EIP to standardize existing DOM-specific implementations using uniform APIs.

@bitpshr Have you considered extending this pattern to host specifics API?
I guess it could be as simple as requesting using a different type.

e.g. for status: access to whisper key, contacts, …

1 Like

Good idea for proposing 1102.

  1. Agree that having an approval process will greatly help with privacy and, i believe it helps security in the long run. Since attacker can easily find out account balances and token holdings by simply having account address, if the web application use the account address together with many other information available at the time of loading web app on browser, it will make it much easier for attacker to collect large amount of accounts and scan to find accounts valuable and vulnerable to exploit.
  2. Since this adds a handshake, have you considered adding API versioning information exchange at the time? In that we can allow future development to make use of it.

Again, kudos for proposing it.

@xinbenlv

1 Like

Why reinventing the wheel and not using existing Permissions API?
See, for example: https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API/Using_the_Permissions_API

Hi @serso. While the experimental Permissions API initially seemed promising for the purposes of opt-in provider access, key limitations made it less than ideal for this specific use case.

The current Permissions API doesn’t offer the ability to define custom permissions and was only intended to provide a better API to request native browser permissions like “geolocation” or “notification”. Requesting a non-standard permission - like “ethereum” - throws an Error. While dapp browsers could override the permissions.query method to explicitly handle a non-standard “ethereum” permission request, malicious sites could then initiate this non-standard request and know they’re in a dapp browser if no Error is thrown as expected. For example:

The following non-standard permission request will immediately throw:

navigator.permissions.query({ name: 'ethereum' });
// TypeError: The provided value ethereum is not a valid PermissionName.

If dapp browsers override permissions.query to handle “ethereum” requests, it will not immediately throw:

const originalQuery = navigator.permissions.query;
navigator.permissions.query = (query) => {
	if (query.name === 'ethereum') {
		// Handle provider request...
    } else {
		return originalQuery.apply(navigator.permissions, arguments);
    }
};
navigator.permissions.query({ name: 'ethereum' });
// undefined

Because the Permissions API is meant only for predefined permissions and doesn’t (yet) allow for dynamically-defined permissions in a given context, any support at all for a non-standard “ethereum” permission would allow malicious websites to fingerprint and track Ethereum users. The Permissions API also displays a default browser confirmation dialog and doesn’t allow custom confirmation UIs; this limits the type of information that can be presented to the user, but this issue is less important.

Thanks for your comment, let me know if you have any other ideas around this. I agree that the Permissions API would be great to leverage if it was safely usable for non-standard permission types. We’ll continue to monitor it closely for EIP-1102 applicability.

Hi! Nice proposal.

In the proposal it says that:

IF user rejects
IF non-Ethereum environment
    NOOP[4]

I think it would be a good idea to also notify the dapp when the user reject the access, so can detect the case and give a better user feedback.

Also, would it make sense to allow also to request a list of provider details, so you can prompt the user with a wallet selector, then the user chooses his preferred one, so we can request the injection of that particular wallet?

Hi again, also I was wondering why do we need still a global variable ethereum for the new method. Could we just send it as as an argument when the extension triggers the listener?

Another small detail, why the event s called message and then we check for the type ETHEREUM_PROVIDER_SUCCESS?. I think the event name can be an arbitrary name, so it can be more specific. Right? Besides I think message is kind of standard one used for websockets: https://developer.mozilla.org/en-US/docs/Web/Events

WDYT?

1 Like

Thank you for the prompt response, @bitpshr. I’ll bring it up for an [internal] discussion at Opera. Maybe we can help you with the web standards.

2 Likes

Hi @anxolin. Thanks for your comments. Responses below:

I think it would be a good idea to also notify the dapp when the user reject the access, so can detect the case and give a better user feedback.

If dapps were notified when a user rejected provider access, that would expose enough information to allow malicious sites to continue uniquely identifying Ethereum users. For example, if a malicious site initiates a provider request, a user rejects access, and the malicious site is notified, the site now knows the user is an Ethereum user within a dapp browser. Even without provider or account access, a malicious site can still run targeted ad and phishing campaigns simply by knowing a certain IP address is associated with Ethereum use.
I agree that this means dapp UX will need to change, and there are discussions around this very topic. Still, the lack of notification of rejection is a crucial piece to this protocol’s privacy.

Also, would it make sense to allow also to request a list of provider details…

This is a very good idea. The request protocol outlined in this proposal was intentionally designed to be open-ended so that future EIPs can build on it and add new information to the request. @danfinlay had some thoughts around this idea as well, such as requesting a specific account type on a specific network when requesting a provider.

Could we just send it as as an argument when the extension triggers the listener?

Because the provider request protocol leverages window.postMessage, it’s not possible to return the provider object as an argument to the listener. Data sent via window.postMessage is serialized using structured cloning which means the provider object’s functions would be lost. Additionally, while some browsers support a third transfer argument when calling window.postMessage to transfer an object between two windows, browser support is very limited. The only viable way to respond with a full provider (for now) is global injection.

Another small detail, why the event called message and then we check for the type ETHEREUM_PROVIDER_SUCCESS? I think the event name can be an arbitrary name, so it can be more specific.

The protocol makes use of window.postMessage to request a provider, and then listens for the browser to post a message using window.postMessage in response. You bring up an interesting point: the dapp browser could respond by triggering an Ethereum-specific event listener, negating the need to sort through other posted messages based on a type. Part of me likes the symmetry of posting a message, then listening for a posted message, but I will experiment with this today for security.

If we chose to emit an event instead of post a response message after user approval as suggested by @anxolin, dapp browsers could return the provider object to the event listener instead of injecting it globally.

We could go from this:

window.addEventListener('message', ({ data }) => {
    if (data && data.type && data.type === 'ETHEREUM_PROVIDER_SUCCESS') {
        const networkVersion = await window.ethereum.send('net_version', []);
    }
});
window.postMessage({ type: 'ETHEREUM_PROVIDER_REQUEST' }, '*');

To something like this:

window.addEventListener('ethereumprovider', (ethereum) => {
    const networkVersion = await ethereum.send('net_version', []);
});
window.postMessage({ type: 'ETHEREUM_PROVIDER_REQUEST' }, '*');

There wouldn’t be any immediate fingerprinting threat since an event would only be emitted after user approval. What do you think? @anxolin @ryanio @danfinlay @andytudhope @serso @brunobar79

3 Likes

Thanks for including me.

Choosing to emit an event sounds nice. However, I found when coding the implementation in Mist that assigning the provider to window.ethereum is actually nice because on page load if the dapp has previously been authenticated by the user we can load the page with window.ethereum already available without needing another request.

(Also in dev environments you could assume window.ethereum is available without needing to request it.)

With sending an event, we wouldn’t know when in the dapp lifecycle the listener would be set up to know when to emit.

Hi @ryanio, thanks for the feedback. I see your point about auto-injecting window.ethereum for already-approved sites, but this seems like an odd approach for approval caching since it requires dapps to check for window.ethereum before requesting a provider. If a domain has already been approved, rather than injecting window.ethereum automatically, why not still wait for a request, but respond immediately with the provider and show no user dialog? For reference, this is what MetaMask is doing. This approach allows domain caching (meaning users don’t have to constantly re-approve the same dapp) while imposing no additional code on dapps (they still request and add a listener like normal and don’t have to first check for window.ethereum.) It would also be trivial to dispatch an ethereumprovider event on the Window for local testing.

I’m starting to think the ethereumprovider event for success response is a cleaner API, but I’m definitely open to more thoughts.

1 Like

hey guys, I’m Andrey , implementing 1102 in status.im.

Agree with @bitpshr, ethereumprovider event looks cleaner, only one way how to get a provider, and safer, because we don’t post a message for success response. We also implemented in status immediate response if the domain was allowed before.

Do you plan to use CustomEvent? so it will be something like

window.addEventListener('ethereumprovider', (event) => {
    const networkVersion = await event.detail.ethereum.send('net_version', []);
});
4 Likes

So, re: The UX of EIP 1102 - looking for a way to guarantee both user control of privacy and preserve the relative ease of UX available with current Metamask/web3.

My general idea to solve this is basically to find a way to automatically push a refresh and injection of window.ethereum based on an application requesting that, upon install of Metamask, their still-open tab will be able to have a pending request of availability somehow notify the user that they’ve requested access, and then upon access being granted, initiate the reload and window.ethereum injection. Doing so would not expose any user information without their request, while still allowing applications to have relatively seamless UX flow (with the workflow, 1. Request Metamask install and/or request acceptance, 2. Detect Metamask has been opened and accepted). The en-queuing of such a request could be easily accomplished by storage of a specially keyed cookie or localStorage object (e.g., _web3_permission_pending_request). Any risk of accidental acceptance of a website’s pending permission requests could be mitigated by a combiation of (a) only showing requests originating from open tabs or domains, and (b) sufficiently clear UX on the part of the ethereum provider (e.g. Metamask). I strongly encourage an approach like this for keeping UX simplicity.

I like how clean this is, and would be in favor of it provided there are no major downsides we can think of.