Lit JS SDK V3: Claimable Keys

Lit JS SDK V3: Claimable Keys

Lit Protocol is a decentralized key management network powered by threshold cryptography. A blockchain-agnostic identity layer, Lit can be used to power encryption, signing, and compute for web applications.

Introduction

Imagine if you could send money to your friends on Discord before they've even set up a web3 wallet. With new feature through Lit called claimable keys, this is now possible.

Here's how it works: Your friend's Discord ID acts like a key identifier where a unique public address is created, a unique wallet address, just for them. You can send funds to that address, even if they don't have a wallet yet. Then when your friend is ready, they simply link their Discord account to claim the funds waiting for them.

This makes getting started with decentralized applications easier. Instead of telling friends to go through the steps of setting up a wallet, you can send them a few currencies upfront. The assets will be there ready and waiting once they link their Discord. No more explaining public keys and addresses, just send funds to their account and they can claim it later in a few simple clicks.

Use Cases

  1. Send a welcome bonus to new users' email addresses before they sign up. The user can claim the funds once they verify the email.
  2. Allow users to receive funds via their phone number before installing a decentralized app. They can claim the funds once they verify their phone number.
  3. Send promotional funds to Twitter handles. Users can claim the funds by authenticating their Twitter account.
  4. Distribute airdrops to Discord users using their IDs. They can claim the airdrop once they connect their Discord account.
  5. Invite new users by sending them a small amount of tokens to their Lens profile. They can claim it by verifying their Lens account ownership.

Claimable Keys with Lit

Claimable or what we will also call Homogenous Derived (HD) keys work off a set of root keys which combined with a key identifier allow the deterministic generation of new key pairs.

Key vocabulary to understand:

Term Definition
authentication methods Deterministically generated identifier from authentication material
auth method identifier An ID generated from a given authentication method
key identifier (key id) The identifier used to derive the public key, this is the auth method identifier
claiming The action of using an authentication method to derive a key from it’s auth method identifier
root keys a set of asymmetric keys used to derive asymmetric ECDSA Keys or Programmable Key Pairs

The key identifier is generated from an auth method identifier based on authentication methods which is a deterministically generated identifier from authentication material. This generation scopes the ID to the specific application context allowing authentication methods to be registered to more than a single application context.

