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
- Send a welcome bonus to new users' email addresses before they sign up. The user can claim the funds once they verify the email.
- 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.
- Send promotional funds to Twitter handles. Users can claim the funds by authenticating their Twitter account.
- Distribute airdrops to Discord users using their IDs. They can claim the airdrop once they connect their Discord account.
- 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 thecontract-sdks
claimAndMint
RelayClaimProcessor
- allows for specifying a
RelayConfig
which overrides default options for calling a customrelayer
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 thekey id
based on auser id
andapp id
computeHdPubkey
calculates thepublic key
based on thekey 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.