Skip to content

Commit eb219e5

Browse files
authored
Merge pull request #972 from LIT-Protocol/feature/jss-35-naga-js-sdk-wrapped-keys-implementation
Feature/jss 35 naga js sdk wrapped keys implementation
2 parents 5e23a65 + f0b272c commit eb219e5

File tree

111 files changed

+5893
-931
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

111 files changed

+5893
-931
lines changed

.changeset/cold-dancers-push.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
'@lit-protocol/wrapped-keys-lit-actions': minor
3+
'@lit-protocol/auth-services': minor
4+
'@lit-protocol/auth-helpers': minor
5+
'@lit-protocol/wrapped-keys': minor
6+
'@lit-protocol/lit-client': minor
7+
'@lit-protocol/artillery': minor
8+
'@lit-protocol/constants': minor
9+
'@lit-protocol/contracts': minor
10+
'@lit-protocol/networks': minor
11+
'@lit-protocol/crypto': minor
12+
'@lit-protocol/auth': minor
13+
'@lit-protocol/e2e': minor
14+
---
15+
16+
Introduce wrapped-keys support to v8 so applications can generate, import, export, and sign with encrypted keys across EVM and Solana without exposing private key material. New `auth` package APIs include `validateDelegationAuthSig`, `generatePkpDelegationAuthSig`, `generateEoaDelegationAuthSig`, `createPkpAuthContextFromPreGenerated`, and `createPkpSessionSigs`. New `wrapped-keys` APIs include `generatePrivateKey`, `importPrivateKey`, `exportPrivateKey`, `listEncryptedKeyMetadata`, `getEncryptedKey`, `storeEncryptedKey`, `storeEncryptedKeyBatch`, `batchGeneratePrivateKeys`, `signMessageWithEncryptedKey`, and `signTransactionWithEncryptedKey`. See the updated docs (guides/server-sessions, sdk-reference/wrapped-keys, and the new auth references) for end-to-end examples. [PR](https://github.com/LIT-Protocol/js-sdk/pull/972)

.changeset/config.json

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,5 @@
77
"access": "public",
88
"baseBranch": "naga",
99
"updateInternalDependencies": "minor",
10-
"ignore": [
11-
"@lit-protocol/wrapped-keys",
12-
"@lit-protocol/wrapped-keys-lit-actions"
13-
]
10+
"ignore": []
1411
}

docs/docs.json

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@
8080
"pages": [
8181
"sdk/auth-context-consumption/pkp-sign",
8282
"sdk/auth-context-consumption/execute-js",
83-
"sdk/auth-context-consumption/encrypt-and-decrypt"
83+
"sdk/auth-context-consumption/encrypt-and-decrypt",
84+
"sdk/auth-context-consumption/wrapped-keys"
8485
]
8586
},
8687
{
@@ -110,14 +111,35 @@
110111
{
111112
"group": "@lit-protocol/auth",
112113
"pages": [
113-
"sdk/sdk-reference/auth/functions/createAuthManager"
114+
"sdk/sdk-reference/auth/functions/createAuthManager",
115+
"sdk/sdk-reference/auth/functions/generatePkpDelegationAuthSig",
116+
"sdk/sdk-reference/auth/functions/generateEoaDelegationAuthSig",
117+
"sdk/sdk-reference/auth/functions/validateDelegationAuthSig",
118+
"sdk/sdk-reference/auth/functions/createPkpAuthContextFromPreGenerated",
119+
"sdk/sdk-reference/auth/functions/createPkpSessionSigs"
114120
]
115121
},
116122
{
117123
"group": "@lit-protocol/networks",
118124
"pages": [
119125
"sdk/sdk-reference/networks/functions/withOverrides"
120126
]
127+
},
128+
{
129+
"group": "@lit-protocol/wrapped-keys",
130+
"pages": [
131+
"sdk/sdk-reference/wrapped-keys/index",
132+
"sdk/sdk-reference/wrapped-keys/functions/generatePrivateKey",
133+
"sdk/sdk-reference/wrapped-keys/functions/exportPrivateKey",
134+
"sdk/sdk-reference/wrapped-keys/functions/importPrivateKey",
135+
"sdk/sdk-reference/wrapped-keys/functions/getEncryptedKey",
136+
"sdk/sdk-reference/wrapped-keys/functions/listEncryptedKeyMetadata",
137+
"sdk/sdk-reference/wrapped-keys/functions/storeEncryptedKey",
138+
"sdk/sdk-reference/wrapped-keys/functions/storeEncryptedKeyBatch",
139+
"sdk/sdk-reference/wrapped-keys/functions/batchGeneratePrivateKeys",
140+
"sdk/sdk-reference/wrapped-keys/functions/signMessageWithEncryptedKey",
141+
"sdk/sdk-reference/wrapped-keys/functions/signTransactionWithEncryptedKey"
142+
]
121143
}
122144
]
123145
},
@@ -131,7 +153,8 @@
131153
{
132154
"group": "Guides",
133155
"pages": [
134-
"guides/lit-action-sign-as-action"
156+
"guides/lit-action-sign-as-action",
157+
"guides/server-sessions"
135158
]
136159
},
137160
{
@@ -223,4 +246,4 @@
223246
"discord": "https://litgateway.com/discord"
224247
}
225248
}
226-
}
249+
}

