EIP-1102: Opt-in provider access

What I had in mind was simply the dapp instructing the user to install the extension and then click the icon in the navbar to enable or similar. It’s possibly worse UX than an app-initiated request, especially one based on a mouse/keyboard/touch event, although I suspect that if it’s standardized and intuitive then it’s an issue of training the user to perform a single action for provider injection, as opposed to a different action for each site (if access cannot easily be requested dynamically by the app). If the available options for whatever reason can no longer include an app-initiated request, then something like that becomes a must. The UX issues are of course significantly different on mobile and desktop. Anyway, this may be out of band for the discussion.

The protocol defined in EIP-1102 was originally intended to both standardize the way dapp browsers expose a provider and to do so in a way that makes it impossible to uniquely identify Ethereum users from non-Ethereum users. Despite the technical feasibility of this approach and the “total privacy” it provides, it requires a monumental shift in dapp UX: user on-boarding is made more difficult because dapps can’t detect if a user has a dapp browser installed. We initially thought this level of privacy was clearly ideal, and only after the announcement of our rollout plan did the implied dapp UX receive full scrutiny.

Rethinking total privacy

Taking a step back, the original motivation behind EIP-1102 was to make provider exposure less obtrusive and safer. The proposal satisfies these requirements by requiring explicit user approval before exposing a provider.

But the proposal goes one step further by also removing any fingerprintable API surface from the dapp context. This means that dapps aren’t informed when a user rejects provider access or if they don’t have a dapp browser installed. The idea behind this was to prevent malicious websites from associating IP addresses with Ethereum use and running targeted attacks. But this type of total privacy becomes less important as more browsers with unique user agents add crypto features such as Brave, Opera, or Toshi. Since these browsers are already identifiable by user agent and because they all support Ethereum use either directly or indirectly, malicious sites can already fingerprint many users as potential Ethereum users regardless of EIP-1102 or an injected provider.

Considering these new UX findings and realizing that this type of fingerprint prevention is firmly stopgap in a future of maintstream, crypto-enabled browsers, it feels natural to revisit the goals and approach behind EIP-1102.

An updated proposal

After talking with various community members, weighing informative UX feedback, and realistically analyzing security risks around provider exposure, we drafted updates to EIP-1102 in which DOM environments expose a read-only provider until full provider access is approved by the user. By injecting a restricted provider on page load, dapps can still detect correct browser installation and initiate RPC requests to properly render their UI; by requiring user approval before fully-enabling the provider, users are still protected from unsolicited transactions and unwanted account exposure on untrusted websites.

EIP-1102: Proposed updates (feedback encouraged)

cc @danfinlay @andrey @serso @ryanio @dustinvs @rmeissner @JaceHensley @ricburton @dustinvs

3 Likes

Hello Paul, I am Everton Fraga from the ethereum Mist team.

Great job on the proposed changes. I am glad you considered the user-agent case, as Mist users are not protected against this kind of targeting.

But I thought about something that was sitting there even before you revisit this subject: if the majority of dapps happen to request {type: … id: 'metamask'} it would make no sense to non-Metamask DOM providers to take id into consideration and decide if it should respond to the request or not — they’d respond positively anyway.

So, to be future-proof and mitigate the risk of unnecessary fragmentation – or a potentially useless id parameter — if a dapp wants to make anything specific to metamask, instead of:

DO REQUEST METAMASK_PROVIDER
  SHOW FoxWithAHat
END

It could perform:

DO REQUEST ETHEREUM_PROVIDER
  IF PROVIDER === METAMASK
    SHOW FoxWithAHat
  END
END

Cheers!

Hi @everton, thanks for the feedback. The id property was originally intended to allow dapps to specify which wallet provider should service their request in scenarios where a user has multiple Ethereum extensions installed, but you’re right: this shouldn’t be codified into the proposal. If an extension or browser wishes to only respond to specific provider requests, they can establish their own proprietary API to do so (an id property being one option.)

I updated the new draft of the proposal accordingly. Thanks again for the input.

