Authentication recommendation proposal for new dapps

Internet Identity popularized the use of un-scoped delegations, which effectively provides every dapp with an expirable private key.

The benefit is that apps don’t need to request a wallet approval prompt to makes calls to other canisters (because they effectively have the private key) but comes at the expense of an isolated ecosystem of apps and more complexity for developers entering the ICP ecosystem.

Therefore I propose the following guideline that outlines how to manage user authentication for applications, to be written in DFINITY docs if acceptable:

  1. Apps that Access User Assets: For applications that need access to a user’s assets, request a user’s wallet address (principal/subaccount) and subsequently request one-off approvals for using them. Examples: DEX, marketplace.
  2. Apps that Save User Data: If your application requires frequently saving user data to your canister backend that needs to be publicly queryable, reusable by other apps, and you don’t want to trigger wallet prompts for update calls to your canisters, request a delegation scoped to your canisters. Examples: Social apps (i.e. Farcaster on ETH).
  3. Isolated Applications: For applications that do not need access to user assets and are designed to be completely siloed from the rest of the ecosystem, request a delegation with no scope. This is today’s Internet Identity flow. This makes vendor lock-in easier, so should mostly be used for private applications.

Thoughts? @sea-snake @peterparker @frederikrothenberger @skilesare

4 Likes

Don’t necessarily agree on 2, ideally I’d use a scoped delegation for everything.

A canister can keep a list of scoped delegation identities for a given un-scoped identity and allow a user to add additional identities with a one-off call. Added benefit is the possibility to revoke any of these identities, if for example multiple frontends have been given access, a user can revoke access to a given frontend. The dapp could (and probably should in most cases) still use the un-scoped identity as user id on the platform shown to other.

The comparison I made before, a scoped identity is like a private identity within a dapp and a global un-scoped identity is like an email or phone number tied to this account that makes dapps interoperable.

Sadly, if a dapp want to create a vendor lock in, I don’t see a way to prevent it. A dapp could always use the global un-scoped identity only to register and e.g. a random identity in the browser cache as authentication afterwards. On new devices, the dapp then requires the global un-scoped identity once to login before generating another random session identity for this device. All of this can be done without ever making the user’s global un-scoped identity known to other users and/or platforms.

Best that can be done against vendor lock-ins is open standards, best practices and healthy competition so users can vote against unfair practices with their foot. But there’s no technical way to prevent it from happening on the IC as far as I’m aware.

1 Like

@dostro: I don’t quite see how the vendor lock-in is affected by the use of global / non-global delegations.

It is up to the IDP / wallet to decide which type of delegations to give to whom. In both scenarios, if you want to use a different front-end to operate on the same canisters you need to violate some isolation mechanism:

  • If it is a global delegation, you’ll violate ICRC-28 since the other front-end is not listed as trusted
  • If it is a session delegation, you’ll give the same principal to a different origin that is not a valid alternative origin

In both cases it is up to the wallet / IDP developer to decide whether they want to add such an “escape hatch” to their product and let users break dapp isolation.

I agree with @sea-snake, you cannot solve vendor lock-in by mandating developers to refer to your identity in a specific way. Vendor lock in is not only an issue of identity, but also of APIs, data availability, etc.

In my opinion, dapps should request a delegation of some type for session purposes, and request transaction approval for asset management. These two ways of operating should coexist, without a dapp developer making additional assumptions about the nature of the delegation or how it relates to the principal that the assets are tied to.

2 Likes

Let’s call them wallet delegations and expirable private keys, since that’s more aligned with the reality of delegations today.

Currently, the main narrative within the ecosystem is that every app should receive the user’s private key from II so that wallet approvals are not necessary. You are suggesting we continue with this narrative and standardize a complex access control backend that Dynamic has raised enormous capital to build for other networks. To think we can standardize this and make it easy for devs to use in a reasonable amount of time is not realistic IMO, especially since that’s basically what we did with NFID Vaults and I can say it’s absolutely not something I’d recommend every developer do when they enter the ICP ecosystem.

I believe our overarching goal is to help support a cohesive, interoperable ecosystem of applications where users can connect with consistent identifiers, making it easier for developers to read and write user data across the ecosystem (i.e. what assets they have and approval to exchange them). One approach to achieving this is by enabling a unified identifier for all connections - aka a wallet address. You advocate for access control with a multi-principal backend architecture. We should certainly have this one day when we have 2 years and the resources to standardize and create such tooling, but I’m not aware of any developer besides DFINITY with the resources to last that long, nor do I believe this should be the first priority over doing what other networks do.

I disagree because I believe it should be up to the dapp to decide if it wants to be interoperable with the rest of the ecosystem, and let the user make the final decision. In the case a dapp wants to be interoperable with other apps but remove wallet prompts for canisters it controls, offer the user the ability to safely connect their wallet address. In the case a dapp wants to be isolated by default, don’t offer the user this ability. In either case, signers should always give users the ability to connect anonymously, just like signers in every other network do.

As I mentioned above, it would take years to standardize and build sophisticated-enough tooling to make this easy for devs. Giving apps an expirable private key is a “hammer” that you advocate to then build guardrails around with an access control backend architecture. Dapps should firstly request an account because Web3 is about making an identity a first-class citizen on the internet. Dapps should secondly request an account with the authority to make calls on behalf of the user in a safe way (i.e. ICRC-28) because it keeps the account a first-class citizen while also offering a safe way to remove wallet prompts. Dapps may thirdly request a “hammer” (private key) with authority to make all calls on behalf of the user, making user accounts a third-class citizen. There is no situation where I think this is a great idea, at least not without extensive tooling to abstract away the complexity of working with access control groups (i.e. rebuild Dynamic as an open-source toolkit for ICP devs).