orbitdb/docs/IDENTITIES.md

6.5 KiB

Identities

In OrbitDB, an identity is a cryptographically signed public key which is used to verify write access to a database's operations log and, if allowed, to sign each database update as it is added to the operations log.

Identities provides methods to manage one or more identities and includes functionality for creating, retrieving, signing and verifying an identity and, internally, it is used to sign and verify database updates.

Creating an identity

An identity can be created by using the createIdentity function.

A root key is used to create a new key with the "id" of the root key's public key. Using the derived private key, the root public key is signed.

A new identity is signed using the root key's private key. The identity is consists of the signed message and the derived public key concatenated together ("signed identity")

A "signatures object" is then created to hold both the signed message and signed identity.

Finally, a new identity consisting of the root public key and derived public key plus the signatures object is generated and stored to the Identities storage.

import { Identities } from '@orbitdb/core'

const id = 'userA'
const identities = await Identities() 
const identity = identities.createIdentity({ id })

The id parameter that is passed to createIdentity is used to reference the root key pair in the PublicKeyIdentityProvider. The id can be any arbitrary text, e.g. 'bob', 'My-Key-123', etc.

The PublicKeyIdentityProvider stores the id and the root keys as a key/value pair in the key store. Other providers may not store root keys in the same manner and so the id parameter may not always be required.

Once created, identities and the associated id can be passed to OrbitDB:

const orbitdb = await createOrbitDB({ identities, id: 'userA' })

This identity can now be used by OrbitDB to control access to database actions such as write.

Key Store

The key store is a local key manager for OrbitDB and is used to store the private keys generated by Identities.createIdentity.

PublicKeyIdentityProvider also uses key store to store the root key which is used to sign the identities created with Identities.createIdentity.

Specifying a keystore

An existing keystore can be passed to Identities:

import { Identities, KeyStore } from '@orbitdb/core'

const keystore = await KeyStore()
const id = 'userA'
const identities = await Identities({ keystore })
const identity = identities.createIdentity({ id })

Customizing the key store path

There are different ways to customize the location of the key store.

To change the keystore using OrbitDB, pass a custom directory:

// This will create a key store under ./different-path/key-store
const orbitdb = await createOrbitDB({ directory: './different-path' })
// Be aware that this will change the base path to the database as well.

To change the keystore using the KeyStore function, pass a custom path to the KeyStore function:

// This will create a key store under ./different-key-store.
const path = './different-key-store'
const keystore = await KeyStore({ path })

// keystore can now be used with other functions, for example:
const identities = await Identities({ keystore })

To specify a different keystore path using Identities, pass a custom path to the Identities function:

/// This will create a KeyStore under ./different-identities-path
const path = './different-identities-path'
const identities = await Identities({ path })

Identity as a linked data object

The identity object is stored like any other IPLD data structure and can therefore be retrieved from IPFS using the identity's hash:

import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia' from 'ipfs-core'
import * as Block from 'multiformats/block'
import { Identities } from '@orbitdb/core'
import * as dagCbor from '@ipld/dag-cbor'
import { sha256 } from 'multiformats/hashes/sha2'
import { base58btc } from 'multiformats/bases/base58'
import { CID } from 'multiformats/cid'
import { Libp2pOptions } from './config/libp2p.js'

const libp2p = await createLibp2p(Libp2pOptions)
const ipfs = await createHelia({ libp2p })

const identities = await Identities({ ipfs })
const identity = await identities.createIdentity({ id: 'me' })

const cid = CID.parse(identity.hash, base58btc)

// Extract the hash from the full db path.
const bytes = await ipfs.block.get(cid)

// Defines how we serialize/hash the data.
const codec = dagCbor
const hasher = sha256

// Retrieve the block data, decoding it to human-readable JSON text.
const { value } = await Block.decode({ bytes, codec, hasher })

console.log('identity', value)

The resulting output is an object containing the identity information:

{
  id: '031a7bf5f233ad9dccb2a305edfd939f0c54f0ed2e270c4ac390d91ecd33d0c28f',
  type: 'publickey',
  publicKey: '02343cbdd3a5deaacc115f8f241db979f59864aad5b4fbf1561e19d5d04d7b1d14',
  signatures: {
    id: '30440220492ed7bc2945bd6859e96fa929870343bcda188b481248dfa51c7dd7a1eb59ef022049542399338e66454f523dc4033723bc4ff4365f17537171361e128f10703be1',
    publicKey: '30450221008291e9e12d586129de2882e731f286b4168cbed43d2ecf90d8ae9c53e15c56110220204ac640b22e75bf6083a6715a7e6c988659fc08f79022fab8af62563e9fdd67'
  }
}

Custom identity providers

A custom identity provider can be used provided the module takes the following form:

// A unique name for the identity provider
const type = 'custom'

// check whether the identity was signed by the identity's id.
const verifyIdentity = identity => {
  // ...
}

// The identity provider.
const MyCustomIdentityProvider = ({ keystore }) => async () => {
  const getId = async ({ id } = {}) => {
    // return the "root" identity managed by the custom identity provider,
    // eg. a public key or a wallet address
  }

  const signIdentity = async (publicKeyAndIdSignature) => {
    // sign the publicKeyAndIdSignature using the custom identity provider system
  }

  return {
    type,
    getId,
    signIdentity
  }
}

MyCustomIdentityProvider.verifyIdentity = verifyIdentity
MyCustomIdentityProvider.type = type

export default MyCustomIdentityProvider

To use it, add it to the list of known identity providers:

import { addIdentityProvider } from '@orbitdb/core'
import MyCustomIdentityProvider from 'my-custom-identity-provider'
addIdentityProvider(MyCustomIdentityProvider)
const identity = await createIdentity({ provider: MyCustomIdentityProvider(options) })

where my-custom-identity-provider is the custom module.