As we were thinking of approaches to show identity information in Aragon, it became clearer and clearer that no single “source of truth” exists. In particular, there are several “identity” services today, including:
- Reverse-resolution of an address to its ENS domain
- Autark’s AddressBook app
- A BrightID score
- Loading profile information from 3Box or alternative sources (such as an ENS text record with an IPFS hash)
- Built-in Aragon client features, such as assigning a custom label to an address that is only available on the currently running instance of Aragon
Given the myriad of choices available to users, we thought identity resolution might best be handled in a generic and pluggable fashion that was user- and developer-extensible.
Introducing Identity Providers (“Identity Stacks”). See the technical interface draft.
Note that this is orthogonal, but related, to an address actually being a “member” of an organization.
Identity Providers
Note that this is an initial mock up and will likely not represent the finished product.
Users would be able to install (locally and for an organization) custom identity providers that are backed by an external service (e.g. ENS or 3Box). They would pick and choose which ones are enabled, and their resolution priority.
In the example above, the user has installed 3Box, ENS, and BrightID integrations, with their priority moving down from local custom labels to an built-in Identity app, to 3Box, to ENS reverse resolution, to BrightID.
For ease of implementation, Identity Providers are initially “all or nothing”. This means that they are either used completely, or not all at, so a user cannot pick and choose which aspects they’d like to use from each provider (e.g. you cannot choose to use 3Box solely for someone’s profile picture, but resolve their name from ENS). If a provider resolves to metadata, it is used and no more resolutions are performed.
For performance concerns in the client, we may restrict the number of providers that can be enabled at the same time, and may only use the highest-priority provider when rendering an AddressBadge
without its popover, i.e. this state:
Degrees of Freedom
This is the most tricky aspect, as we don’t know the full range of information available from identity services. We need help making this generic!
The basic AddressBadge
implements a “badge” and “popover” state. The badge itself (see above) can have its icon (defaults to identicon) and text field (defaults to address) changed. The popover starts off as:
But can be extended to (see highlights for changeable aspects):
This is fairly limiting, with only four degrees of freedom:
- Name
- Provider type (shown above, is the local custom label)
- Image
- Action (and label)
In particular, some identity services can provide aggregated sources (e.g. 3Box and Keybase can provide Facebook, Twitter, LinkedIn, etc.), and with the current degrees, you would need to use a separate identity provider for each source, even though they’re backed by the same service. I find this mostly reasonable, since most people have established preferences of which sources of identity they prefer, but can be a challenge to technically optimize.
We may also need an additional customizable area for putting the most important information related to an identity (e.g. BrightID’s score or a twitter handle).
Custom Actions
Of particular difficulty is the customizable action in the popover. We really need help making this generic enough!
The initial implementation of an Identity Provider, custom local labels, only involves a trip to localStorage
or an indexedDB
database in the browser. As such, it’s fairly easy for the Aragon client to know how to render input fields as well as apply the modification:
All “Save” does is set that input field’s string to be associated with the address in localStorage
or indexedDB
.
However, there are at least the following other types of modification actions:
- An HTTP request
- An Ethereum transaction
- A signature from the wallet
And potential inputs:
- Images
- Dates
- Etc.
The easiest way out, which may become the design recommendation, is to simply link to the external service, so that a user would manage their profile from there (or view more of the profile, if it’s not their connected address).
More Technical Details
Apps
As apps are in control of rendering AddressBadge
s, the responsibility of resolving identities is solely on app developers. A pre-built component will likely be available, and the “resolution stack” will be completed from Aragon (so apps are completely agnostic to the providers and don’t need to request which one they’d like to resolve with).
Some of these providers may take longer than others to resolve, and all of it will be done asynchronously, so it may also be necessary to design some sort of loading state in the AddressBadge
.
Aragon client
As noted, the client will be responsible for actually resolving and applying the “resolution stack”, and sending the results to apps.
Identity Providers
Given that any installed providers will only be loaded at run-time, and could be made by anybody (potentially malicious), sandboxing is a strict requirement.
As such, I envision these providers to be self-contained packages that are run in isolated WebWorker
instances, similar to how app background scripts are run today. These should implement a standard API, for resolving and modifying metadata, that is accessed through the WebWorker.postMessage()
API (potentially using @aragon/messenger
).
An abstraction like @aragon/client
may still be necessary, if they require access to web3; they should only be allowed to read from contracts.
These providers would be published and made available to Aragon clients through an aragonPM instance (again, similar to how apps are published and made available today).
Modification
The current IdentityProvider
interface includes IdentityProvider.modify()
as a generic API to modify an address’ associated metadata.
The return type is particularly difficult to get right, but as an initial thought, we could implement something like:
return {
type: 'success | transaction | signature',
data: 'transaction data | signature data'
}
With the assumption that any HTTP requests are able to be completed within the WebWorker
itself.