diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1a9a2627a3..f8dec6bedb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,7 +103,7 @@ jobs: run: cp .env.ci .env - name: Run End to End Tests if: steps.build.outputs.exit_code == 0 - run: yarn test:local --filter=testUseEoaSessionSigsToExecuteJsSigning,testUseEoaSessionSigsToPkpSign,testUsePkpSessionSigsToExecuteJsSigning,testUsePkpSessionSigsToPkpSign,testUseValidLitActionCodeGeneratedSessionSigsToPkpSign,testUseValidLitActionCodeGeneratedSessionSigsToExecuteJsSigning,testDelegatingCapacityCreditsNFTToAnotherWalletToExecuteJs,testEthAuthSigToEncryptDecryptString,testExecuteJsSignAndCombineEcdsa,testExecutJsDecryptAndCombine,testExecuteJsBroadcastAndCollect --exclude=Parallel + run: yarn test:local --filter=testUseEoaSessionSigsToExecuteJsSigning,testUseEoaAuthContextToPkpSign,testUseEoaSessionSigsToPkpSign,testUsePkpSessionSigsToExecuteJsSigning,testUsePkpSessionSigsToPkpSign,testUseValidLitActionCodeGeneratedSessionSigsToPkpSign,testUseValidLitActionCodeGeneratedSessionSigsToExecuteJsSigning,testDelegatingCapacityCreditsNFTToAnotherWalletToExecuteJs,testEthAuthSigToEncryptDecryptString,testExecuteJsSignAndCombineEcdsa,testExecutJsDecryptAndCombine,testExecuteJsBroadcastAndCollect --exclude=Parallel - name: Get Container Logs if: always() run: docker logs shiva diff --git a/local-tests/test.ts b/local-tests/test.ts index f4154aa420..7a55853d93 100644 --- a/local-tests/test.ts +++ b/local-tests/test.ts @@ -2,6 +2,7 @@ import { TinnyEnvironment } from './setup/tinny-environment'; import { runInBand, runTestsParallel } from './setup/tinny-operations'; // import { testBundleSpeed } from './tests/test-bundle-speed'; // import { testExample } from './tests/test-example'; +import { testUseEoaAuthContextToPkpSign } from 'local-tests/tests/testUseEoaAuthContextToPkpSign'; import { testUseEoaSessionSigsToExecuteJsSigning } from './tests/testUseEoaSessionSigsToExecuteJsSigning'; import { testUseEoaSessionSigsToPkpSign } from './tests/testUseEoaSessionSigsToPkpSign'; import { testUsePkpSessionSigsToExecuteJsSigning } from './tests/testUsePkpSessionSigsToExecuteJsSigning'; @@ -163,6 +164,7 @@ setLitActionsCodeToLocal(); }; const eoaSessionSigsTests = { + testUseEoaAuthContextToPkpSign, testUseEoaSessionSigsToExecuteJsSigning, testUseEoaSessionSigsToPkpSign, testUseEoaSessionSigsToExecuteJsSigningInParallel, diff --git a/local-tests/tests/testPkpEthersWithEoaSessionSigsToSignWithAuthContext.ts b/local-tests/tests/testPkpEthersWithEoaSessionSigsToSignWithAuthContext.ts index aca18c4d54..4f817fa828 100644 --- a/local-tests/tests/testPkpEthersWithEoaSessionSigsToSignWithAuthContext.ts +++ b/local-tests/tests/testPkpEthersWithEoaSessionSigsToSignWithAuthContext.ts @@ -24,7 +24,6 @@ export const testPkpEthersWithEoaSessionSigsToSignWithAuthContext = async ( pkpPubKey: alice.pkp.publicKey, litNodeClient: devEnv.litNodeClient, authContext: { - client: devEnv.litNodeClient, getSessionSigsProps: { authNeededCallback: async function ( params: AuthCallbackParams diff --git a/local-tests/tests/testUseEoaAuthContextToPkpSign.ts b/local-tests/tests/testUseEoaAuthContextToPkpSign.ts new file mode 100644 index 0000000000..f2e16f47cc --- /dev/null +++ b/local-tests/tests/testUseEoaAuthContextToPkpSign.ts @@ -0,0 +1,129 @@ +import { ethers } from 'ethers'; + +import { + createSiweMessageWithRecaps, + generateAuthSig, + LitActionResource, + LitPKPResource, +} from '@lit-protocol/auth-helpers'; +import { LIT_ABILITY } from '@lit-protocol/constants'; +import { log } from '@lit-protocol/misc'; +import { AuthCallbackParams, AuthSig } from '@lit-protocol/types'; +import { TinnyEnvironment } from 'local-tests/setup/tinny-environment'; + +/** + * Test Commands: + * ✅ NETWORK=datil-dev yarn test:local --filter=testUseEoaAuthContextToPkpSign + * ✅ NETWORK=datil-test yarn test:local --filter=testUseEoaAuthContextToPkpSign + * ✅ NETWORK=custom yarn test:local --filter=testUseEoaAuthContextToPkpSign + */ +export const testUseEoaAuthContextToPkpSign = async ( + devEnv: TinnyEnvironment +) => { + const alice = await devEnv.createRandomPerson(); + + const ONE_MINUTE = 1 * 60 * 1000; + const expiration = new Date(Date.now() + ONE_MINUTE).toISOString(); + const resourceAbilityRequests = [ + { + resource: new LitPKPResource('*'), + ability: LIT_ABILITY.PKPSigning, + }, + { + resource: new LitActionResource('*'), + ability: LIT_ABILITY.LitActionExecution, + }, + ]; + const litNodeClient = alice.envConfig.litNodeClient; + + litNodeClient.setAuthContext({ + getSessionSigsProps: { + chain: 'ethereum', + resourceAbilityRequests, + expiration, + authNeededCallback: async function ( + params: AuthCallbackParams + ): Promise { + const toSign = await createSiweMessageWithRecaps({ + uri: params.uri, + expiration: params.expiration, + resources: params.resourceAbilityRequests, + walletAddress: alice.wallet.address, + nonce: await litNodeClient.getLatestBlockhash(), + litNodeClient: devEnv.litNodeClient, + }); + + const authSig = await generateAuthSig({ + signer: alice.wallet, + toSign, + }); + + return authSig; + }, + }, + }); + + const runWithAuthContext = await litNodeClient.pkpSign({ + toSign: alice.loveLetter, + pubKey: alice.pkp.publicKey, + }); + + devEnv.releasePrivateKeyFromUser(alice); + + // Expected output: + // { + // r: "25fc0d2fecde8ed801e9fee5ad26f2cf61d82e6f45c8ad1ad1e4798d3b747fd9", + // s: "549fe745b4a09536e6e7108d814cf7e44b93f1d73c41931b8d57d1b101833214", + // recid: 1, + // signature: "0x25fc0d2fecde8ed801e9fee5ad26f2cf61d82e6f45c8ad1ad1e4798d3b747fd9549fe745b4a09536e6e7108d814cf7e44b93f1d73c41931b8d57d1b1018332141c", + // publicKey: "04A3CD53CCF63597D3FFCD1DF1E8236F642C7DF8196F532C8104625635DC55A1EE59ABD2959077432FF635DF2CED36CC153050902B71291C4D4867E7DAAF964049", + // dataSigned: "7D87C5EA75F7378BB701E404C50639161AF3EFF66293E9F375B5F17EB50476F4", + // } + + // -- assertions + // r, s, dataSigned, and public key should be present + if (!runWithAuthContext.r) { + throw new Error(`Expected "r" in runWithAuthContext`); + } + if (!runWithAuthContext.s) { + throw new Error(`Expected "s" in runWithAuthContext`); + } + if (!runWithAuthContext.dataSigned) { + throw new Error(`Expected "dataSigned" in runWithAuthContext`); + } + if (!runWithAuthContext.publicKey) { + throw new Error(`Expected "publicKey" in runWithAuthContext`); + } + + // signature must start with 0x + if (!runWithAuthContext.signature.startsWith('0x')) { + throw new Error(`Expected "signature" to start with 0x`); + } + + // recid must be parseable as a number + if (isNaN(runWithAuthContext.recid)) { + throw new Error(`Expected "recid" to be parseable as a number`); + } + + const signature = ethers.utils.joinSignature({ + r: '0x' + runWithAuthContext.r, + s: '0x' + runWithAuthContext.s, + recoveryParam: runWithAuthContext.recid, + }); + const recoveredPubKey = ethers.utils.recoverPublicKey( + alice.loveLetter, + signature + ); + if (recoveredPubKey !== `0x${runWithAuthContext.publicKey.toLowerCase()}`) { + throw new Error( + `Expected recovered public key to match runWithAuthContext.publicKey` + ); + } + if (recoveredPubKey !== `0x${alice.pkp.publicKey.toLowerCase()}`) { + throw new Error( + `Expected recovered public key to match alice.pkp.publicKey` + ); + } + + log('✅ testUseEoaAuthContextToPkpSign'); +}; diff --git a/packages/core/src/lib/lit-core.ts b/packages/core/src/lib/lit-core.ts index 2c23752e09..4a62769059 100644 --- a/packages/core/src/lib/lit-core.ts +++ b/packages/core/src/lib/lit-core.ts @@ -560,6 +560,13 @@ export class LitCore { } } + protected async assertConnected() { + // -- if it's not ready yet, then connect + if (!this.ready) { + await this.connect(); + } + } + private async _handshakeAndVerifyNodeAttestation({ url, requestId, diff --git a/packages/lit-node-client-nodejs/src/lib/lit-node-client-nodejs.ts b/packages/lit-node-client-nodejs/src/lib/lit-node-client-nodejs.ts index d07d82b9f0..ec97da2998 100644 --- a/packages/lit-node-client-nodejs/src/lib/lit-node-client-nodejs.ts +++ b/packages/lit-node-client-nodejs/src/lib/lit-node-client-nodejs.ts @@ -30,6 +30,7 @@ import { LitNodeClientNotReadyError, ParamNullError, ParamsMissingError, + UnauthorizedException, UnknownError, UnsupportedMethodError, WalletSignatureNotFoundError, @@ -63,7 +64,11 @@ import { setStorageItem, } from '@lit-protocol/misc-browser'; import { nacl } from '@lit-protocol/nacl'; -import { ILitResource, ISessionCapabilityObject } from '@lit-protocol/types'; +import { + AuthenticationProps, + ILitResource, + ISessionCapabilityObject, +} from '@lit-protocol/types'; import { uint8arrayFromString, uint8arrayToString, @@ -139,6 +144,7 @@ export class LitNodeClientNodeJs implements LitClientSessionManager, ILitNodeClient { defaultAuthCallback?: (authSigParams: AuthCallbackParams) => Promise; + authContext?: AuthenticationProps; // ========== Constructor ========== constructor(args: LitNodeClientConfig | CustomNetwork) { @@ -151,6 +157,30 @@ export class LitNodeClientNodeJs if (args !== undefined && args !== null && 'defaultAuthCallback' in args) { this.defaultAuthCallback = args.defaultAuthCallback; } + + this.setAuthContext(args.authContext); + } + + public setAuthContext(authContext?: AuthenticationProps) { + this.authContext = authContext; + } + + public async getAuthContextSessionSigs( + getSessionSigsPropsOverride?: Partial + ): Promise { + if (!this.authContext) { + throw new UnauthorizedException( + { + info: {}, + }, + 'authContext is not set. Cannot authenticate using it' + ); + } + + return this.getSessionSigs({ + ...this.authContext?.getSessionSigsProps, + ...(getSessionSigsPropsOverride || {}), + }); } // ========== Rate Limit NFT ========== @@ -184,10 +214,7 @@ export class LitNodeClientNodeJs await params.dAppOwnerWallet.getAddress() ); - // -- if it's not ready yet, then connect - if (!this.ready) { - await this.connect(); - } + await this.assertConnected(); const siweMessage = await createSiweMessageWithCapacityDelegation({ uri: 'lit:capability:delegation', @@ -270,9 +297,10 @@ export class LitNodeClientNodeJs * @param obj - The object to check. * @returns True if the object is of type SessionKeyPair. */ - isSessionKeyPair(obj: any): obj is SessionKeyPair { + isSessionKeyPair(obj: unknown): obj is SessionKeyPair { return ( typeof obj === 'object' && + obj !== null && 'publicKey' in obj && 'secretKey' in obj && typeof obj.publicKey === 'string' && @@ -509,7 +537,7 @@ export class LitNodeClientNodeJs resourceAbilityRequests, }: { authSig: AuthSig; - sessionKeyUri: any; + sessionKeyUri: string; resourceAbilityRequests: LitResourceAbilityRequest[]; }): Promise => { const authSigSiweMessage = new SiweMessage(authSig.signedMessage); @@ -727,10 +755,15 @@ export class LitNodeClientNodeJs ); } + const sessionSigs = await this._getValidSessionSigs( + params.sessionSigs, + params.getSessionSigsPropsOverride + ); + // determine which node to run on const ipfsId = await this.getIpfsId({ dataToHash: params.code!, - sessionSigs: params.sessionSigs, + sessionSigs, }); // select targetNodeRange number of random index of the bootstrapUrls.length @@ -781,7 +814,7 @@ export class LitNodeClientNodeJs // -- choose the right signature const sessionSig = this.getSessionSigByUrl({ - sessionSigs: params.sessionSigs, + sessionSigs, url, }); @@ -859,15 +892,27 @@ export class LitNodeClientNodeJs formattedParams: JsonExecutionSdkParams, requestId: string ) { + if (!formattedParams.sessionSigs) { + throw new UnauthorizedException( + { + info: { + url, + requestId, + }, + }, + `sessionSigs missing on execute js request` + ); + } + // -- choose the right signature - const sessionSig = this.getSessionSigByUrl({ + const authSig = this.getSessionSigByUrl({ sessionSigs: formattedParams.sessionSigs, url, }); const reqBody: JsonExecutionRequest = { ...formattedParams, - authSig: sessionSig, + authSig, }; const urlWithPath = composeLitUrl({ @@ -889,13 +934,7 @@ export class LitNodeClientNodeJs executeJs = async ( params: JsonExecutionSdkParams ): Promise => { - // ========== Validate Params ========== - if (!this.ready) { - const message = - '[executeJs] LitNodeClient is not ready. Please call await litNodeClient.connect() first.'; - - throw new LitNodeClientNotReadyError({}, message); - } + await this.assertConnected(); const paramsIsSafe = safeParams({ functionName: 'executeJs', @@ -913,38 +952,35 @@ export class LitNodeClientNodeJs ); } - // validate session sigs - const checkedSessionSigs = validateSessionSigs(params.sessionSigs); - - if (checkedSessionSigs.isValid === false) { - throw new InvalidSessionSigs( - {}, - `Invalid sessionSigs. Errors: ${checkedSessionSigs.errors}` - ); - } + // -- get sessionSigs + const sessionSigs = await this._getValidSessionSigs( + params.sessionSigs, + params.getSessionSigsPropsOverride + ); // Format the params let formattedParams: JsonExecutionSdkParams = { ...params, ...(params.jsParams && { jsParams: normalizeJsParams(params.jsParams) }), ...(params.code && { code: encodeCode(params.code) }), + sessionSigs, }; // Check if IPFS options are provided and if the code should be fetched from IPFS and overwrite the current code. // This will fetch the code from the specified IPFS gateway using the provided ipfsId, - // and update the params with the fetched code, removing the ipfsId afterward. + // and update the formattedParams with the fetched code, removing the ipfsId afterward. const overwriteCode = params.ipfsOptions?.overwriteCode || GLOBAL_OVERWRITE_IPFS_CODE_BY_NETWORK[this.config.litNetwork]; - if (overwriteCode && params.ipfsId) { + if (overwriteCode && formattedParams.ipfsId) { const code = await this._getFallbackIpfsCode( - params.ipfsOptions?.gatewayUrl, - params.ipfsId + formattedParams.ipfsOptions?.gatewayUrl, + formattedParams.ipfsId ); formattedParams = { - ...params, + ...formattedParams, code: code, ipfsId: undefined, }; @@ -954,7 +990,7 @@ export class LitNodeClientNodeJs // ========== Get Node Promises ========== // Handle promises for commands sent to Lit nodes const getNodePromises = async () => { - if (params.useSingleNode) { + if (formattedParams.useSingleNode) { return this.getRandomNodePromise((url: string) => this.executeJsNodeRequest(url, formattedParams, requestId) ); @@ -970,7 +1006,7 @@ export class LitNodeClientNodeJs const res = await this.handleNodePromises( nodePromises, requestId, - params.useSingleNode ? 1 : this.connectedNodes.size + formattedParams.useSingleNode ? 1 : this.connectedNodes.size ); // -- case: promises rejected @@ -1090,13 +1126,14 @@ export class LitNodeClientNodeJs * @param { JsonPkpSignSdkParams } params * @param params.toSign - The data to sign * @param params.pubKey - The public key to sign with - * @param params.sessionSigs - The session signatures to use - * @param params.authMethods - (optional) The auth methods to use + * @param [params.sessionSigs] - The session signatures to use + * @param [params.getSessionSigsPropsOverride] - The props override when obtaining sessionSigs from the auth context */ pkpSign = async (params: JsonPkpSignSdkParams): Promise => { + await this.assertConnected(); + // -- validate required params const requiredParamKeys = ['toSign', 'pubKey']; - (requiredParamKeys as (keyof JsonPkpSignSdkParams)[]).forEach((key) => { if (!params[key]) { throw new ParamNullError( @@ -1112,53 +1149,26 @@ export class LitNodeClientNodeJs } }); - // -- validate present of accepted auth methods - if ( - !params.sessionSigs && - (!params.authMethods || params.authMethods.length <= 0) - ) { - throw new ParamNullError( - { - info: { - params, - }, - }, - 'Either sessionSigs or authMethods (length > 0) must be present.' - ); - } - - const requestId = this._getNewRequestId(); - - // validate session sigs - const checkedSessionSigs = validateSessionSigs(params.sessionSigs); - - if (checkedSessionSigs.isValid === false) { - throw new InvalidSessionSigs( - {}, - `Invalid sessionSigs. Errors: ${checkedSessionSigs.errors}` - ); - } - // ========== Get Node Promises ========== + // -- get sessionSigs + const sessionSigs = await this._getValidSessionSigs( + params.sessionSigs, + params.getSessionSigsPropsOverride + ); // Handle promises for commands sent to Lit nodes + const requestId = this._getNewRequestId(); const nodePromises = this.getNodePromises((url: string) => { // -- get the session sig from the url key - const sessionSig = this.getSessionSigByUrl({ - sessionSigs: params.sessionSigs, + const authSig = this.getSessionSigByUrl({ + sessionSigs, url, }); const reqBody: JsonPkpSignRequest = { toSign: normalizeArray(params.toSign), pubkey: hexPrefixed(params.pubKey), - authSig: sessionSig, - - // -- optional params - ...(params.authMethods && - params.authMethods.length > 0 && { - authMethods: params.authMethods, - }), + authSig, }; logWithRequestId(requestId, 'reqBody:', reqBody); @@ -1225,15 +1235,9 @@ export class LitNodeClientNodeJs * @throws { Error } if the subnetPubKey is null */ encrypt = async (params: EncryptSdkParams): Promise => { - // ========== Validate Params ========== - // -- validate if it's ready - if (!this.ready) { - throw new LitNodeClientNotReadyError( - {}, - '6 LitNodeClient is not ready. Please call await litNodeClient.connect() first.' - ); - } + await this.assertConnected(); + // ========== Validate Params ========== // -- validate if this.subnetPubKey is null if (!this.subnetPubKey) { throw new LitNodeClientNotReadyError({}, 'subnetPubKey cannot be null'); @@ -1315,15 +1319,9 @@ export class LitNodeClientNodeJs const { sessionSigs, authSig, chain, ciphertext, dataToEncryptHash } = params; - // ========== Validate Params ========== - // -- validate if it's ready - if (!this.ready) { - throw new LitNodeClientNotReadyError( - {}, - '6 LitNodeClient is not ready. Please call await litNodeClient.connect() first.' - ); - } + await this.assertConnected(); + // ========== Validate Params ========== // -- validate if this.subnetPubKey is null if (!this.subnetPubKey) { throw new LitNodeClientNotReadyError({}, 'subnetPubKey cannot be null'); @@ -1520,15 +1518,9 @@ export class LitNodeClientNodeJs ): Promise => { log(`[signSessionKey] params:`, params); - // ========== Validate Params ========== - // -- validate: If it's NOT ready - if (!this.ready) { - throw new LitNodeClientNotReadyError( - {}, - '[signSessionKey] ]LitNodeClient is not ready. Please call await litNodeClient.connect() first.' - ); - } + await this.assertConnected(); + // ========== Validate Params ========== // -- construct SIWE message that will be signed by node to generate an authSig. const _expiration = params.expiration || @@ -1666,7 +1658,7 @@ export class LitNodeClientNodeJs // ========== Extract shares from response data ========== // -- 1. combine signed data as a list, and get the signatures from it - let curveType = responseData[0]?.curveType; + const curveType = responseData[0]?.curveType; if (curveType === 'ECDSA') { throw new Error( @@ -2025,6 +2017,41 @@ export class LitNodeClientNodeJs return signatures; }; + private _getValidSessionSigs = async ( + providedSessionSigs?: SessionSigsMap, + getSessionSigsPropsOverride?: Partial + ) => { + const sessionSigs = + providedSessionSigs || + (await this.getAuthContextSessionSigs(getSessionSigsPropsOverride)); + + if (!sessionSigs) { + throw new ParamNullError( + { + info: { + providedSessionSigs, + authContext: this.authContext, + }, + }, + 'Could not get sessionSigs. Must provide them or set an authContext on construction or with setAuthContext' + ); + } + const checkedSessionSigs = validateSessionSigs(sessionSigs); + if (!checkedSessionSigs.isValid) { + throw new InvalidSessionSigs( + { + info: { + sessionSigsValidation: checkedSessionSigs.isValid, + sessionSigsErrors: checkedSessionSigs.errors, + }, + }, + `Invalid sessionSigs. Errors: ${checkedSessionSigs.errors}` + ); + } + + return sessionSigs; + }; + /** * Retrieves the PKP sessionSigs. * @@ -2194,11 +2221,7 @@ export class LitNodeClientNodeJs async claimKeyId( params: ClaimRequest ): Promise { - if (!this.ready) { - const message = - 'LitNodeClient is not ready. Please call await litNodeClient.connect() first.'; - throw new LitNodeClientNotReadyError({}, message); - } + await this.assertConnected(); if (params.authMethod.authMethodType == AUTH_METHOD_TYPE.WebAuthn) { throw new LitNodeClientNotReadyError( diff --git a/packages/types/src/lib/interfaces.ts b/packages/types/src/lib/interfaces.ts index c003e0a083..96605748d5 100644 --- a/packages/types/src/lib/interfaces.ts +++ b/packages/types/src/lib/interfaces.ts @@ -186,14 +186,18 @@ export interface LitNodeClientConfig { contractContext?: LitContractContext | LitContractResolverContext; storageProvider?: StorageProvider; defaultAuthCallback?: (authSigParams: AuthCallbackParams) => Promise; + authContext?: AuthenticationProps; rpcUrl?: string; } export type CustomNetwork = Pick< LitNodeClientConfig, - 'litNetwork' | 'contractContext' | 'checkNodeAttestation' -> & - Partial>; + | 'litNetwork' + | 'contractContext' + | 'checkNodeAttestation' + | 'authContext' + | 'minNodeCount' +>; /** * Override for LocalStorage and SessionStorage @@ -233,17 +237,17 @@ pub struct JsonExecutionRequest { */ export interface BaseJsonPkpSignRequest { - authMethods?: AuthMethod[]; toSign: ArrayLike; } /** * The 'pkpSign' function param. Please note that the structure - * is different than the payload sent to the node. + * is different from the payload sent to the node. */ export interface JsonPkpSignSdkParams extends BaseJsonPkpSignRequest { pubKey: string; - sessionSigs: SessionSigsMap; + sessionSigs?: SessionSigsMap; + getSessionSigsPropsOverride?: Partial; } /** @@ -475,12 +479,12 @@ export interface JsonExecutionSdkParams /** * the session signatures to use to authorize the user with the nodes */ - sessionSigs: SessionSigsMap; + sessionSigs?: SessionSigsMap; /** - * auth methods to resolve + * the lit node client auth context props to get session sigs override */ - authMethods?: AuthMethod[]; + getSessionSigsPropsOverride?: Partial; } export interface ExecuteJsAdvancedOptions { @@ -1046,7 +1050,7 @@ export interface CommonGetSessionSigsProps { /** * When this session signature will expire. After this time is up you will need to reauthenticate, generating a new session signature. The default time until expiration is 24 hours. The formatting is an [RFC3339](https://datatracker.ietf.org/doc/html/rfc3339) timestamp. */ - expiration?: any; + expiration?: string; /** * The chain to use for the session signature and sign the session key. This value is almost always `ethereum`. If you're using EVM, this parameter isn't very important.