docs/guides/server-sessions.mdx

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
---
2+
title: "Server Sessions"
3+
description: "Delegate a PKP to an ephemeral session key and hand that scoped capability to backend services that mint fresh session signatures on demand."
4+
---
5+
6+
# Overview
7+
8+
Some integrations need a backend or serverless worker to execute Lit actions long after a user has left the client. Instead of caching `pkpSessionSigs` (which expire quickly and can become invalid when nodes join or leave the network), you can delegate the PKP to a session key and send the **session keypair plus delegation auth signature** to the server. The server then recreates the auth context and generates short-lived session signatures immediately before each request.
9+
10+
This pattern keeps the delegation scoped (resources and expiration are enforced by the delegation) while avoiding the flakiness that comes from reusing stale session signatures.
11+
12+
# Workflow
13+
14+
1. **Client generates a session keypair** with `generateSessionKeyPair()`.
15+
2. **Client creates a delegation auth signature** with `authManager.generatePkpDelegationAuthSig`, scoping the allowed [Lit resources](/sdk/resources/lit-resources-and-abilities) and expiration.
16+
3. **Client sends the bundle** { sessionKeyPair, delegationAuthSig, pkpPublicKey }` to the server over a secure channel.
17+
4. **Server restores an auth context** using `authManager.createPkpAuthContextFromPreGenerated`.
18+
5. **Server issues fresh session signatures on demand** (e.g., `authManager.createPkpSessionSigs`) immediately before calling SDK helpers such as the wrapped-keys API or `pkpSign`.
19+
20+
# Client hand-off example
21+
22+
```ts
23+
import {
24+
createAuthManager,
25+
generateSessionKeyPair,
26+
} from '@lit-protocol/auth';
27+
28+
const sessionKeyPair = generateSessionKeyPair();
29+
30+
const delegationAuthSig = await authManager.generatePkpDelegationAuthSig({
31+
pkpPublicKey,
32+
authData,
33+
sessionKeyPair,
34+
authConfig: {
35+
resources: [
36+
['pkp-signing', '*'],
37+
['lit-action-execution', '*'],
38+
['access-control-condition-decryption', '*'],
39+
],
40+
expiration: new Date(Date.now() + 15 * 60 * 1000).toISOString(),
41+
},
42+
litClient: litClient,
43+
});
44+
45+
const envelope = JSON.stringify({
46+
pkpPublicKey,
47+
payload: Buffer.from(
48+
JSON.stringify({ sessionKeyPair, delegationAuthSig }),
49+
'utf8'
50+
).toString('base64url'),
51+
});
52+
```
53+
54+
# Server restore example
55+
56+
```ts
57+
import {
58+
createAuthManager,
59+
storagePlugins,
60+
validateDelegationAuthSig,
61+
} from '@lit-protocol/auth';
62+
import { createLitClient } from '@lit-protocol/lit-client';
63+
64+
const parsedEnvelope = JSON.parse(envelope) as {
65+
pkpPublicKey: string;
66+
payload: string;
67+
};
68+
69+
const decodedPayload = JSON.parse(
70+
Buffer.from(parsedEnvelope.payload, 'base64url').toString('utf8')
71+
) as {
72+
sessionKeyPair: typeof sessionKeyPair;
73+
delegationAuthSig: typeof delegationAuthSig;
74+
};
75+
76+
validateDelegationAuthSig({
77+
delegationAuthSig: decodedPayload.delegationAuthSig,
78+
sessionKeyUri: decodedPayload.sessionKeyPair.publicKey,
79+
});
80+
81+
const serverLitClient = await createLitClient({ network: resolvedNetwork });
82+
const serverAuthManager = createAuthManager({
83+
storage: storagePlugins.localStorageNode({
84+
appName: 'server-session-demo',
85+
networkName: resolvedNetwork.name,
86+
storagePath: './.server-session-cache',
87+
}),
88+
});
89+
90+
const authContext =
91+
await serverAuthManager.createPkpAuthContextFromPreGenerated({
92+
pkpPublicKey: parsedEnvelope.pkpPublicKey,
93+
sessionKeyPair: decodedPayload.sessionKeyPair,
94+
delegationAuthSig: decodedPayload.delegationAuthSig,
95+
});
96+
97+
// Only call this when the downstream API explicitly requires session sigs
98+
const pkpSessionSigs = await serverAuthManager.createPkpSessionSigs({
99+
sessionKeyPair: decodedPayload.sessionKeyPair,
100+
pkpPublicKey: parsedEnvelope.pkpPublicKey,
101+
delegationAuthSig: decodedPayload.delegationAuthSig,
102+
litClient: serverLitClient,
103+
});
104+
105+
await serverLitClient.chain.ethereum.pkpSign({
106+
authContext,
107+
pubKey: parsedEnvelope.pkpPublicKey,
108+
toSign: 'hello from server reuse',
109+
});
110+
```
111+
112+
<Note>
113+
Only call `createPkpSessionSigs` when the downstream API explicitly requires the session sigs (for example, the wrapped-keys service).
114+
</Note>
115+
116+
# Security considerations
117+
118+
- **Treat the session keypair like a secret.** Whoever holds the private key can mint new session signatures until the delegation expires.
119+
- **Scope the delegation.** Restrict resources to the minimal [Lit resources](/sdk/resources/lit-resources-and-abilities) needed and set conservative expirations.
120+
- **Rotate on failure.** If a node joins or leaves the network the server can simply regenerate session signatures with the current handshake; if that fails, request a fresh delegation from the client.
121+
122+
# When to use this pattern
123+
124+
- Long-running workflows where session signatures might expire before all steps finish (e.g., Bitcoin transactions that need multiple confirmations).
125+
- Server-driven orchestration that must run without a browser tab staying open.
126+
- Integrations that want to avoid caching `pkpSessionSigs`, but still need durable delegated access.
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
---
2+
title: "Wrapped Keys"
3+
description: "Manage generated or imported keys through Lit’s wrapped-keys service by delegating a PKP session and calling the specialised APIs for EVM and Solana."
4+
---
5+
6+
# Overview
7+
8+
Wrapped keys let you generate, import, store, and use non-PKP private keys while keeping them encrypted by the Lit Network. The `@lit-protocol/wrapped-keys` package exposes helper APIs that run curated Lit Actions via `executeJs` for you so that signed messages or transactions never leave the Lit node unless you explicitly export the key material.
9+
10+
Unlike the Core API flows (`pkpSign`, `executeJs`, `encryptAndDecrypt`) that accept an `authContext` directly, the wrapped-keys APIs expect a PKP session signature bundle (`pkpSessionSigs`). This keeps the SDK compatible with the v7 infrastructure and Lambda/serverless deployments that pass session materials between runtimes. Mint the bundle with `authManager.createPkpSessionSigs` as shown below.
11+
12+
# How the flow differs from other Core API methods
13+
14+
- **Why generate a delegation auth sig?**
15+
You delegate your PKP to an ephemeral session key so the wrapped-keys Lit Actions can execute without presenting your long-lived auth credentials. The delegation constrains the permissions (`pkp-signing`, `lit-action-execution`, `access-control-condition-decryption`) and sets an expiry window, limiting blast radius if the session key is ever leaked. Refer to the [Lit resources guide](/sdk/resources/lit-resources-and-abilities) for the full catalogue of resource prefixes and abilities.
16+
17+
- **Why supply PKP session signatures?**
18+
Wrapped-keys endpoints reuse the same session bundle across all networks (for example, Lambda functions in the v7 stack). Provide `pkpSessionSigs` produced by `authManager.createPkpSessionSigs` so the Lit nodes can verify the delegated permissions before executing the action.
19+
20+
- **Controlling network spend**
21+
Because every wrapped-keys helper ultimately calls `executeJs`, you can supply an optional `userMaxPrice` to cap how much a caller is willing to pay for node execution (mirroring the parameter available on the core `executeJs` API).
22+
23+
- **Optional: bundle Lit Action source**
24+
By default the SDK references IPFS CIDs. To remove the IPFS dependency, inject the Lit Action source code at runtime:
25+
26+
```ts
27+
import { api as wrappedKeysApi, config as wrappedKeysConfig } from '@lit-protocol/wrapped-keys';
28+
import {
29+
litActionRepository,
30+
litActionRepositoryCommon,
31+
} from '@lit-protocol/wrapped-keys-lit-actions';
32+
33+
wrappedKeysConfig.setLitActionsCode(litActionRepository);
34+
wrappedKeysConfig.setLitActionsCodeCommon(litActionRepositoryCommon);
35+
```
36+
37+
# Session material workflow
38+
39+
```ts
40+
import { createAuthManager, generateSessionKeyPair } from '@lit-protocol/auth';
41+
import { api as wrappedKeysApi } from '@lit-protocol/wrapped-keys';
42+
43+
const authManager = createAuthManager();
44+
const litClient = await createLitClient({ network: resolvedNetwork });
45+
46+
// 1. Ephemeral signer that will front the PKP for wrapped-key actions
47+
const sessionKeyPair = generateSessionKeyPair();
48+
49+
// 2. Delegate the PKP to the session key with explicit Lit resources and expiry
50+
const delegationAuthSig = await authManager.generatePkpDelegationAuthSig({
51+
pkpPublicKey,
52+
authData,
53+
sessionKeyPair,
54+
authConfig: {
55+
resources: [
56+
['pkp-signing', '*'],
57+
['lit-action-execution', '*'],
58+
['access-control-condition-decryption', '*'],
59+
],
60+
expiration: new Date(Date.now() + 15 * 60 * 1000).toISOString(),
61+
},
62+
litClient,
63+
});
64+
65+
// 3. Produce the SessionSigsMap required by every wrapped-keys API call
66+
const pkpSessionSigs = await authManager.createPkpSessionSigs({
67+
sessionKeyPair,
68+
pkpPublicKey,
69+
delegationAuthSig,
70+
litClient,
71+
});
72+
73+
// 4. Call wrapped-keys APIs with the session signatures
74+
const { id } = await wrappedKeysApi.generatePrivateKey({
75+
pkpSessionSigs,
76+
litClient,
77+
network: 'evm',
78+
memo: 'wallet for gasless relays',
79+
userMaxPrice: 1000n, // optional cap on per-request spend
80+
});
81+
```
82+
83+
<Note>
84+
Wrapped-keys calls need `pkpSessionSigs` explicitly. Other Lit APIs that accept an `authContext` (for example `executeJs` or `pkpSign`) creates session signatures internally, so you do not need to export it when you are not touching wrapped keys.
85+
</Note>
86+
87+
Need a backend to create fresh session signatures on demand? Follow the [server sessions guide](/guides/server-sessions) to delegate a session key and let the server recreate `pkpSessionSigs` per request.
88+
89+
# Example usage
90+
91+
## Generate and sign on EVM
92+
93+
```ts
94+
const { id, generatedPublicKey } = await wrappedKeysApi.generatePrivateKey({
95+
pkpSessionSigs,
96+
litClient,
97+
network: 'evm',
98+
memo: 'lit',
99+
});
100+
101+
// Export once if you need the raw private key
102+
const { decryptedPrivateKey } = await wrappedKeysApi.exportPrivateKey({
103+
pkpSessionSigs,
104+
litClient,
105+
network: 'evm',
106+
id,
107+
});
108+
109+
// Sign an EVM transaction without revealing the key outside the Lit nodes
110+
const serializedTx = await wrappedKeysApi.signTransactionWithEncryptedKey({
111+
pkpSessionSigs,
112+
litClient,
113+
network: 'evm',
114+
id,
115+
unsignedTransaction: {
116+
chain: 'yellowstone',
117+
chainId: 175188,
118+
toAddress: '0x0000000000000000000000000000000000000000',
119+
value: '0',
120+
},
121+
broadcast: false,
122+
});
123+
```
124+
125+
## Generate and sign on Solana
126+
127+
```ts
128+
const { id, publicKey } = await wrappedKeysApi.generatePrivateKey({
129+
pkpSessionSigs,
130+
litClient,
131+
network: 'solana',
132+
memo: 'vault signer',
133+
});
134+
135+
const signature = await wrappedKeysApi.signMessageWithEncryptedKey({
136+
pkpSessionSigs,
137+
litClient,
138+
network: 'solana',
139+
id,
140+
messageToSign: 'hello from wrapped keys',
141+
});
142+
```
143+
144+
# API catalogue
145+
146+
See the full reference for every helper at [Wrapped Keys Reference](/sdk/sdk-reference/wrapped-keys/index).

0 commit comments

Comments
 (0)