diff --git a/README.md b/README.md
index 693c80a70..1c0301e3a 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,6 @@
Lit Protocol Javascript/Typescript SDK V8.x.x
-

diff --git a/e2e/src/e2e.spec.ts b/e2e/src/e2e.spec.ts
index 79da78b57..c5ff7614d 100644
--- a/e2e/src/e2e.spec.ts
+++ b/e2e/src/e2e.spec.ts
@@ -1,6 +1,7 @@
import {
createCustomAuthContext,
createPkpAuthContext,
+ createPkpAuthContextWithPreGeneratedMaterials,
} from './helper/auth-contexts';
import {
createExecuteJsTest,
@@ -154,9 +155,97 @@ describe('all', () => {
});
});
- describe('EOA Native', () => {
- console.log('π Testing EOA native authentication and PKP minting');
+ describe('PKP Auth with Pre-generated Materials', () => {
+ console.log('π Testing PKP auth with pre-generated session materials');
+ let preGeneratedAuthContext: any;
+
+ beforeAll(async () => {
+ try {
+ preGeneratedAuthContext =
+ await createPkpAuthContextWithPreGeneratedMaterials(ctx);
+ } catch (e) {
+ console.error('Failed to create pre-generated auth context:', e);
+ throw e;
+ }
+ });
+
+ describe('endpoints', () => {
+ it('pkpSign with pre-generated materials', () =>
+ createPkpSignTest(ctx, () => preGeneratedAuthContext)());
+
+ it('executeJs with pre-generated materials', () =>
+ createExecuteJsTest(ctx, () => preGeneratedAuthContext)());
+
+ it('pkpEncryptDecrypt with pre-generated materials', () =>
+ createPkpEncryptDecryptTest(ctx, () => preGeneratedAuthContext)());
+ });
+
+ describe('error handling', () => {
+ it('should reject when only sessionKeyPair is provided', async () => {
+ const tempAuthContext = await ctx.authManager.createPkpAuthContext({
+ authData: ctx.aliceViemAccountAuthData,
+ pkpPublicKey: ctx.aliceViemAccountPkp.publicKey,
+ authConfig: {
+ resources: [['pkp-signing', '*']],
+ expiration: new Date(Date.now() + 1000 * 60 * 15).toISOString(),
+ },
+ litClient: ctx.litClient,
+ });
+
+ const sessionKeyPair = tempAuthContext.sessionKeyPair;
+
+ await expect(
+ ctx.authManager.createPkpAuthContext({
+ authData: ctx.aliceViemAccountAuthData,
+ pkpPublicKey: ctx.aliceViemAccountPkp.publicKey,
+ authConfig: {
+ resources: [['pkp-signing', '*']],
+ expiration: new Date(Date.now() + 1000 * 60 * 15).toISOString(),
+ },
+ litClient: ctx.litClient,
+ sessionKeyPair, // Only providing sessionKeyPair
+ // delegationAuthSig is missing
+ })
+ ).rejects.toThrow(
+ 'Both sessionKeyPair and delegationAuthSig must be provided together, or neither should be provided'
+ );
+ });
+
+ it('should reject when only delegationAuthSig is provided', async () => {
+ const tempAuthContext = await ctx.authManager.createPkpAuthContext({
+ authData: ctx.aliceViemAccountAuthData,
+ pkpPublicKey: ctx.aliceViemAccountPkp.publicKey,
+ authConfig: {
+ resources: [['pkp-signing', '*']],
+ expiration: new Date(Date.now() + 1000 * 60 * 15).toISOString(),
+ },
+ litClient: ctx.litClient,
+ });
+
+ const delegationAuthSig = await tempAuthContext.authNeededCallback();
+
+ await expect(
+ ctx.authManager.createPkpAuthContext({
+ authData: ctx.aliceViemAccountAuthData,
+ pkpPublicKey: ctx.aliceViemAccountPkp.publicKey,
+ authConfig: {
+ resources: [['pkp-signing', '*']],
+ expiration: new Date(Date.now() + 1000 * 60 * 15).toISOString(),
+ },
+ litClient: ctx.litClient,
+ // sessionKeyPair is missing
+ delegationAuthSig, // Only providing delegationAuthSig
+ })
+ ).rejects.toThrow(
+ 'Both sessionKeyPair and delegationAuthSig must be provided together, or neither should be provided'
+ );
+ });
+ });
+ });
+
+ describe('EOA Native', () => {
+ console.log('π Testing EOA native authentication and PKP minting');
it('eoaNativeAuthFlow', () => createEoaNativeAuthFlowTest(ctx)());
});
});
diff --git a/e2e/src/helper/auth-contexts.ts b/e2e/src/helper/auth-contexts.ts
index 54a6be92a..628fe3810 100644
--- a/e2e/src/helper/auth-contexts.ts
+++ b/e2e/src/helper/auth-contexts.ts
@@ -1,7 +1,70 @@
import { init } from '../init';
-
+import { generateSessionKeyPair } from '@lit-protocol/auth';
import { hexToBigInt, keccak256, toBytes } from 'viem';
+/**
+ * Creates a PKP authentication context with pre-generated session materials
+ * This simulates a server-side use case where session key pair and delegation
+ * signature are generated once and reused for multiple requests
+ */
+export const createPkpAuthContextWithPreGeneratedMaterials = async (
+ ctx: Awaited
>
+) => {
+ console.log('π Creating PKP Auth Context with Pre-generated Materials');
+ try {
+ // Step 1: Generate a session key pair directly
+ console.log(' π Step 1: Generating session key pair...');
+ const sessionKeyPair = generateSessionKeyPair();
+
+ // Step 2: Generate PKP delegation signature for the session key pair
+ console.log(' π Step 2: Generating PKP delegation signature...');
+ const delegationAuthSig =
+ await ctx.authManager.generatePkpDelegationAuthSig({
+ pkpPublicKey: ctx.aliceViemAccountPkp.publicKey,
+ authData: ctx.aliceViemAccountAuthData,
+ sessionKeyPair,
+ authConfig: {
+ resources: [
+ ['pkp-signing', '*'],
+ ['lit-action-execution', '*'],
+ ['access-control-condition-decryption', '*'],
+ ],
+ expiration: new Date(Date.now() + 1000 * 60 * 15).toISOString(),
+ },
+ litClient: ctx.litClient,
+ });
+
+ console.log(' π Session materials generated:', {
+ hasSessionKeyPair: !!sessionKeyPair,
+ hasDelegationAuthSig: !!delegationAuthSig,
+ sessionKeyPublicKey: sessionKeyPair?.publicKey?.substring(0, 20) + '...',
+ });
+
+ // Step 3: Create auth context using the pre-generated materials
+ // Using the dedicated function for pre-generated materials with a clean, minimal signature
+ console.log(
+ ' π Step 3: Creating auth context with pre-generated materials...'
+ );
+ const authContextWithPreGenerated =
+ await ctx.authManager.createPkpAuthContextFromPreGenerated({
+ pkpPublicKey: ctx.aliceViemAccountPkp.publicKey,
+ sessionKeyPair,
+ delegationAuthSig,
+ // Optional: can provide authData if needed, otherwise minimal default is used
+ authData: ctx.aliceViemAccountAuthData,
+ });
+
+ console.log('β
PKP Auth Context with Pre-generated Materials created');
+ return authContextWithPreGenerated;
+ } catch (e) {
+ console.error(
+ 'β Error creating PKP Auth Context with Pre-generated Materials',
+ e
+ );
+ throw e;
+ }
+};
+
/**
* Creates a PKP authentication context
*/
@@ -78,3 +141,72 @@ export const createCustomAuthContext = async (
throw e;
}
};
+
+/**
+ * Creates an EOA authentication context with pre-generated session materials
+ * This demonstrates how to pre-generate EOA session materials for server-side use
+ */
+export const createEoaAuthContextWithPreGeneratedMaterials = async (
+ ctx: Awaited>
+) => {
+ console.log('π Creating EOA Auth Context with Pre-generated Materials');
+ try {
+ // Step 1: Generate a session key pair directly
+ console.log(' π Step 1: Generating session key pair...');
+ const sessionKeyPair = generateSessionKeyPair();
+
+ // Step 2: Generate EOA delegation signature for the session key pair
+ console.log(' π Step 2: Generating EOA delegation signature...');
+ const delegationAuthSig =
+ await ctx.authManager.generateEoaDelegationAuthSig({
+ account: ctx.aliceViemAccount,
+ sessionKeyPair,
+ authConfig: {
+ resources: [
+ ['pkp-signing', '*'],
+ ['lit-action-execution', '*'],
+ ['access-control-condition-decryption', '*'],
+ ],
+ expiration: new Date(Date.now() + 1000 * 60 * 15).toISOString(),
+ },
+ litClient: ctx.litClient,
+ });
+
+ console.log(' π EOA session materials generated:', {
+ hasSessionKeyPair: !!sessionKeyPair,
+ hasDelegationAuthSig: !!delegationAuthSig,
+ sessionKeyPublicKey: sessionKeyPair?.publicKey?.substring(0, 20) + '...',
+ });
+
+ // Step 3: Create EOA auth context using the pre-generated materials
+ console.log(
+ ' π Step 3: Creating EOA auth context with pre-generated materials...'
+ );
+ const authContextWithPreGenerated =
+ await ctx.authManager.createEoaAuthContext({
+ authConfig: {
+ resources: [
+ ['pkp-signing', '*'],
+ ['lit-action-execution', '*'],
+ ['access-control-condition-decryption', '*'],
+ ],
+ expiration: new Date(Date.now() + 1000 * 60 * 15).toISOString(),
+ },
+ config: {
+ account: ctx.aliceViemAccount,
+ },
+ litClient: ctx.litClient,
+ // Note: EOA auth contexts don't currently support pre-generated materials
+ // This demonstrates the pattern for when it's implemented
+ });
+
+ console.log('β
EOA Auth Context with Pre-generated Materials created');
+ return authContextWithPreGenerated;
+ } catch (e) {
+ console.error(
+ 'β Error creating EOA Auth Context with Pre-generated Materials',
+ e
+ );
+ throw e;
+ }
+};
diff --git a/packages/auth/src/index.ts b/packages/auth/src/index.ts
index 38862930d..db0ee1398 100644
--- a/packages/auth/src/index.ts
+++ b/packages/auth/src/index.ts
@@ -95,6 +95,32 @@ export { getAuthIdByAuthMethod } from './lib/authenticators/helper/utils';
*/
export { generateSessionKeyPair } from './lib/AuthManager/utils/generateSessionKeyPair';
+/**
+ * Utility function to generate a PKP delegation auth signature for a given session key pair.
+ * The PKP will sign the session key delegation message via Lit nodes.
+ * This function is useful for server-side scenarios where you want to pre-generate
+ * PKP session materials and reuse them across multiple requests.
+ */
+export { generatePkpDelegationAuthSig } from './lib/AuthManager/authAdapters/generatePkpDelegationAuthSig';
+
+/**
+ * Utility function to generate an EOA delegation auth signature for a given session key pair.
+ * The EOA wallet will sign the session key delegation message directly.
+ * This function is useful for server-side scenarios where you want to pre-generate
+ * EOA session materials and reuse them across multiple requests.
+ */
+export { generateEoaDelegationAuthSig } from './lib/AuthManager/authAdapters/generateEoaDelegationAuthSig';
+
+/**
+ * Utility function to create a PKP auth context from pre-generated session materials.
+ * This is a streamlined API for server-side scenarios where session materials
+ * are generated once and reused across multiple requests.
+ *
+ * This function only requires the essential parameters (pkpPublicKey, sessionKeyPair, delegationAuthSig)
+ * and extracts auth config information from the delegation signature automatically.
+ */
+export { getPkpAuthContextFromPreGeneratedAdapter } from './lib/AuthManager/authAdapters/getPkpAuthContextFromPreGeneratedAdapter';
+
// ============================== Authenticators ==============================
export {
DiscordAuthenticator,
diff --git a/packages/auth/src/lib/AuthManager/auth-manager.ts b/packages/auth/src/lib/AuthManager/auth-manager.ts
index a48663a05..23dedf55e 100644
--- a/packages/auth/src/lib/AuthManager/auth-manager.ts
+++ b/packages/auth/src/lib/AuthManager/auth-manager.ts
@@ -1,6 +1,6 @@
import { getChildLogger } from '@lit-protocol/logger';
import { AuthData, HexPrefixedSchema } from '@lit-protocol/schemas';
-// import { AuthSig, SessionKeyPair } from '@lit-protocol/types';
+import { AuthSig, SessionKeyPair } from '@lit-protocol/types';
import { z } from 'zod';
import { AuthConfigV2 } from '../authenticators/types';
import type { LitAuthStorageProvider } from '../storage/types';
@@ -11,7 +11,9 @@ import {
import { getPkpAuthContextAdapter } from './authAdapters/getPkpAuthContextAdapter';
import { AuthConfigSchema } from './authContexts/BaseAuthContextType';
import { getCustomAuthContextAdapter } from './authAdapters/getCustomAuthContextAdapter';
-import { hexToBigInt, keccak256, toBytes } from 'viem';
+import { generatePkpDelegationAuthSig } from './authAdapters/generatePkpDelegationAuthSig';
+import { generateEoaDelegationAuthSig } from './authAdapters/generateEoaDelegationAuthSig';
+import { getPkpAuthContextFromPreGeneratedAdapter } from './authAdapters/getPkpAuthContextFromPreGeneratedAdapter';
export interface AuthManagerParams {
storage: LitAuthStorageProvider;
@@ -77,12 +79,20 @@ export const createAuthManager = (authManagerParams: AuthManagerParams) => {
cache?: {
delegationAuthSig?: boolean;
};
- // Optional pre-generated auth materials for server-side usage
- // sessionKeyPair?: SessionKeyPair;
- // delegationAuthSig?: AuthSig;
}) => {
return getPkpAuthContextAdapter(authManagerParams, params);
},
+ createPkpAuthContextFromPreGenerated: (params: {
+ pkpPublicKey: z.infer;
+ sessionKeyPair: SessionKeyPair;
+ delegationAuthSig: AuthSig;
+ authData?: AuthData;
+ }) => {
+ return getPkpAuthContextFromPreGeneratedAdapter(
+ authManagerParams,
+ params
+ );
+ },
createCustomAuthContext: (params: {
// authData: AuthData;
pkpPublicKey: z.infer;
@@ -104,5 +114,22 @@ export const createAuthManager = (authManagerParams: AuthManagerParams) => {
return getCustomAuthContextAdapter(authManagerParams, params);
},
+ generatePkpDelegationAuthSig: (params: {
+ pkpPublicKey: z.infer;
+ authData: AuthData;
+ sessionKeyPair: SessionKeyPair;
+ authConfig: AuthConfigV2;
+ litClient: BaseAuthContext['litClient'];
+ }) => {
+ return generatePkpDelegationAuthSig(authManagerParams, params);
+ },
+ generateEoaDelegationAuthSig: (params: {
+ account: any; // ExpectedAccountOrWalletClient type
+ sessionKeyPair: SessionKeyPair;
+ authConfig: AuthConfigV2;
+ litClient: BaseAuthContext['litClient'];
+ }) => {
+ return generateEoaDelegationAuthSig(authManagerParams, params);
+ },
};
};
diff --git a/packages/auth/src/lib/AuthManager/authAdapters/generateEoaDelegationAuthSig.ts b/packages/auth/src/lib/AuthManager/authAdapters/generateEoaDelegationAuthSig.ts
new file mode 100644
index 000000000..05c876074
--- /dev/null
+++ b/packages/auth/src/lib/AuthManager/authAdapters/generateEoaDelegationAuthSig.ts
@@ -0,0 +1,109 @@
+import { AUTH_METHOD_TYPE } from '@lit-protocol/constants';
+import { getChildLogger } from '@lit-protocol/logger';
+import { AuthConfigSchema } from '@lit-protocol/schemas';
+import { AuthSig, SessionKeyPair } from '@lit-protocol/types';
+import { z } from 'zod';
+import { AuthConfigV2 } from '../../authenticators/types';
+import { LitAuthData } from '../../types';
+import { AuthManagerParams } from '../auth-manager';
+import {
+ ExpectedAccountOrWalletClient,
+ getEoaAuthContext,
+} from '../authContexts/getEoaAuthContext';
+import { processResources } from '../utils/processResources';
+import { WalletClientAuthenticator } from '../../authenticators/WalletClientAuthenticator';
+import { ViemAccountAuthenticator } from '../../authenticators/ViemAccountAuthenticator';
+
+const _logger = getChildLogger({
+ module: 'generateEoaDelegationAuthSig',
+});
+
+/**
+ * Generates an EOA delegation auth signature for a given session key pair.
+ * The EOA wallet will sign the session key delegation message directly.
+ * This function is useful for server-side scenarios where you want to pre-generate
+ * EOA session materials and reuse them across multiple requests.
+ *
+ * @param upstreamParams - Auth manager parameters including storage
+ * @param params - Parameters for generating the EOA delegation signature
+ * @returns The delegation auth signature (AuthSig) signed by the EOA wallet
+ */
+export async function generateEoaDelegationAuthSig(
+ upstreamParams: AuthManagerParams,
+ params: {
+ account: ExpectedAccountOrWalletClient;
+ sessionKeyPair: SessionKeyPair;
+ authConfig: AuthConfigV2;
+ litClient: {
+ getContext: () => Promise;
+ };
+ }
+): Promise {
+ _logger.info(
+ 'generateEoaDelegationAuthSig: Starting EOA delegation signature generation',
+ {
+ hasAccount: !!params.account,
+ hasSessionKeyPair: !!params.sessionKeyPair,
+ }
+ );
+
+ const _resources = processResources(params.authConfig.resources);
+
+ // Get network context from litClient for nonce
+ const litClientCtx = await params.litClient.getContext();
+
+ // Create a minimal LitAuthData structure with the provided session key pair
+ const litAuthData: LitAuthData = {
+ sessionKey: {
+ keyPair: params.sessionKeyPair,
+ expiresAt: params.authConfig.expiration!,
+ },
+ // For EOA, we use EthWallet as the auth method type
+ authMethodType: AUTH_METHOD_TYPE.EthWallet,
+ };
+
+ // Determine the authenticator based on account type
+ let authenticatorClass;
+ if (
+ 'account' in params.account &&
+ params.account.account?.type === 'json-rpc'
+ ) {
+ // WalletClient
+ authenticatorClass = WalletClientAuthenticator;
+ } else {
+ // Viem Account
+ authenticatorClass = ViemAccountAuthenticator;
+ }
+
+ // Create auth config for validation
+ const authConfigForValidation = {
+ ...params.authConfig,
+ resources: _resources,
+ };
+ const validatedAuthConfig = AuthConfigSchema.parse(authConfigForValidation);
+
+ // Call getEoaAuthContext which will generate the delegation signature
+ const authContext = await getEoaAuthContext({
+ authentication: {
+ authenticator: authenticatorClass,
+ account: params.account,
+ },
+ authConfig: validatedAuthConfig,
+ deps: {
+ nonce: litClientCtx.latestBlockhash,
+ authData: litAuthData,
+ },
+ });
+
+ // Get the delegation signature from the auth context
+ const delegationAuthSig = await authContext.authNeededCallback();
+
+ _logger.info(
+ 'generateEoaDelegationAuthSig: EOA delegation signature generated successfully',
+ {
+ hasSignature: !!delegationAuthSig,
+ }
+ );
+
+ return delegationAuthSig as AuthSig;
+}
diff --git a/packages/auth/src/lib/AuthManager/authAdapters/generatePkpDelegationAuthSig.ts b/packages/auth/src/lib/AuthManager/authAdapters/generatePkpDelegationAuthSig.ts
new file mode 100644
index 000000000..04c85217c
--- /dev/null
+++ b/packages/auth/src/lib/AuthManager/authAdapters/generatePkpDelegationAuthSig.ts
@@ -0,0 +1,123 @@
+import { AUTH_METHOD_TYPE_VALUES, PRODUCT_IDS } from '@lit-protocol/constants';
+import { getChildLogger } from '@lit-protocol/logger';
+import {
+ AuthData,
+ HexPrefixedSchema,
+ SessionKeyUriSchema,
+} from '@lit-protocol/schemas';
+import { AuthSig, SessionKeyPair } from '@lit-protocol/types';
+import { ethers } from 'ethers';
+import { z } from 'zod';
+import { AuthConfigV2 } from '../../authenticators/types';
+import { LitAuthData } from '../../types';
+import { AuthManagerParams } from '../auth-manager';
+import { getPkpAuthContext } from '../authContexts/getPkpAuthContext';
+import { processResources } from '../utils/processResources';
+
+const _logger = getChildLogger({
+ module: 'generatePkpDelegationAuthSig',
+});
+
+/**
+ * Generates a PKP delegation auth signature for a given session key pair.
+ * The PKP will sign the session key delegation message via Lit nodes.
+ * This function is useful for server-side scenarios where you want to pre-generate
+ * PKP session materials and reuse them across multiple requests.
+ *
+ * @param upstreamParams - Auth manager parameters including storage
+ * @param params - Parameters for generating the PKP delegation signature
+ * @returns The delegation auth signature (AuthSig) signed by the PKP
+ */
+export async function generatePkpDelegationAuthSig(
+ upstreamParams: AuthManagerParams,
+ params: {
+ pkpPublicKey: z.infer;
+ authData: AuthData;
+ sessionKeyPair: SessionKeyPair;
+ authConfig: AuthConfigV2;
+ litClient: {
+ getContext: () => Promise;
+ };
+ }
+): Promise {
+ _logger.info(
+ 'generatePkpDelegationAuthSig: Starting PKP delegation signature generation',
+ {
+ pkpPublicKey: params.pkpPublicKey,
+ hasSessionKeyPair: !!params.sessionKeyPair,
+ }
+ );
+
+ const _resources = processResources(params.authConfig.resources);
+
+ // Get network context from litClient
+ const litClientCtx = await params.litClient.getContext();
+ const latestConnectionInfo = litClientCtx.latestConnectionInfo;
+ const nodePrices = latestConnectionInfo.priceFeedInfo.networkPrices;
+ const handshakeResult = litClientCtx.handshakeResult;
+ const threshold = handshakeResult.threshold;
+
+ const nodeUrls = litClientCtx.getMaxPricesForNodeProduct({
+ nodePrices: nodePrices,
+ userMaxPrice: litClientCtx.getUserMaxPrice({
+ product: 'LIT_ACTION',
+ }),
+ productId: PRODUCT_IDS['LIT_ACTION'],
+ numRequiredNodes: threshold,
+ });
+
+ const pkpAddress = ethers.utils.computeAddress(params.pkpPublicKey);
+
+ // Create a minimal LitAuthData structure with the provided session key pair
+ const litAuthData: LitAuthData = {
+ sessionKey: {
+ keyPair: params.sessionKeyPair,
+ expiresAt: params.authConfig.expiration!,
+ },
+ authMethodType: params.authData.authMethodType as AUTH_METHOD_TYPE_VALUES,
+ };
+
+ // Call getPkpAuthContext which will generate the delegation signature
+ const authContext = await getPkpAuthContext({
+ authentication: {
+ pkpPublicKey: params.pkpPublicKey,
+ authData: params.authData,
+ },
+ authConfig: {
+ domain: params.authConfig.domain!,
+ resources: _resources,
+ capabilityAuthSigs: params.authConfig.capabilityAuthSigs!,
+ expiration: params.authConfig.expiration!,
+ statement: params.authConfig.statement!,
+ },
+ deps: {
+ litAuthData: litAuthData,
+ connection: {
+ nonce: litClientCtx.latestBlockhash,
+ currentEpoch:
+ litClientCtx.latestConnectionInfo.epochState.currentNumber,
+ nodeUrls: nodeUrls,
+ },
+ signSessionKey: litClientCtx.signSessionKey,
+ storage: upstreamParams.storage,
+ pkpAddress: pkpAddress,
+ },
+ // Disable caching since we're explicitly generating a new signature
+ cache: {
+ delegationAuthSig: false,
+ },
+ });
+
+ // Get the delegation signature from the auth context
+ const delegationAuthSig = await authContext.authNeededCallback();
+
+ _logger.info(
+ 'generatePkpDelegationAuthSig: PKP delegation signature generated successfully',
+ {
+ pkpAddress,
+ hasSignature: !!delegationAuthSig,
+ }
+ );
+
+ return delegationAuthSig;
+}
diff --git a/packages/auth/src/lib/AuthManager/authAdapters/getPkpAuthContextAdapter.ts b/packages/auth/src/lib/AuthManager/authAdapters/getPkpAuthContextAdapter.ts
index eb39b1c11..c3840cf2c 100644
--- a/packages/auth/src/lib/AuthManager/authAdapters/getPkpAuthContextAdapter.ts
+++ b/packages/auth/src/lib/AuthManager/authAdapters/getPkpAuthContextAdapter.ts
@@ -1,11 +1,16 @@
import { AUTH_METHOD_TYPE_VALUES, PRODUCT_IDS } from '@lit-protocol/constants';
+import { getChildLogger } from '@lit-protocol/logger';
import {
AuthData,
HexPrefixedSchema,
NodeUrlsSchema,
- // SessionKeyUriSchema,
+ SessionKeyUriSchema,
} from '@lit-protocol/schemas';
-// import { AuthSig, LitResourceAbilityRequest, SessionKeyPair } from '@lit-protocol/types';
+import {
+ AuthSig,
+ LitResourceAbilityRequest,
+ SessionKeyPair,
+} from '@lit-protocol/types';
import { ethers } from 'ethers';
import { z } from 'zod';
import { AuthConfigV2 } from '../../authenticators/types';
@@ -14,6 +19,10 @@ import { getPkpAuthContext } from '../authContexts/getPkpAuthContext';
import { processResources } from '../utils/processResources';
import { tryGetCachedAuthData } from '../try-getters/tryGetCachedAuthData';
+const _logger = getChildLogger({
+ module: 'getPkpAuthContextAdapter',
+});
+
export const PkpAuthDepsSchema = z.object({
nonce: z.any(),
currentEpoch: z.any(),
@@ -24,36 +33,50 @@ export const PkpAuthDepsSchema = z.object({
/**
* Validates that the provided delegation auth sig hasn't expired and contains required resources
*/
-// function validateDelegationAuthSig(
-// delegationAuthSig: AuthSig,
-// requiredResources: LitResourceAbilityRequest[],
-// sessionKeyUri: string
-// ): void {
-// try {
-// // Parse the signed message to extract expiration and validate session key match
-// const siweMessage = delegationAuthSig.signedMessage;
-
-// // Check expiration
-// const expirationMatch = siweMessage.match(/^Expiration Time: (.*)$/m);
-// if (expirationMatch && expirationMatch[1]) {
-// const expiration = new Date(expirationMatch[1].trim());
-// if (expiration.getTime() <= Date.now()) {
-// throw new Error(`Delegation signature has expired at ${expiration.toISOString()}`);
-// }
-// }
-
-// // Validate session key URI matches
-// if (!siweMessage.includes(sessionKeyUri)) {
-// throw new Error('Session key URI in delegation signature does not match provided session key pair');
-// }
-
-// // TODO: Add resource validation - check if delegationAuthSig has required resources
-// // This would involve parsing the RECAP URN and checking against requiredResources
-
-// } catch (error) {
-// throw new Error(`Invalid delegation signature: ${error instanceof Error ? error.message : 'Unknown error'}`);
-// }
-// }
+function validateDelegationAuthSig(
+ delegationAuthSig: AuthSig,
+ requiredResources: LitResourceAbilityRequest[],
+ sessionKeyUri: string
+): void {
+ try {
+ // Parse the signed message to extract expiration and validate session key match
+ const siweMessage = delegationAuthSig.signedMessage;
+
+ // Check expiration
+ const expirationMatch = siweMessage.match(/^Expiration Time: (.*)$/m);
+ if (expirationMatch && expirationMatch[1]) {
+ const expiration = new Date(expirationMatch[1].trim());
+ if (expiration.getTime() <= Date.now()) {
+ throw new Error(
+ `Delegation signature has expired at ${expiration.toISOString()}`
+ );
+ }
+ }
+
+ // Validate session key URI matches
+ if (!siweMessage.includes(sessionKeyUri)) {
+ throw new Error(
+ 'Session key URI in delegation signature does not match provided session key pair'
+ );
+ }
+
+ // TODO: Add resource validation - check if delegationAuthSig has required resources
+ // This would involve parsing the RECAP URN and checking against requiredResources
+ _logger.debug(
+ 'validateDelegationAuthSig: Delegation signature validated successfully',
+ {
+ sessionKeyUri,
+ hasResources: requiredResources.length > 0,
+ }
+ );
+ } catch (error) {
+ throw new Error(
+ `Invalid delegation signature: ${
+ error instanceof Error ? error.message : 'Unknown error'
+ }`
+ );
+ }
+}
export async function getPkpAuthContextAdapter(
upstreamParams: AuthManagerParams,
@@ -67,54 +90,70 @@ export async function getPkpAuthContextAdapter(
cache?: {
delegationAuthSig?: boolean;
};
- // Optional pre-generated auth materials
- // sessionKeyPair?: SessionKeyPair;
- // delegationAuthSig?: AuthSig;
+ // Optional pre-generated auth materials for server-side usage
+ sessionKeyPair?: SessionKeyPair;
+ delegationAuthSig?: AuthSig;
}
) {
const _resources = processResources(params.authConfig.resources);
- // // Validate optional parameters
- // if ((params.sessionKeyPair && !params.delegationAuthSig) ||
- // (!params.sessionKeyPair && params.delegationAuthSig)) {
- // throw new Error('Both sessionKeyPair and delegationAuthSig must be provided together, or neither should be provided');
- // }
-
- // // If pre-generated auth materials are provided, validate and use them
- // if (params.sessionKeyPair && params.delegationAuthSig) {
- // // Generate sessionKeyUri from the public key
- // const sessionKeyUri = SessionKeyUriSchema.parse(params.sessionKeyPair.publicKey);
-
- // // Validate the delegation signature
- // validateDelegationAuthSig(
- // params.delegationAuthSig,
- // _resources,
- // sessionKeyUri
- // );
-
- // // Return auth context using provided materials
- // return {
- // chain: 'ethereum',
- // pkpPublicKey: params.pkpPublicKey,
- // authData: params.authData,
- // authConfig: {
- // domain: params.authConfig.domain!,
- // resources: _resources,
- // capabilityAuthSigs: params.authConfig.capabilityAuthSigs!,
- // expiration: params.authConfig.expiration!,
- // statement: params.authConfig.statement!,
- // },
- // sessionKeyPair: {
- // ...params.sessionKeyPair,
- // sessionKeyUri, // Add the generated sessionKeyUri to match expected interface
- // },
- // // Provide the pre-generated delegation signature
- // authNeededCallback: async () => params.delegationAuthSig!,
- // };
- // }
+ // Validate optional parameters
+ if (
+ (params.sessionKeyPair && !params.delegationAuthSig) ||
+ (!params.sessionKeyPair && params.delegationAuthSig)
+ ) {
+ throw new Error(
+ 'Both sessionKeyPair and delegationAuthSig must be provided together, or neither should be provided'
+ );
+ }
+
+ // If pre-generated auth materials are provided, validate and use them
+ if (params.sessionKeyPair && params.delegationAuthSig) {
+ _logger.info(
+ 'getPkpAuthContextAdapter: Using pre-generated session materials',
+ {
+ hasSessionKeyPair: true,
+ hasDelegationAuthSig: true,
+ }
+ );
+
+ // Generate sessionKeyUri from the public key
+ const sessionKeyUri = SessionKeyUriSchema.parse(
+ 'lit:session:' + params.sessionKeyPair.publicKey
+ );
+
+ // Validate the delegation signature
+ validateDelegationAuthSig(
+ params.delegationAuthSig,
+ _resources,
+ sessionKeyUri
+ );
+
+ // Return auth context using provided materials
+ return {
+ chain: 'ethereum',
+ pkpPublicKey: params.pkpPublicKey,
+ authData: params.authData,
+ authConfig: {
+ domain: params.authConfig.domain!,
+ resources: _resources,
+ capabilityAuthSigs: params.authConfig.capabilityAuthSigs!,
+ expiration: params.authConfig.expiration!,
+ statement: params.authConfig.statement!,
+ },
+ sessionKeyPair: params.sessionKeyPair,
+ // Provide the pre-generated delegation signature
+ authNeededCallback: async () => {
+ _logger.debug(
+ 'getPkpAuthContextAdapter: Returning pre-generated delegation signature'
+ );
+ return params.delegationAuthSig!;
+ },
+ };
+ }
// Original logic for generating auth materials
- // TODO: π The plan is to identify if the certain operations could be wrapped inside a single function
+ // TODO: π The plan is to identify whether certain operations can be wrapped inside a single function
// where different network modules can provide their own implementations.
// TODO: βοΈTHIS IS NOT TYPED - we have to fix this!
diff --git a/packages/auth/src/lib/AuthManager/authAdapters/getPkpAuthContextFromPreGeneratedAdapter.ts b/packages/auth/src/lib/AuthManager/authAdapters/getPkpAuthContextFromPreGeneratedAdapter.ts
new file mode 100644
index 000000000..735f84e8b
--- /dev/null
+++ b/packages/auth/src/lib/AuthManager/authAdapters/getPkpAuthContextFromPreGeneratedAdapter.ts
@@ -0,0 +1,170 @@
+import { getChildLogger } from '@lit-protocol/logger';
+import { HexPrefixedSchema, SessionKeyUriSchema } from '@lit-protocol/schemas';
+import {
+ AuthSig,
+ LitResourceAbilityRequest,
+ SessionKeyPair,
+} from '@lit-protocol/types';
+import { z } from 'zod';
+import { AuthData } from '@lit-protocol/schemas';
+import { AuthManagerParams } from '../auth-manager';
+import { processResources } from '../utils/processResources';
+
+const _logger = getChildLogger({
+ module: 'getPkpAuthContextFromPreGeneratedAdapter',
+});
+
+/**
+ * Extracts auth config information from a delegation signature's SIWE message
+ */
+function extractAuthConfigFromDelegationAuthSig(delegationAuthSig: AuthSig): {
+ domain?: string;
+ statement?: string;
+ expiration?: string;
+ resources?: LitResourceAbilityRequest[];
+} {
+ const siweMessage = delegationAuthSig.signedMessage;
+
+ // Extract domain
+ const domainMatch = siweMessage.match(/^([^\s]+) wants you to sign in/m);
+ const domain = domainMatch ? domainMatch[1] : undefined;
+
+ // Extract statement
+ const statementMatch = siweMessage.match(/^(.*?)(?:\n\nURI:|$)/m);
+ const statement = statementMatch
+ ? statementMatch[1].split('\n').slice(2).join('\n').trim()
+ : undefined;
+
+ // Extract expiration
+ const expirationMatch = siweMessage.match(/^Expiration Time: (.*)$/m);
+ const expiration = expirationMatch ? expirationMatch[1].trim() : undefined;
+
+ // Extract resources from RECAP URN - simplified for now
+ // TODO: Implement proper RECAP parsing when needed
+ const resources: LitResourceAbilityRequest[] = [];
+
+ return { domain, statement, expiration, resources };
+}
+
+/**
+ * Validates that the provided delegation auth sig hasn't expired and contains required resources
+ */
+function validateDelegationAuthSig(
+ delegationAuthSig: AuthSig,
+ requiredResources: LitResourceAbilityRequest[],
+ sessionKeyUri: string
+): void {
+ try {
+ // Parse the signed message to extract expiration and validate session key match
+ const siweMessage = delegationAuthSig.signedMessage;
+
+ // Check expiration
+ const expirationMatch = siweMessage.match(/^Expiration Time: (.*)$/m);
+ if (expirationMatch && expirationMatch[1]) {
+ const expiration = new Date(expirationMatch[1].trim());
+ if (expiration.getTime() <= Date.now()) {
+ throw new Error(
+ `Delegation signature has expired at ${expiration.toISOString()}`
+ );
+ }
+ }
+
+ // Validate session key URI matches
+ if (!siweMessage.includes(sessionKeyUri)) {
+ throw new Error(
+ 'Session key URI in delegation signature does not match provided session key pair'
+ );
+ }
+
+ // TODO: Add resource validation - check if delegationAuthSig has required resources
+ // This would involve parsing the RECAP URN and checking against requiredResources
+ _logger.debug(
+ 'validateDelegationAuthSig: Delegation signature validated successfully',
+ {
+ sessionKeyUri,
+ hasResources: requiredResources.length > 0,
+ }
+ );
+ } catch (error) {
+ throw new Error(
+ `Invalid delegation signature: ${
+ error instanceof Error ? error.message : 'Unknown error'
+ }`
+ );
+ }
+}
+
+/**
+ * Creates a PKP auth context from pre-generated session materials.
+ * This is a streamlined API for server-side scenarios where session materials
+ * are generated once and reused across multiple requests.
+ */
+export async function getPkpAuthContextFromPreGeneratedAdapter(
+ upstreamParams: AuthManagerParams,
+ params: {
+ pkpPublicKey: z.infer;
+ sessionKeyPair: SessionKeyPair;
+ delegationAuthSig: AuthSig;
+ authData?: AuthData;
+ }
+) {
+ _logger.info(
+ 'getPkpAuthContextFromPreGeneratedAdapter: Creating PKP auth context from pre-generated materials',
+ {
+ pkpPublicKey: params.pkpPublicKey,
+ hasSessionKeyPair: !!params.sessionKeyPair,
+ hasDelegationAuthSig: !!params.delegationAuthSig,
+ hasAuthData: !!params.authData,
+ }
+ );
+
+ // Extract auth config from delegation signature
+ const extractedAuthConfig = extractAuthConfigFromDelegationAuthSig(
+ params.delegationAuthSig
+ );
+
+ // Create auth config using extracted information with sensible defaults
+ const authConfig = {
+ domain: extractedAuthConfig.domain || 'localhost',
+ resources:
+ extractedAuthConfig.resources || processResources([['pkp-signing', '*']]),
+ capabilityAuthSigs: [],
+ expiration:
+ extractedAuthConfig.expiration ||
+ new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
+ statement: extractedAuthConfig.statement || '',
+ };
+
+ // Generate sessionKeyUri from the public key
+ const sessionKeyUri = SessionKeyUriSchema.parse(
+ 'lit:session:' + params.sessionKeyPair.publicKey
+ );
+
+ // Validate the delegation signature
+ validateDelegationAuthSig(
+ params.delegationAuthSig,
+ authConfig.resources,
+ sessionKeyUri
+ );
+
+ // Return auth context using pre-generated materials
+ return {
+ chain: 'ethereum',
+ pkpPublicKey: params.pkpPublicKey,
+ authData:
+ params.authData ||
+ ({
+ // Provide minimal auth data if not provided
+ authMethodType: 1, // Default auth method type
+ } as AuthData),
+ authConfig,
+ sessionKeyPair: params.sessionKeyPair,
+ // Provide the pre-generated delegation signature
+ authNeededCallback: async () => {
+ _logger.debug(
+ 'getPkpAuthContextFromPreGeneratedAdapter: Returning pre-generated delegation signature'
+ );
+ return params.delegationAuthSig;
+ },
+ };
+}
diff --git a/packages/auth/src/lib/AuthManager/authContexts/getPkpAuthContext.ts b/packages/auth/src/lib/AuthManager/authContexts/getPkpAuthContext.ts
index 88fc7c780..74ca3b1f6 100644
--- a/packages/auth/src/lib/AuthManager/authContexts/getPkpAuthContext.ts
+++ b/packages/auth/src/lib/AuthManager/authContexts/getPkpAuthContext.ts
@@ -49,6 +49,9 @@ export const GetPkpAuthContextSchema = z.object({
// @depreacted - to be removed. testing only.
pkpAddress: z.string(),
+
+ // Optional pre-generated delegation signature
+ preGeneratedDelegationAuthSig: z.any().optional(),
}),
cache: z
.object({
@@ -143,20 +146,24 @@ export const getPkpAuthContext = async (
resources: _params.authConfig.resources,
};
- const delegationAuthSig = await tryGetCachedDelegationAuthSig({
- cache: _params.cache?.delegationAuthSig,
- storage: _params.deps.storage,
- address: _params.deps.pkpAddress,
- expiration: _params.authConfig.expiration,
- signSessionKey: () =>
- _params.deps.signSessionKey({
- requestBody,
- nodeUrls: _nodeInfo.urls,
- }),
- });
+ // Use pre-generated delegation signature if provided, otherwise generate/fetch one
+ const delegationAuthSig = _params.deps.preGeneratedDelegationAuthSig
+ ? _params.deps.preGeneratedDelegationAuthSig
+ : await tryGetCachedDelegationAuthSig({
+ cache: _params.cache?.delegationAuthSig,
+ storage: _params.deps.storage,
+ address: _params.deps.pkpAddress,
+ expiration: _params.authConfig.expiration,
+ signSessionKey: () =>
+ _params.deps.signSessionKey({
+ requestBody,
+ nodeUrls: _nodeInfo.urls,
+ }),
+ });
_logger.info('getPkpAuthContext: delegationAuthSig', {
delegationAuthSig,
+ isPreGenerated: !!_params.deps.preGeneratedDelegationAuthSig,
});
return {