With deterministic keys, it is now possible to know the public addresses of Programmable Key Pairs (PKP's) ahead of time as long as you know the authentication method. This also allows you to generate keys from a single authentication method since this identifier is known ahead of time from deriving the auth method to its auth method identifier. This allows for pre-generation of the public key of the key pair ahead of time, while keeping the private key unknown as the derivation of the private key does not follow the same derivation as the public portion.

To support derived keys we have added a new concept to our network claiming. Claiming is the process of registering the key identifier as a claimed key such that no other entity may claim that key. This claim is signed by our network of nodes, and a threshold of signatures is present when persisting the claim.

Below is a table of how each supported authentication method derives the key ID

Auth Method User ID App ID
Google OAuth token sub token aud
Discord OAuth user handle client app identifier
Stytch user id project id
Lit Actions user provided IPFS CID

How the tech works

The Lit JS SDK V3 provides a claimKeyId method which authenticates an auth method to derive the key id . If authentication is successful, a signature is generated by each of the nodes in the network. This signature, the key id, and derived public key are then provided to a ClaimProcessor which will register the generated key claim on chain and then routes the generated public key for use.

The ClaimProcessor can be provided which defines how the response from the claim operation will be registered on chain. It is recommended to use either the Lit relay server, or the contract-sdk . There are two types of ClaimProccessor

  • ClientClaimProcessor
  • Allows for specifying a signer for registering claims through the contract-sdks claimAndMint
  • RelayClaimProcessor
  • allows for specifying a RelayConfig which overrides default options for calling a custom relayer instance

If you wish to derive public keys before claiming, the LitNodeClient offers two helper functions to allow you to compute derived public keys.

  • computeHdKeyId calculates the key id based on a user id and app id
  • computeHdPubkey calculates the public key based on the key id

Video walkthrough

Code example

Check out this repository for a working example of creating a claimable key and claiming a key.

https://github.com/LIT-Protocol/claim-key-demo-nodejs

Installation Set Up

You will need to auth with Stytch.

Get an account through Stytch: https://stytch.com/docs

To get set up for this project, set up a new project with Stytch and check out your dashboard’s API keys to get set up: https://stytch.com/dashboard/api-keys

const client = new stytch.Client({
  project_id: STYTCH_PROJECT_ID,
  secret: STYTCH_SECRET,
});

const litNodeClient = new LitNodeClientNodeJs({
  litNetwork: "cayenne",
  debug: false,
});

await litNodeClient.connect();

const authClient = new LitAuthClient({
  litRelayConfig: {
    relayApiKey: LIT_RELAY_API_KEY,
  },
  litNodeClient,
});

Creating a claimable key

In this code example, we set up functions to call creating a claimable key through email with the help of Lit’s relay server.

const otpResponse = await prompts({
  type: "text",
  name: "code",
  message: "Enter the code sent to your email:",
});

const authResponse = await client.otps.authenticate({
  method_id: stytchResponse.email_id,
  code: otpResponse.code,
  session_duration_minutes: 60 * 24 * 7,
});

Create a session through the one time password auth response.


let sessionResp = await client.sessions.get({
  user_id: authResponse.user_id,
});

const sessionStatus = await client.sessions.authenticate({
  session_token: authResponse.session_token,
});

const session = authClient.initProvider<StytchOtpProvider>(
  ProviderType.StytchOtp,
  {
    userId: sessionStatus.session.user_id,
    appId: STYTCH_PROJECT_ID,
  }
);

Obtain the authMethod from the session and then it’s possible to compute the public key (or claimable key) for an email address.

const authMethod = await session.authenticate({
  accessToken: sessionStatus.session_jwt,
});

const publicKey = await session.computePublicKeyFromAuthMethod(authMethod);
console.log("local public key computed: ", publicKey);

From the terminal, run this to go through creating a claimable key:

yarn start --claim

Claiming a key

let claimResp = await session.claimKeyId({
  authMethod,
});

console.log("claim response public key: ", claimResp.pubkey);

From the terminal, run this to query the keys after a claimable key has been created:

yarn start --lookup

The output should look something like:

Lit's relay server URL: <https://relayer-server-staging-cayenne.getlit.dev>
JWT body:  {
  aud: [ 'project-test-549bbcd2-cd88-405c-baf2-6401b279e5c9' ],
  exp: 1696296825,
  '<https://stytch.com/session>': {
    id: 'session-test-6d6e7280-8c12-4fa8-ae33-9018c275e3f4',
    started_at: '2023-10-03T01:28:45Z',
    last_accessed_at: '2023-10-03T01:28:45Z',
    expires_at: '2023-10-10T01:28:45Z',
    attributes: { user_agent: '', ip_address: '' },
    authentication_factors: [ [Object] ]
  },
  iat: 1696296525,
  iss: 'stytch.com/project-test-549bbcd2-cd88-405c-baf2-6401b279e5c9',
  nbf: 1696296525,
  sub: 'user-test-a2bb8fe5-fbd4-4917-919d-4d5b7f2eef07'
}
JWT body:  {
  aud: [ 'project-test-549bbcd2-cd88-405c-baf2-6401b279e5c9' ],
  exp: 1696296825,
  '<https://stytch.com/session>': {
    id: 'session-test-6d6e7280-8c12-4fa8-ae33-9018c275e3f4',
    started_at: '2023-10-03T01:28:45Z',
    last_accessed_at: '2023-10-03T01:28:45Z',
    expires_at: '2023-10-10T01:28:45Z',
    attributes: { user_agent: '', ip_address: '' },
    authentication_factors: [ [Object] ]
  },
  iat: 1696296525,
  iss: 'stytch.com/project-test-549bbcd2-cd88-405c-baf2-6401b279e5c9',
  nbf: 1696296525,
  sub: 'user-test-a2bb8fe5-fbd4-4917-919d-4d5b7f2eef07'
}
local public key computed:  04473c0d569dec04a76a96de2f99691849a89ef5057101b22966dd9c17a987982ab53c718423081755b2047d315ab22e2d2d84e93af245d507d1922c297cb0c663
JWT body:  {
  aud: [ 'project-test-549bbcd2-cd88-405c-baf2-6401b279e5c9' ],
  exp: 1696296825,
  '<https://stytch.com/session>': {
    id: 'session-test-6d6e7280-8c12-4fa8-ae33-9018c275e3f4',
    started_at: '2023-10-03T01:28:45Z',
    last_accessed_at: '2023-10-03T01:28:45Z',
    expires_at: '2023-10-10T01:28:45Z',
    attributes: { user_agent: '', ip_address: '' },
    authentication_factors: [ [Object] ]
  },
  iat: 1696296525,
  iss: 'stytch.com/project-test-549bbcd2-cd88-405c-baf2-6401b279e5c9',
  nbf: 1696296525,
  sub: 'user-test-a2bb8fe5-fbd4-4917-919d-4d5b7f2eef07'
}
Successfully fetched PKPs with relayer
[
  {
    tokenId: {
      type: 'BigNumber',
      hex: '0x9f26eebba968c6e597d729becdbe1112b876acd711ee954fbd277f5b137bc03d'
    },
    publicKey: '0x04473c0d569dec04a76a96de2f99691849a89ef5057101b22966dd9c17a987982ab53c718423081755b2047d315ab22e2d2d84e93af245d507d1922c297cb0c663',
    ethAddress: '0x0181Be4640Af70C1257b5cfb62F2860DBEb61824'
  }
]

Closing

Claimable keys unlocks a lot of potential with how people can share and access wallets. While technical in nature, claimable keys have the potential to help make shift ease and accessiblity of blockchain technology. By abstracting away cryptographic complexities, claimable keys are an important building block as we work towards a more open web.

If you have a great idea on how to use claimable keys, start building today!

You can reach out to the team on Discord, GitHub or Twitter.