-
Notifications
You must be signed in to change notification settings - Fork 88
Description
Is there an existing issue for this?
- I have searched the existing issues
SDK version
- SDK Version: v7.3.0
Lit Network
- Network: datil-dev
Description of the bug/issue
When working with multiple PKPs sequentially, session signature generation fails because the SDK uses a globally cached wallet signature that is not scoped to individual PKPs. This causes the second (and subsequent) PKP to incorrectly use the first PKP's cached authentication data.
Impact
- Multi-PKP applications: Cannot reliably use multiple PKPs in the same session
- Testing: Tests pass individually but fail when run sequentially
- Silent failure: No error during authentication; fails later with cryptic error message
- Error message:
"Access control conditions check failed"when attempting to use the PKP
Root Cause
File: packages/lit-node-client-nodejs/src/lib/lit-node-client-nodejs.ts:353
getWalletSig = async ({ ... }: GetWalletSigProps): Promise<AuthSig> => {
let walletSig: AuthSig;
const storageKey = LOCAL_STORAGE_KEYS.WALLET_SIGNATURE; // ❌ Not PKP-specific
const storedWalletSigOrError = getStorageItem(storageKey);
// Retrieves cached signature without validating PKP context
if (storedWalletSigOrError.type !== EITHER_TYPE.ERROR) {
walletSig = JSON.parse(storedWalletSigOrError.result as string);
}
return walletSig!;
};The cache key "lit-wallet-sig" is not scoped by PKP public key, so all PKPs share the same cached signature.
Additional Context
The same issue likely affects SESSION_KEY caching (line 238) which also uses a non-scoped storage key.
Related Code
- Cache key definitions:
packages/constants/src/lib/constants/constants.ts:1551-1559 - Storage utilities:
packages/misc-browser/src/lib/misc-browser.ts:21-70
Severity of the bug
Medium
Steps To Reproduce
import { LitNodeClient } from '@lit-protocol/lit-node-client';
import { EthWalletProvider } from '@lit-protocol/lit-auth-client';
// Test 1: Create first PKP
const authMethod1 = await EthWalletProvider.authenticate({
signer: wallet,
litNodeClient,
expiration,
});
const pkp1 = await api.pkp.mint.post({
authMethod: authMethod1,
scopes: [AUTH_METHOD_SCOPE.SignAnything],
});
const sessionSigs1 = await litNodeClient.getSessionSigs({
pkpPublicKey: pkp1.publicKey,
authNeededCallback: async () => { /* ... */ },
});
console.log('PKP1 Session Sig Address:', Object.values(sessionSigs1)[0].address);
// Output: "0x3D2870939c94FEC0aa94A78ff6a53FBF6b938aAc" ✅
// Test 2: Create second PKP (WITHOUT clearing localStorage)
const authMethod2 = await EthWalletProvider.authenticate({
signer: wallet,
litNodeClient,
expiration,
});
const pkp2 = await api.pkp.mint.post({
authMethod: authMethod2,
scopes: [AUTH_METHOD_SCOPE.SignAnything],
});
const sessionSigs2 = await litNodeClient.getSessionSigs({
pkpPublicKey: pkp2.publicKey,
authNeededCallback: async () => { /* ... */ },
});
console.log('PKP2 Session Sig Address:', Object.values(sessionSigs2)[0].address);
// Output: "0x3D2870939c94FEC0aa94A78ff6a53FBF6b938aAc" ❌ WRONG! Should be PKP2's address
// Bug: sessionSigs2 contains PKP1's address because it used cached signature
// Later operations fail:
await litNodeClient.executeJs({
sessionSigs: sessionSigs2, // ❌ Fails with "Access control conditions check failed"
code: `...`,
});Expected Behavior
Each PKP should have its own cached wallet signature, or the cache should be invalidated when the PKP context changes.
Current Workaround
Manually clear localStorage between PKP operations:
localStorage.removeItem('lit-wallet-sig');
localStorage.removeItem('lit-session-key');Link to code
- Affected Files:
packages/lit-node-client-nodejs/src/lib/lit-node-client-nodejs.tspackages/lit-auth-client/src/lib/providers/EthWalletProvider.ts
Anything else?
- Runtime: Node.js (Bun)
Suggested Fixes
Option 1: Scope cache key by PKP (Recommended)
const storageKey = pkpPublicKey
? `${LOCAL_STORAGE_KEYS.WALLET_SIGNATURE}-${pkpPublicKey}`
: LOCAL_STORAGE_KEYS.WALLET_SIGNATURE;Option 2: Add validation
Check if cached signature matches current PKP context before reusing:
if (storedWalletSigOrError.type !== EITHER_TYPE.ERROR) {
const cached = JSON.parse(storedWalletSigOrError.result as string);
// Validate signature is for correct PKP
if (isValidForCurrentPKP(cached, pkpPublicKey)) {
walletSig = cached;
}
}Option 3: Clear cache on new authentication
EthWalletProvider.authenticate() should clear stale cached signatures:
public static async authenticate({ ... }) {
// Clear previous PKP's cached signature
removeStorageItem(LOCAL_STORAGE_KEYS.WALLET_SIGNATURE);
removeStorageItem(LOCAL_STORAGE_KEYS.SESSION_KEY);
// ... rest of authentication
}