0.0.1 • Published 5 years ago

argos-did v0.0.1

Weekly downloads
-
License
MIT
Repository
github
Last release
5 years ago

Argos-DID Library

DID Specification | ERC-1056

This library conforms to ERC-1056 and is intended to use Ethereum addresses as fully self-managed Decentralized Identifiers (DIDs), it allows you to easily create and manage keys for these identities. It also lets you sign standards compliant JSON Web Tokens (JWT) that can be consumed using the DID-JWT library.

A DID is an Identifier that allows you to lookup a DID document that can be used to authenticate you and messages created by you.

Argos-DID provides a scalable identity method for Ethereum addresses that gives any Ethereum address the ability to collect on-chain and off-chain data. Because Argos-DID allows any Ethereum key pair to become an identity, it is more scalable and privacy-preserving than smart contract based identity methods.

This particular DID method relies on the Argos-Did-Registry. The Argos-DID-Registry is a smart contract that facilitates public key resolution for off-chain (and on-chain) authentication. It also facilitates key rotation, delegate assignment and revocation to allow 3rd party signers on a key's behalf, as well as setting and revoking off-chain attribute data. These interactions and events are used in aggregate to form a DID's DID document using the Argos-Did-Resolver.

An example of a DID document resolved using the Argos-Did-Resolver:

{
  '@context': 'https://w3id.org/did/v1',
  id: 'did:argos:0xb9c5714089478a327f09197987f16f9e5d936e8a',
  publicKey: [{
       id: 'did:argos:0xb9c5714089478a327f09197987f16f9e5d936e8a#owner',
       type: 'Secp256k1VerificationKey2018',
       owner: 'did:argos:0xb9c5714089478a327f09197987f16f9e5d936e8a',
       ethereumAddress: '0xb9c5714089478a327f09197987f16f9e5d936e8a'}],
  authentication: [{
       type: 'Secp256k1SignatureAuthentication2018',
       publicKey: 'did:argos:0xb9c5714089478a327f09197987f16f9e5d936e8a#owner'}]
}

On-chain refers to something that is resolved with a transaction on a blockchain, while off-chain can refer to anything from temporary payment channels to IPFS.

It supports the proposed Decentralized Identifiers spec from the W3C Credentials Community Group.

DID Method

A "DID method" is a specific implementation of a DID scheme that is identified by a method name. In this case, the method name is argos, and the method identifier is an Ethereum address.

To encode a DID for an Ethereum address, simply prepend did:argos:

For example:

did:argos:0xf3beac30c498d9e26865f34fcaa57dbb935b0d74

Configuration

import ArgosDID from 'argos-did'

const argosDID = new ArgosDID({address: '0x...', privateKey: '...', provider})
keydescriptionrequired
addressArgos address representing Identityyes
registryregistry addressno
providerweb3 providerno
web3preconfigured web3 objectno
rpcUrlJSON-RPC endpoint urlno
signerSigning functioneither signer or privateKey
privateKeyHex encoded private keyyes*

Getting Started

Construct a New Identity

Create Argos-DID

Creating an Argos-DID is analogous to creating an Ethereum account, which is an address on the Ethereum blockchain controlled by a key pair. Your Argos-DID will be your key pair.

We provide a convenient method to easily create one ArgosDID.createKeyPair() which returns an object containing an Ethereum address and private key.

const keypair = ArgosDID.createKeyPair()
// store keypair somewhere safe

const argosDid = new ArgosDID({...keypair, provider})

Use Existing Web3 Provider

If you use a built-in web3 provider like metamask you can use one of your metamask addresses as your identity.

const argosDid = new ArgosDID({provider: web3.currentProvider, address: web3.eth.defaultAccount})

Web3 providers are not directly able to sign data in a way that is compliant with the JWT-ES256K standard. This is a requirement for exchanging verifiable off-chain data, so you will need to add a key pair as a signing delegate to be able to sign JWT's.

You can quickly add one like this:

await argosDid.createSigningDelegate() // Adds a signing delegate valid for 1 day

See section on adding delegates below.