1 Like

After successful community iteration with additional browser teams and dapp developers alike, EIP-1102 has been formally updated to reflect the latest thinking on user-approved provider exposure:

Browsers expose a provider populated with no accounts by default. Before initiating any RPC request that requires an account, like eth_sendTransaction, dapps must request a full provider by calling a new provider method, provider#enable. This method triggers the user interface that allows the user to approve or deny full provider access for a given dapp. If the user approves full provider access, the provider is populated with accounts and thus fully-enabled; if the user denies full provider access, the provider is left unchanged.

This latest version of EIP-1102 avoids any previously-discussed UX issues and is now live for further review. The new protocol actively being investigated and implemented by privacy-conscious browsers including MetaMask, Mist, Status, and imToken.

Additional feedback is both welcomed and encouraged.

3 Likes

Personally I preferred the event driven solution more, since it also gave an alternative to the issue when the user has multiple web3 provider enabled. But as this might not really belong in this EIP it might make sense to remove it. (But I do think “multiple installed providers” is something that needs to be thought about, especially when more standard browsers add default support, see Opera)

Another question for me would be, are there any information exposed in the read-only provider, about what provider it is? I know from some of our projects that they require meta-mask right now. So currently they check isMetaMask, but by exposing this method in the read-only provider you already give out quite some info again (or maybe not? :smiley: ). I am asking this because we will probably expose a similar method for our provider and it would be nice to be consistent.

EDIT:
Why not make it possible that the injected ethereum object has information about all available providers. The dapp could then request which providers are installed and let the user choose (or if one is preferred just select that one).
When a provider tries to inject the ethereum object it checks if it exists if not it creates it. Afterwards it adds itself as a possible provider.
The flow for the dapp would stay nearly the same as right now, but it would be possible to support different providers.

Something I think was a mistake in the early designs of Ethereum was combining Ethereum network access and signing tool/credential store. I would love to take this opportunity to split the two.

A browser plug-in may provide both signing interface and Ethereum network interface, but the dapp should see them as two separate things, not a single thing.

As a dapp user, I may use some public node like Infura or a semi-private node like QuikNode for getting data from the blockchain and submitting signed transactions to the blockchain. I may then use a ledger or MetaMask for signing things.

If we take this approach, then there would be no “upgrading” your connection to full access. The Ethereum network provider would not know (or at least claim to not know) anything about your accounts/credentials and it would have no ability to sign anything. The connected signing tool on the other hand would have privacy protecting features and not give up any information (even that it exists) until the user okays it.

4 Likes

Hi @rmeissner, thanks for your feedback. An additional EIP could definitely specify that an object should be passed to the enable method that specifies things like a specific wallet ID, a specific account type, a specific network, etc. This may be one path to solving the “multiple installed providers” situation you mentioned, but I think it’s probably beyond the scope of this initial EIP as you suggested. Your concerns around multiple providers being injected at once are interesting; exposure protocol aside, you’re right, this is a growing issue in the ecosystem.

Also, a provider implementation can expose any non-standard method (like isMetaMask) in either read-only mode or full mode if they desire; for reference, MetaMask plans to expose the isMetaMask method in read-only mode so dapps can still react accordingly even before user-approved full access.

Thanks again for your feedback.

1 Like

@MicahZoltu I agree that an ultimate separation of blockchain access and transaction signing would be very beneficial, and I think it’s a future we’re all working towards. This requires a fairly large technical shift in dapp browser architecture since most rely on exposing an augmented provider that intercepts certain RPC calls to populate accounts and to show approval UIs. We’re actively researching patterns to better separate signing interfaces from network interfaces, and thus the provider itself, but the ecosystem (and especially the user-facing dapp ecosystem) just isn’t there yet. I don’t feel that a proposal intended to standardize provider exposure and improve user privacy should determine the new paradigm for the separation of blockchain access and signers (though I’m very interested in continuing to research this more.)

At its core, this proposal directly strengthens user privacy by hiding user accounts and preventing unsolicited transaction requests on untrusted sites.

