Search…
Vault

Use Case: Hand Holding without Key Holding

Our customers need to sign transactions with a cryptographic key. We do not want to be in possession of this key. But our customers can not easily be trained to manage keys themselves. We need the keys to be managed for them, without us holding the keys.
To accomplish this, we will give each user a vault that will hold their keys and sign their transactions. The user will authorize an API access token for each transaction, that will temporarily allow us to request that the vault sign the transaction with the key. The token will be scoped to the specific transaction to be signed.

What tools are involved?

  • PRVD CLI
    • The CLI is a convenience layer for developers to interact with the services.
    • Code – https://github.com/provideplatform/provide-cli
    • Docs – https://docs.provide.services/api/quickstart/cli-quickstart
  • PRVD Ident service
    • The Ident service will manage our identity and our customers’ identities.
    • API – https://ident.provide.services/api/v1/
    • Code – https://github.com/provideplatform/ident
    • Docs – https://docs.provide.services/api/rest-api-v1/ident
  • PRVD Vault service
    • The Vault service will manage vaults, keys and signing.
    • API – https://vault.provide.services/api/v1/
    • Code – https://github.com/provideplatform/vault
    • Docs – https://docs.provide.services/api/rest-api-v1/vault
  • PRVD JS
    • This library is an npm package for interacting with the services in JavaScript.
    • Code – https://github.com/provideplatform/provide-js

Creating our own identity

Before we start helping others with identities and vaults, we need our own. We’ll need a user, an organization, and an application. We can do this through the Ident API, but it is more convenient to use the CLI. Follow the CLI instructions in Prerequisites and Installing from Source before continuing. You can run prvd --help to verify the CLI is installed and to see what all it can do.

Create a user

1
$ prvd users init
Copied!
The CLI conveniently prompts for the required input: First Name, Last Name, Email, Password.
1
First Name: Philip
2
Last Name: Keiter
4
Password: ********
Copied!
After filing these in, the CLI returns the new User ID.
1
created user: 92e9804f-0639-4d61-9ffa-c1d0434089b1
Copied!

Create an organization

1
$ prvd organizations init
Copied!
The CLI conveniently prompts for the required input: Organization Name.
1
Organization Name: Philip's Org
Copied!
After filing that in, the CLI returns the new Organization ID.
1
initialized organization: Philip's Org aa79cc1c-300b-4814-aa4f-be9ce918c9ce
Copied!

Create an application

1
$ prvd applications init --name 'My App' --network 024ff1ef-7369-4dee-969c-1918c6edb5d4 --without-wallet
Copied!
The CLI returns the application ID, name, account, and address.
1
63b9faad-2269-4298-90e6-9281e6437a54 My App
2
Account adc02bbb-98ac-4011-8bdf-b12ab5150b22 0x69C147D1C8B84f87002438b71B8A681624d3E620
Copied!
You can create your application on any network. prvd networks list will show your private networks and prvd networks list --public will show available public networks.

Create an application API token

1
$ prvd api_tokens init --application '63b9faad-2269-4298-90e6-9281e6437a54' --offline-access
Copied!
The CLI returns an access token and refresh token.
1
Access token authorized for application: 63b9faad-2269-4298-90e6-9281e6447a54 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
2
Refresh token authorized for application: 63b9faad-2269-4298-90e6-9281e6447a54 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Copied!

Creating customer identities

Now that we have our own identity, we can create identities for our customers. We have a lot of customers, so we will do this in an automated way using scripts. PRVD has helper libraries for many programming languages to more conveniently use the API. JavaScript is a popular language and can be used in either a front end proof of concept, or a back end NodeJS server.

Import the client factories

1
import { identClientFactory, vaultClientFactory } from 'provide-js';
Copied!

Set the account ID, application ID, and access token

1
const accountId = 'adc02bbb-98ac-4011-8bdf-b12ab5150b22';
2
const appId = '63b9faad-2269-4298-90e6-9281e6437a54';
3
const appAccessToken = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
Copied!

Initialize an Ident client instance

1
const ident = identClientFactory(appAccessToken);
Copied!

Create a virtual user

1
const virtualUser = await ident.createUser({
2
application_id: appId,
3
email: `joe.user.${new Date().getTime()}@example.com`,
4
first_name: 'Joe',
5
last_name: 'User',
6
})
7
8
console.log(virtualUser.id);
Copied!
The user ID should be persisted and associated with your record of your user.

Create a user access token

1
const userAccessToken = (await ident.createToken({
2
application_id: appId,
3
user_id: virtualUser.id,
4
scope: 'offline_access',
5
})).accessToken;
Copied!
The refresh token is also returned. It should be persisted and used to create future access tokens.

Create an organization

1
const org = await ident.createOrganization({
2
name: 'Acme Inc.',
3
});
4
5
console.log(org.id);
Copied!
The organization ID should be persisted and associated with your record of your user.

Create an organization access token

1
const orgAccessToken = (await ident.createToken({
2
organization_id: org.id,
3
scope: 'offline_access',
4
})).accessToken;
Copied!
The refresh token is also returned. It should be persisted and used to create future access tokens.

Initialize a Vault client instance

1
const vault = vaultClientFactory(orgAccessToken);
Copied!

Create a vault

1
const orgVault = await vault.createVault({
2
name: `${org.name} Vault`,
3
description: `${org.name} vault instance`,
4
});
5
6
console.log(orgVault.id);
Copied!
The vault ID should be persisted and associated with your record of your user.

Create a key

1
const key = await vault.createVaultKey(orgVault.id, {
2
type: 'asymmetric',
3
usage: 'sign/verify',
4
spec: 'secp256k1',
5
name: `${org.name} ETH keypair`,
6
description: `${org.name} ETH keypair`,
7
});
8
9
console.log(key.id);
Copied!
The key ID should be persisted and associated with your record of your user.

Creating and signing transactions

Create a transaction

Use your preferred library of choice to construct a raw Ethereum transaction. To sign it, Vault will need its transaction hash.

Format transaction hash

If the transaction hash starts with "0x" we need to trim that off.
1
const hash = (transactionHash.substring(0, 2) === '0x')
2
? transaction.hash.substring(2)
3
: transaction.hash;
Copied!

Sign a transaction

1
const signedMessage = await vault.signMessage(orgVault.id, key.id, hash);
2
3
console.log(signedMessage.signature);
Copied!

Mission Accomplished

As you can see, we have the user ID, organization ID, vault ID, key ID, and an access token, but we do not have the actual key. It is stored securely in the vault. Vault signs the message in a custodial or non-custodial manner, depending on if you (i) are relying on the commercially-supported managed Provide vault (custodial) or (ii) deployed Vault to your own infrastructure (non-custodial).