Note when using HttpProvider from web3.js v1.0.0: There is an issue where the sendAsync function is undefined. In order to avoid errors resulting from this, please configure ArgosDID using a different HTTP provider such as the one from ethjs:

import HttpProvider from 'ethjs-provider-http'
const provider = new HttpProvider('http://localhost:8545')
const argosDid = new ArgosDID({provider, address, registry})

or manually assign sendAsync before creating an instance of the web3 provider.

import Web3 from 'web3'

Web3.providers.HttpProvider.prototype.sendAsync = Web3.providers.HttpProvider.prototype.send

const provider = new Web3.providers.HttpProvider('http://localhost:8545')
const argosDid = new ArgosDID({provider, address, registry})

Ethereum Web3 Wallet Developers

You can easily add support for signing yourself by implementing a signer function with a clean GUI. See DID-JWT Signer Functions.

The signer function can be passed in as the signer option to the ArgosDID constructor:

const argosDid = new ArgosDID({provider, address: web3.eth.defaultAccount, signer: wallet.jwtSigner})

Exchange Verifiable Data

Signing a JWT

A JWT is a JSON object that is signed so it can be verified as being created by a given DID.

A JWT starts like this:

eyJ0eXA ...

Use any JSON compatible Javascript Object as a payload to sign.

const hello = await argosDid.signJWT({hello: 'world'})

const verification = await argosDid.signJWT({claims: {name: 'John Smith'}})

Verifying a JWT

You can easily verify a JWT using verifyJWT(). When a JWT is verified, the signature of the public key is compared to the known issuer of the DID. If the signature matches the time it was issued and expiration times are checked for validity too.

An unverified JWT will resemble:

{ header: { typ: 'JWT', alg: 'ES256K' },
  payload:
   { iat: 1525927517,
     aud: 'did:argos:2osnfJ4Wy7LBAm2nPBXire1WfQn75RrV6Ts',
     exp: 1557463421,
     name: 'Argos Developer',
     iss: 'did:argos:2osnfJ4Wy7LBAm2nPBXire1WfQn75RrV6Ts' },
  signature: 'R7owbvNZoL4ti5ec-Kpktb0datw9Y-FshHsF5R7cXuKaiGlQz1dcOOXbXTOb-wg7-30CDfchFERR6Yc8F61ymw',
  data: 'eyJ0eXA ...' }

The verifyJWT function will take the above encoded JWT and validate it:

const jwt = 'eyJ0eXA ...'

const {payload, issuer} = argosDid.verifyJWT(jwt)

// Payload
console.log(`${payload}`)

// Issuer contains the DID of the signing identity
console.log(issuer)

Its verified output looks like:

{ payload:
   { iat: 1525927517,
     aud: 'did:argos:2osnfJ4Wy7LBAm2nPBXire1WfQn75RrV6Ts',
     exp: 1557463421,
     name: 'Argos Developer',
     iss: 'did:argos:2osnfJ4Wy7LBAm2nPBXire1WfQn75RrV6Ts' },
  doc:
   { '@context': 'https://w3id.org/did/v1',
     id: 'did:argos:2osnfJ4Wy7LBAm2nPBXire1WfQn75RrV6Ts',
     publicKey: [ ... ],
     profile:
      { '@context': 'http://schema.org',
        '@type': 'App',
        name: 'Argos Demo',
        description: 'This app demonstrates basic login functionality',
        url: 'https://developer.argos.id' } },
  issuer: 'did:argos:2osnfJ4Wy7LBAm2nPBXire1WfQn75RrV6Ts',
  signer:
   { id: 'did:argos:2osnfJ4Wy7LBAm2nPBXire1WfQn75RrV6Ts#keys-1',
     type: 'EcdsaPublicKeySecp256k1',
     owner: 'did:argos:2osnfJ4Wy7LBAm2nPBXire1WfQn75RrV6Ts',
     publicKeyHex: 04c74d8a9154bbf48ce4b259b703c420e10aba42d03fa592ccf9dea60c83cd9ca81d3e08b859d4dc5a6dee30da2600e50ace688201b6f5a1e0938d135ec4b442ad' },
  jwt: 'eyJ0eXA ...' }

A consuming app can verify JWTs.

import { verifyJWT } from 'did-jwt'

require('argos-did-resolver')()

const {payload, issuer} = await verifyJWT(hello)

Manage Keys

The Argos-DID supports general key management that can be used to change ownership of keys, delegate signing rights temporarily to another account, and publish information about the identity in its DID document.

Currently, the following public key types are supported:

  • Secp256k1SignatureVerificationKey2018
    • with publicKeyHex encoding and ES256K, ES256K-R algorithm's.
  • Secp256k1VerificationKey2018 is also supported1
    • with publicKeyHex encoding and ES256K, ES256K-R algorithm's.
    • or with ethereumAddress encoding but only with the ES256K-R algorithm.

Private keys (signing keys), also used for account recovery are hex encoded using secp256k1.

The Concept of Identity Ownership

By default, an identity address is owned by itself. An identity owner is an address that is able to make and publish changes to the identity. As this is a very important function, you could change the ownership to use a smart contract based address implementing recovery or multi-sig at some point in the future.

Smart contracts are not able to sign, so you would also need to add a key pair based address as a signing delegate.

Most web3 providers also don't allow users to sign data that is compatible with JWT standards, which means that you would have to add a separate delegate key that you can use to sign JWTs on your behalf.

All the following functions assume that the passed in web3 provider can sign Ethereum transactions on behalf of the identity owner.

Changing an Owner

You can change the owner of an Argos-DID. This is useful in particular if you are changing an identity provider and want to continue to use the same identity.

This creates an Ethereum transaction, which will also broadcast a DIDOwnerChanged event. Make sure that the current account owner has sufficient gas to be able to update it.

await argosDid.changeOwner(web3.eth.accounts[2])

Adding a Delegate Signer

You can temporarily add a delegate signer to your DID. This is an address that can sign JWTs on your behalf. By adding an expiresIn value, it will automatically expire after a certain time. It will by default expire after a day.

You can add different delegate types. The two types currently supported by DID-JWT are:

  • veriKey Which adds a Secp256k1VerificationKey2018 (Default for signing general purpose JWTs)
  • sigAuth Which adds a Secp256k1SignatureAuthentication2018 signer who is able to interactively authenticate as the DID's owner (log in)

This is useful if you want to give a dApp the permission to sign on your behalf.

This creates an Ethereum transaction, so your current owner account needs sufficient gas to be able to update it.

await argosDid.addDelegate(web3.eth.accounts[3])

// Overriding
await argosDid.addDelegate(web3.eth.accounts[3], {expiresIn: 360, delegateType: 'sigAuth'})

There also exists a convenience function that creates a new delegate key pair, configures a signer with it and finally calls the above addDelegate() function.

const keypair = await argosDid.createSigningDelegate('sigAuth', 360)

The key pair object contains an address and privateKey attribute. Unless the key is just added temporarily, store it somewhere safe.

Set Public Attributes

You can set various public attributes to your DID using setAttribute(key, value, expiresIn). These cannot be queried within smart contracts, but they let you publish information to your DID document such as public encryption keys.

The following attribute key formats are currently support:

  • did/pub/(Secp256k1|Rsa|Ed25519)/(veriKey|sigAuth)/(hex|base64) for adding a public key
  • did/svc/[ServiceName] for adding a service

By adding an expiresIn value, it will automatically expire after a certain time. By default, it will expire after a day.

await argosDid.setAttribute('did/pub/Ed25519/veriKey/base64', 'Arl8MN52fwhM4wgBaO4pMFO6M7I11xFqMmPSnxRQk2tx', 31104000)
await argosDid.setAttribute('did/pub/Ed25519/veriKey/base64', Buffer.from('Arl8MN52fwhM4wgBaO4pMFO6M7I11xFqMmPSnxRQk2tx', 'base64'), 31104000)
await argosDid.setAttribute('did/svc/HubService', 'https://hubs.argos.id', 10)