2 Likes

My understanding (which may be mistaken) is that this proposed change is a breaking change for all dapps, which is why it feels like a good opportunity to move over to a split interface.

Am I wrong that this is a breaking change for dapps?

(posted from a friend’s computer originally, reposting from my account)

Yes, the current (and most likely final) form of the proposal is technically a breaking change in that dapps must request a fully-enabled provider before initiating account-requiring RPC requests. But this amounts to a single additional API call; and while I see the value in the long-term goal of separating network access from signing capability, the magnitude and intent of this particular breaking change doesn’t seem to be a suitable platform for addressing this. In my mind, standardizing and restricting currently-unrestricted provider access doesn’t seem to be a valid opportunity to rethink how transaction signing works in browsers altogether.

I appreciate the feedback and hope to share ideas around the topic of network-signer separation in a new investigatory topic soon.

Hello again :slight_smile: I’m really liking the new spec! Seems very smooth. Only point of feedback would be maybe adding a field to the provider. Something like provider#readonly, which is just a bool value.

With that the dApp wouldn’t need to ask for permission (I know the browser would cache it so you don’t have to always ask permission) if the user had already granted permission and continue on with whatever flow the dapp has.

1 Like

I like the latest draft of the proposal, it simplifies the implementation considerably. I have a question about when to expect browser implementations.

As a developer, I’ve started a draft implementation of this proposal for our dApps that are in development. The cut-off date appears to be in November, but when can we expect to see the major browsers start incorporating this proposal for testing purposes?

I was hoping to see an implementation of this in Mint Browser (ie. window.ethereum doesn’t exist).

Hi, congrats about this great EIP, really good job done here.

So, I have read a couple of times, and correct me if I am wrong, the main reason of this EIP is improving the user privacy and maintain user experience in a safer way. The approach followed basically allows the user to connect to “ethereum” exposing some “private” information, having in the meantime a limited provider access (the reasoning for that has been well explained few comments above). In addition, interesting discussions on this thread (leaving aside the UX conversation) have been if this EIP should contain or not a mechanism for allowing the user to obtain this full access provider using a certain wallet, and also, a couple of indications about how to improve the protocol (relying on events).

Following on the previous discussion, despite I really understand your point to keep it as simpler, effective, less intrusive, reducing at max the necessary changes to adapt dapps, I also think this EIP should provide a solution for handling multiple wallets, especially if the scope of this EIP is targeted to chrome-extensions. Or at least define how we should proceed and then stick to it.

Some reasoning and spare ideas:

  • Right now having “race” conditions between providers is not just not cool, seems to be kind of weird.
  • Modify the whole process of connecting to ethereum but leaving in a weak (in the sense of not knowing “who” has provided you full access) mode the initial part does not look right
  • Adding/defining a mechanism when having 1+ web3 providers is not a big deal right now, but defining a new EIP it is.
  • Dapp browsers can easily ignore the listeners.
  • One logic assumption would be if two extensions are loaded, let the user choose

Following the solutions proposed in this thread, the event-driven solution (having a way to identify the provider who fired the success event) seems to be one of the best solutions, we can create a pool (implement the event mechanism as a simple carrier state transfer) of providers easily on the frontend (we could decide if that pool should be done by wallet providers or by ourselves in the frontend) and then, apply some extensible architecture over them: like a chain of responsibility pattern or a simpler Mapper pattern. In that way:

  • We can create continue with the typical open to extension-close to modification way: responsibility is in one small class no big impact if changes are needed, events do not need a response and can work in a clean way.
  • We can choose easily provider
  • We know easily which available providers we have
  • We have less cohesive architect solutions: not compromising implementation on providers’ code, keeping them optimistic, we still code “against” more abstract solution: not specifying the id on the call when login, having third-party integrations less intrusive (events are more independent and simpler than postMessage-open-opener way)

What do you guys think? Is that something you are willing to reconsider?

3 Likes

