diff --git a/e2e-nodejs/group-lit-actions/test-lit-action-claim-key.mjs b/e2e-nodejs/group-lit-actions/test-lit-action-claim-key.mjs index 3a5f04ef92..b9745fc86d 100644 --- a/e2e-nodejs/group-lit-actions/test-lit-action-claim-key.mjs +++ b/e2e-nodejs/group-lit-actions/test-lit-action-claim-key.mjs @@ -16,18 +16,10 @@ export async function main() { }); // ==================== Post-Validation ==================== - if (!res.success) { + if (!res.success && !res.claims) { return fail(`response should be success`); } - if (Object.keys(res.signedData).length > 0) { - return fail(`signedData should be empty`); - } - - if (Object.keys(res.decryptedData).length > 0) { - return fail(`decryptedData should be empty`); - } - // -- should have claimData if ( res.claims === undefined || @@ -46,9 +38,9 @@ export async function main() { return fail(`claimData.foo should have ${key}`); } }); - + const key = 'foo'; for (let i = 0; i < res.claims[key].signatures.length; i++) { - if (!res.claims[key].signatures[i].r || !res.claims[key].signatures[i].s || res.claims[key].signatures[i].v) { + if (!res.claims[key].signatures[i].r || !res.claims[key].signatures[i].s || !res.claims[key].signatures[i].v) { return fail(`signature data misformed, should be of ethers signature format`); } } diff --git a/e2e-nodejs/group-lit-actions/test-lit-action-grouped-claims.mjs b/e2e-nodejs/group-lit-actions/test-lit-action-grouped-claims.mjs index 6bf4da3bdf..07cbc01d75 100644 --- a/e2e-nodejs/group-lit-actions/test-lit-action-grouped-claims.mjs +++ b/e2e-nodejs/group-lit-actions/test-lit-action-grouped-claims.mjs @@ -17,18 +17,10 @@ export async function main() { }); // ==================== Post-Validation ==================== - if (!res.success) { + if (!res.success && !res.claims) { return fail(`response should be success`); } - if (Object.keys(res.signedData).length > 0) { - return fail(`signedData should be empty`); - } - - if (Object.keys(res.decryptedData).length > 0) { - return fail(`decryptedData should be empty`); - } - // -- should have claims Object.entries(res.claims).forEach(([key, value]) => { if (key !== 'foo' || key !== 'bar') { diff --git a/e2e-nodejs/group-pkp-auth-method/test-pkp-auth-method-authentication.mjs b/e2e-nodejs/group-pkp-auth-method/test-pkp-auth-method-authentication.mjs new file mode 100644 index 0000000000..aab344d1e8 --- /dev/null +++ b/e2e-nodejs/group-pkp-auth-method/test-pkp-auth-method-authentication.mjs @@ -0,0 +1,108 @@ +import path from 'path'; +import { success, fail, testThis } from '../../tools/scripts/utils.mjs'; +import LITCONFIG from '../../lit.config.json' assert { type: 'json' }; +import { client } from '../00-setup.mjs'; +import { LitAbility, LitActionResource } from '@lit-protocol/auth-helpers'; +import { LitAuthClient } from '@lit-protocol/lit-auth-client'; +import { AuthMethodType, ProviderType } from '@lit-protocol/constants'; +import { ethers } from 'ethers'; +import { PKPEthersWallet } from '@lit-protocol/pkp-ethers'; + +// NOTE: you need to hash data before you send it in. +// If you send something that isn't 32 bytes, the nodes will return an error. +const TO_SIGN = new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode("meow"))); + +export async function main() { + // ==================== Setup ==================== + + const litAuthClient = new LitAuthClient({ + litRelayConfig: { + relayApiKey: '67e55044-10b1-426f-9247-bb680e5fe0c8_relayer', + }, + version: 'V3', + litNodeClient: client, + }); + + // -- eth wallet + const authProvider = litAuthClient.initProvider(ProviderType.EthWallet); + const authMethod = { + authMethodType: AuthMethodType.EthWallet, + accessToken: JSON.stringify(LITCONFIG.CONTROLLER_AUTHSIG), + }; + + let pkps = await authProvider.fetchPKPsThroughRelayer(authMethod); + + if (pkps.length <= 0) { + try { + await authProvider.mintPKPThroughRelayer(authMethod); + } catch (e) { + return fail('Failed to mint PKP'); + } + pkps = await authProvider.fetchPKPsThroughRelayer(authMethod); + } + + const pkp = pkps[pkps.length - 1]; + + // convert BigNumber to string + pkp.tokenId = ethers.BigNumber.from(pkp.tokenId).toString(); + + const pkpPubKey = pkp.publicKey; + + const pkpSignRes = await client?.executeJs({ + authSig: LITCONFIG.CONTROLLER_AUTHSIG, + authMethods: [authMethod], // This is not workingu! + code: `(async () => { + const sigShare = await LitActions.signEcdsa({ + toSign, + publicKey, + sigName: "sig", + }); + LitActions.setResponse({response: JSON.stringify(Lit.Auth)}); + })();`, + jsParams: { + toSign: TO_SIGN, + publicKey: LITCONFIG.PKP_PUBKEY, + }, + }); + + // ==================== Post-Validation ==================== + + if (!pkpSignRes) { + return fail( + 'Failed to sign data with sessionSigs generated by eth wallet auth method' + ); + } + + let missingKeys = []; + + if (pkpSignRes) { + ['r', 's', 'recid', 'signature', 'publicKey', 'dataSigned'].forEach( + (key) => { + if (pkpSignRes.signatures['sig'][key] === undefined) { + missingKeys.push(key); + } + } + ); + } + + if (missingKeys.length > 0) { + return fail(`Missing keys: ${missingKeys.join(', ')}`); + } + + if (!pkpSignRes.response.authSigAddress == LITCONFIG.CONTROLLER_AUTHSIG.address) { + return fail(`Missing or non matching auth sig address in Lit.Auth context`); + } + + if(pkpSignRes.response.authMethodContexts.length < 1) { + return fail('not enough authentication material in Lit.Auth context'); + } + + // ==================== Success ==================== + return success( + `it should use sessionSigs generated by eth wallet auth method to sign data. Signature is ${signature} and pkpSignRes is ${JSON.stringify( + pkpSignRes + )}` + ); +} + +await testThis({ name: path.basename(import.meta.url), fn: main }); \ No newline at end of file diff --git a/packages/core/src/lib/lit-core.ts b/packages/core/src/lib/lit-core.ts index c478c13793..cfd81e4453 100644 --- a/packages/core/src/lib/lit-core.ts +++ b/packages/core/src/lib/lit-core.ts @@ -29,11 +29,14 @@ import { throwError, } from '@lit-protocol/misc'; import { + AuthMethod, AuthSig, CustomNetwork, FormattedMultipleAccs, HandshakeWithSgx, + JsonExecutionRequest, JsonHandshakeResponse, + JsonPkpSignRequest, KV, LitNodeClientConfig, MultipleAccessControlConditions, @@ -350,22 +353,24 @@ export class LitCore { * Get either auth sig or session auth sig * */ - getAuthSigOrSessionAuthSig = ({ + getAuthMaterial = ({ + authMethods, authSig, sessionSigs, url, mustHave = true, }: { + authMethods?: Array, authSig?: AuthSig; sessionSigs?: SessionSigsMap; url: string; mustHave?: boolean; - }): AuthSig | SessionSig => { + }): AuthSig | SessionSig | Object[] => { - if (!authSig && !sessionSigs) { + if ([authMethods, authSig, sessionSigs].filter((val, index, arr) => val !== undefined).length < 1) { if (mustHave) { throwError({ - message: `You must pass either authSig or sessionSigs`, + message: `You must pass either authSig, sessionSigs or authMethods`, errorKind: LIT_ERROR.INVALID_ARGUMENT_EXCEPTION.kind, errorCode: LIT_ERROR.INVALID_ARGUMENT_EXCEPTION.name, }); @@ -388,9 +393,28 @@ export class LitCore { return sigToPassToNode; } + if (authMethods) { + return authMethods; + } + return authSig!; }; + /* + Here we do a check on the 'length' property of the object + returned to see if it is an array type + we cast to the array type to check as the union of types does not overlap + */ + setAuthMaterial(reqBody: T, authMaterial: AuthSig | SessionSig | Object[]): T { + if (!(authMaterial as Object[]).length){ + reqBody.authSig = (authMaterial as AuthSig); + } else { + reqBody.authMethods = (authMaterial as Object[]); + } + + return reqBody; + } + /** * * Get hash of access control conditions 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 a28137c519..df802bebae 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 @@ -154,6 +154,7 @@ export class LitNodeClientNodeJs extends LitCore { const reqBody: JsonExecutionRequest = { ...(params.authSig && { authSig: params.authSig }), ...(params.sessionSigs && { sessionSigs: params.sessionSigs }), + ...(params.authMethods && {authMethods: params.authMethods}), jsParams: convertLitActionsParams(params.jsParams), // singleNode: params.singleNode ?? false, targetNodeRange: params.targetNodeRange ?? 0, @@ -543,16 +544,16 @@ export class LitNodeClientNodeJs extends LitCore { // -- execute const urlWithPath = `${url}/web/execute`; - if (!authSig) { - throw new Error('authSig is required'); + if (!authSig && !authMethods) { + throw new Error('authSig or authMethods are required'); } - - const data: JsonExecutionRequest = { - code, - ipfsId, - authSig, - jsParams, - authMethods, + let data: JsonExecutionRequest = { + authSig, + code, + ipfsId, + jsParams, + authMethods, + sessionSigs }; return await this.sendCommandToNode({ url: urlWithPath, data, requestId }); @@ -757,7 +758,7 @@ export class LitNodeClientNodeJs extends LitCore { ): Promise< SuccessNodePromises | RejectedNodePromises > => { - const { code, authSig, jsParams, debug, sessionSigs, targetNodeRange } = + const { code, authMethods, authSig, jsParams, debug, sessionSigs, targetNodeRange } = params; log('running runOnTargetedNodes:', targetNodeRange); @@ -863,13 +864,14 @@ export class LitNodeClientNodeJs extends LitCore { this.getLitActionRequestBody(params); // -- choose the right signature - const sigToPassToNode = this.getAuthSigOrSessionAuthSig({ + const sigToPassToNode = this.getAuthMaterial({ + authMethods, authSig, sessionSigs, url, }); - - reqBody.authSig = sigToPassToNode; + + this.setAuthMaterial(reqBody, sigToPassToNode); // this return { url: string, data: JsonRequest } const singleNodePromise = this.getJsExecutionShares( @@ -1276,6 +1278,7 @@ export class LitNodeClientNodeJs extends LitCore { executeJs = async (params: ExecuteJsProps): Promise => { // ========== Prepare Params ========== const { + authMethods, code, ipfsId, authSig, @@ -1311,8 +1314,17 @@ export class LitNodeClientNodeJs extends LitCore { }); } - let res; + // the nodes will only accept a normal array type as a paramater due to serizalization issues with ArrayBuffer type. + // this loop below is to normalize the data to a basic array. + if (jsParams.toSign) { + let arr = []; + for (let i = 0; i < jsParams.toSign.length; i++) { + arr.push((jsParams.toSign as Buffer)[i]); + } + jsParams.toSign = arr; + } + let res; // -- only run on a single node if (targetNodeRange) { res = await this.runOnTargetedNodes(params); @@ -1327,12 +1339,14 @@ export class LitNodeClientNodeJs extends LitCore { const requestId = this.getRequestId(); const nodePromises = this.getNodePromises((url: string) => { // -- choose the right signature - let sigToPassToNode = this.getAuthSigOrSessionAuthSig({ + let sigToPassToNode = this.getAuthMaterial({ + authMethods, authSig, sessionSigs, url, }); - reqBody.authSig = sigToPassToNode; + + this.setAuthMaterial(reqBody, sigToPassToNode); return this.getJsExecutionShares(url, reqBody, requestId); }); @@ -1492,7 +1506,7 @@ export class LitNodeClientNodeJs extends LitCore { const requestId = this.getRequestId(); const nodePromises = this.getNodePromises((url: string) => { // -- choose the right signature - let sigToPassToNode = this.getAuthSigOrSessionAuthSig({ + let sigToPassToNode = this.getAuthMaterial({ authSig, sessionSigs, url,