hey @benzap you can already try it in latest Status.im nightly build https://status-im.github.io/nightly/
Open Status, create account and login, go to profile tab, tap Advanced and enable Dev mode , then enable web3 opt-in option and open your Dapp, feel free to contact me if you have any questions.

2 Likes

Hi @benzap. In addition to the Status.im nightly build that @andrey mentioned, you can also test your dapp against MetaMask by using one of the latest builds from this pull request. We also added mock 1102 support that will be available in the next MetaMask release; this means dapps can begin to call provider.enable() to update their initialization flows prior to the MetaMask 1102 release date of November 2nd, 2018.

1 Like

Hi @apanizo. I agree that the issue of multiple extensions injecting (and thus overwriting) providers and Web3 instances is getting harder to ignore, especially with the growing number of extension-based wallets and signers. The issue is complex: who should decide which is the “correct” provider to use, the dapp or the user? For example, if a user has wallets A, B, and C installed as extensions and visits a dapp, should the dapp or the user choose which provider (and thus which wallet) to use for signing transactions? Since this EIP no longer changes provider injection (e.g. a provider is still injected at page load) and instead only restricts account access, I’m not sure how we could easily solve the multiple-provider issue in this proposal.

Still, the issue is a serious one. One possible solution would be a separate EIP that standardizes a <meta> tag that dapps should include. The presence of this meta tag would tell dapp browsers to inject a provider, and some attribute on the <meta> tag could indicate which provider should be injected. Something like:

<meta name="ethereum" providerID="metamask">

I think this is a complex problem that’s outside the scope of this proposal in its current form, but the <meta> tag pattern is one possible solution if dapps (and not users) should decide which provider and wallet to use.

2 Likes

@bitpshr @apanizo I think this is a pretty valid concern too! My opinion is that users should get to choose which provider they want. Maybe there can be some sort of mechanism where the EIP defines how providers inject themselves, but also how dapps consume them and display that info to users? I see it getting complex quickly, as maybe a user wants to change their provider for that dapp in the future… each dapp would need to implement that UI, or a fallback for just choosing one. Or maybe it can be twofold. If dapps want to control the provider, they use the meta tag. If dapps want to allow users to choose, they implement the UI. Thoughts?

Hey, is there plan to finalize this EIP ?

If so, it would be good to add a challenge payload option so the application can authenticate the address and be sure that the wallet really have the privateKey it claims to have.

There is a proposal described here : Automatic Authentication Signature

Basically the eth_requestAccount call would look like :

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "eth_requestAccounts",
  "params": ["0x55f55f5e5e6e7e867e"]
}

I would make the payload unrestricted so any data could be specified here.

To prevent application from using it to sign arbitrary messages, the payload is prepended with Automatic Signature before being signed.

Note that in the proposal mentioned above, I also propose to add a new json method eth_autoSign that would remove the need to add that facility in the eth_requestAccount method (since in that case authentication could be done any time afterward).
Nevertheless I would still argue that having it in eth_requestAccount is a good idea since it remove the need to make another call. It will also increase the likelihood of having such authentication mechanism available for apps without having to wait for the auto_sign proposal to be accepted

Note that we can make it backward compatible with existing app by making the challenge payload optional.

2 Likes

Hey,

while dapps can requestAccounts they currently cannot provide a UI for users to disable access (logout) without implementing it their own way (in an unreliable manner).

This feature is useful for applications that want to provide access choice for different wallets, built-in vs web-based wallet like portis, bitski…, as it would allow the application to disconnect from the built-in wallet, giving them back choice.

You can imagine the flow this way

if (isBuiltInWalletEnabled()) {
  use builtin wallet
} else if(iswebWalletLoggedIn()) {
  use web wallet
} else {
  offerChoice()
}

where the users could go back to the choice by closing access to the built-in accounts (if connected this way) or via the web wallet logout method (if connected this way).

While this is currently achievable by storing a cookie or a value in localStorage to remember the last used wallet and let user forget it, it is unreliable as it relies on cookie/local storage.

I propose thus

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "eth_closeAccounts"
}

that would reset account access for that particular application