diff --git a/e2e-nodejs/group-contracts/test-contracts-write-mint-a-pkp-and-set-scope-1-2-easy.mjs b/e2e-nodejs/group-contracts/test-contracts-write-mint-a-pkp-and-set-scope-1-2-easy.mjs new file mode 100644 index 0000000000..7cacdf893e --- /dev/null +++ b/e2e-nodejs/group-contracts/test-contracts-write-mint-a-pkp-and-set-scope-1-2-easy.mjs @@ -0,0 +1,72 @@ +import path from 'path'; +import { success, fail, testThis } from '../../tools/scripts/utils.mjs'; +import LITCONFIG from '../../lit.config.json' assert { type: 'json' }; +import { LitContracts } from '@lit-protocol/contracts-sdk'; +import { ethers } from 'ethers'; +import { AuthMethodType, AuthMethodScope } from '@lit-protocol/constants'; + +export async function main() { + // ========== Controller Setup =========== + const provider = new ethers.providers.JsonRpcProvider( + LITCONFIG.CHRONICLE_RPC + ); + + const controllerWallet = new ethers.Wallet( + LITCONFIG.CONTROLLER_PRIVATE_KEY, + provider + ); + + // ==================== LitContracts Setup ==================== + const contractClient = new LitContracts({ + signer: controllerWallet, + }); + + await contractClient.connect(); + + // ==================== Test Logic ==================== + const mintInfo = await contractClient.mintWithAuth({ + authMethod: { + authMethodType: AuthMethodType.EthWallet, + accessToken: JSON.stringify(LITCONFIG.CONTROLLER_AUTHSIG), + }, + scopes: [AuthMethodScope.SignAnything, AuthMethodScope.OnlySignMessages], + }); + + if (!mintInfo.tx.transactionHash) { + return fail(`failed to mint a PKP`); + } + + // ==================== Post-Validation ==================== + // NOTE: When using other auth methods, you might need to wait for a block to be mined before you can read the scopes + // -- get the scopes + const scopes = + await contractClient.pkpPermissionsContract.read.getPermittedAuthMethodScopes( + mintInfo.pkp.tokenId, + AuthMethodType.EthWallet, + LITCONFIG.CONTROLLER_AUTHSIG.address, // auth id + 3 // we only offer 2 scopes atm. and index 0 doesn't exist, so either 1 = sign anything or 2 = only sign messages + ); + + const signAnythingScope = scopes[1]; + const onlySignMessagesScope = scopes[2]; + + if (!signAnythingScope) { + return fail(`signAnythingScope should be true`); + } + + if (!onlySignMessagesScope) { + return fail(`onlySignMessagesScope should be true`); + } + + // ==================== Success ==================== + return success(`ContractsSDK mints a PKP and set scope 1 and 2 +Logs: +--- +tokenId: ${mintInfo.pkp.tokenId} +transactionHash: ${mintInfo.tx.transactionHash} +signAnythingScope: ${signAnythingScope} +onlySignMessagesScope: ${onlySignMessagesScope} +`); +} + +await testThis({ name: path.basename(import.meta.url), fn: main }); diff --git a/e2e-nodejs/group-contracts/test-contracts-write-mint-a-pkp-and-set-scope-1-advanced.mjs b/e2e-nodejs/group-contracts/test-contracts-write-mint-a-pkp-and-set-scope-1-advanced.mjs new file mode 100644 index 0000000000..2a4a00bb13 --- /dev/null +++ b/e2e-nodejs/group-contracts/test-contracts-write-mint-a-pkp-and-set-scope-1-advanced.mjs @@ -0,0 +1,78 @@ +import path from 'path'; +import { success, fail, testThis } from '../../tools/scripts/utils.mjs'; +import LITCONFIG from '../../lit.config.json' assert { type: 'json' }; +import { LitContracts } from '@lit-protocol/contracts-sdk'; +import { ethers } from 'ethers'; +import { AuthMethodType } from '@lit-protocol/constants'; + +export async function main() { + // ========== Controller Setup =========== + const provider = new ethers.providers.JsonRpcProvider( + LITCONFIG.CHRONICLE_RPC + ); + + const controllerWallet = new ethers.Wallet( + LITCONFIG.CONTROLLER_PRIVATE_KEY, + provider + ); + + // ==================== LitContracts Setup ==================== + const contractClient = new LitContracts({ + signer: controllerWallet, + }); + + await contractClient.connect(); + + // ==================== Test Logic ==================== + const mintCost = await contractClient.pkpNftContract.read.mintCost(); + + // -- minting a PKP + const mintTx = + await contractClient.pkpHelperContract.write.mintNextAndAddAuthMethods( + 2, + [AuthMethodType.EthWallet], + [LITCONFIG.CONTROLLER_AUTHSIG.address], // auth id + ['0x'], // only for web3auth atm + [[1]], + true, // addPkpEthAddressAsPermittedAddress, + true, // sendPkpToItself, + { + value: mintCost, + } + ); + + const mintTxReceipt = await mintTx.wait(); + + const tokenId = mintTxReceipt.events[0].topics[1]; + console.log('tokenId', tokenId); + + // -- get the scopes + const scopes = + await contractClient.pkpPermissionsContract.read.getPermittedAuthMethodScopes( + tokenId, + AuthMethodType.EthWallet, + LITCONFIG.CONTROLLER_AUTHSIG.address, // auth id + 3 // we only offer 2 scopes atm. and index 0 doesn't exist, so either 1 = sign anything or 2 = only sign messages + ); + + // ==================== Post-Validation ==================== + if (mintCost === undefined || mintCost === null) { + return fail('mintCost should not be empty'); + } + + if (scopes[1] !== true) { + return fail('scope 1 (sign anything) should be true'); + } + + // ==================== Success ==================== + return success(`ContractsSDK mints a PKP +Logs: +--- +mintHash: ${mintTxReceipt.transactionHash} +tokenId: ${tokenId} +scope 1 (sign anything): ${scopes[1]} +scope 2 (only sign messages): ${scopes[2]} +`); +} + +await testThis({ name: path.basename(import.meta.url), fn: main }); diff --git a/e2e-nodejs/group-contracts/test-contracts-write-mint-a-pkp-then-set-scope-1.mjs b/e2e-nodejs/group-contracts/test-contracts-write-mint-a-pkp-then-set-scope-1.mjs new file mode 100644 index 0000000000..933736ad46 --- /dev/null +++ b/e2e-nodejs/group-contracts/test-contracts-write-mint-a-pkp-then-set-scope-1.mjs @@ -0,0 +1,98 @@ +import path from 'path'; +import { success, fail, testThis } from '../../tools/scripts/utils.mjs'; +import LITCONFIG from '../../lit.config.json' assert { type: 'json' }; +import { LitContracts } from '@lit-protocol/contracts-sdk'; +import { ethers } from 'ethers'; +import { AuthMethodType } from '@lit-protocol/constants'; + +export async function main() { + // ========== Controller Setup =========== + const provider = new ethers.providers.JsonRpcProvider( + LITCONFIG.CHRONICLE_RPC + ); + + const controllerWallet = new ethers.Wallet( + LITCONFIG.CONTROLLER_PRIVATE_KEY, + provider + ); + + // ==================== LitContracts Setup ==================== + const contractClient = new LitContracts({ + signer: controllerWallet, + }); + + await contractClient.connect(); + + // ==================== Test Logic ==================== + const mintCost = await contractClient.pkpNftContract.read.mintCost(); + + // -- minting a PKP using a PKP + const mintTx = await contractClient.pkpNftContract.write.mintNext(2, { + value: mintCost, + }); + + const mintTxReceipt = await mintTx.wait(); + + const tokenId = mintTxReceipt.events[0].topics[1]; + console.log('tokenId', tokenId); + + // -- get the scopes + const scopes = + await contractClient.pkpPermissionsContract.read.getPermittedAuthMethodScopes( + tokenId, + AuthMethodType.EthWallet, + LITCONFIG.CONTROLLER_AUTHSIG.address, // auth id + 3 // we only offer 2 scopes atm. and index 0 doesn't exist, so either 1 = sign anything or 2 = only sign messages + ); + + // -- validate both scopes should be false + if (scopes[1] !== false) { + return fail('scope 1 (sign anything) should be false'); + } + + if (scopes[2] !== false) { + return fail('scope 2 (only sign messages) should be false'); + } + + // -- set the scope + const setScopeTx = + await contractClient.pkpPermissionsContract.write.addPermittedAuthMethodScope( + tokenId, + AuthMethodType.EthWallet, + LITCONFIG.CONTROLLER_AUTHSIG.address, // auth id + 1 // sign anything + ); + + const setScopeTxReceipt = await setScopeTx.wait(); + + // -- check the scopes again + const scopes2 = + await contractClient.pkpPermissionsContract.read.getPermittedAuthMethodScopes( + tokenId, + AuthMethodType.EthWallet, + LITCONFIG.CONTROLLER_AUTHSIG.address, // auth id + 3 // we only offer 2 scopes atm. and index 0 doesn't exist, so either 1 = sign anything or 2 = only sign messages + ); + + // ==================== Post-Validation ==================== + if (mintCost === undefined || mintCost === null) { + return fail('mintCost should not be empty'); + } + + if (scopes2[1] !== true) { + return fail('scope 1 (sign anything) should be true'); + } + + // ==================== Success ==================== + return success(`ContractsSDK mints a PKP +Logs: +--- +mintHash: ${mintTxReceipt.transactionHash} +tokenId: ${tokenId} +setScopeHash: ${setScopeTxReceipt.transactionHash} +scope 1 (sign anything): ${scopes2[1]} +scope 2 (only sign messages): ${scopes2[2]} +`); +} + +await testThis({ name: path.basename(import.meta.url), fn: main }); diff --git a/e2e-nodejs/group-contracts/test-contracts-write-mint-a-pkp-with-a-pkp-with-no-permissions.mjs b/e2e-nodejs/group-contracts/test-contracts-write-mint-a-pkp-with-a-pkp-with-no-permissions.mjs new file mode 100644 index 0000000000..f41ffba251 --- /dev/null +++ b/e2e-nodejs/group-contracts/test-contracts-write-mint-a-pkp-with-a-pkp-with-no-permissions.mjs @@ -0,0 +1,96 @@ +import path from 'path'; +import { success, fail, testThis } from '../../tools/scripts/utils.mjs'; +import LITCONFIG from '../../lit.config.json' assert { type: 'json' }; +import { LitContracts } from '@lit-protocol/contracts-sdk'; +import { PKPEthersWallet } from '@lit-protocol/pkp-ethers'; +import { ethers } from 'ethers'; + +async function getFundsFromPKPController() { + // ========== Controller Setup =========== + const provider = new ethers.providers.JsonRpcProvider( + LITCONFIG.CHRONICLE_RPC + ); + + const controllerWallet = new ethers.Wallet( + LITCONFIG.CONTROLLER_PRIVATE_KEY, + provider + ); + + // get the fund + console.log( + 'Controller Balance:', + (await controllerWallet.getBalance()).toString() + ); + + // send some funds to the pkp + const amount = '0.0000001'; + + const tx = await controllerWallet.sendTransaction({ + to: LITCONFIG.PKP_ETH_ADDRESS, + value: ethers.utils.parseEther(amount), + }); + + await tx.wait(); + + console.log( + 'New Controller Balance:', + (await controllerWallet.getBalance()).toString() + ); + console.log(`Sent ${amount} ETH to ${LITCONFIG.PKP_ETH_ADDRESS}`); +} + +export async function main() { + // ========== PKP WALLET SETUP =========== + const pkpWallet = new PKPEthersWallet({ + pkpPubKey: LITCONFIG.PKP_PUBKEY, + controllerAuthSig: LITCONFIG.CONTROLLER_AUTHSIG, + rpc: LITCONFIG.CHRONICLE_RPC, + }); + + await pkpWallet.init(); + + const pkpBalance = parseFloat(await pkpWallet.getBalance()); + + if (pkpBalance <= 0) { + console.log( + `PKP Balance is ${pkpBalance}. Getting funds from controller...` + ); + await getFundsFromPKPController(); + console.log('New PKP Balance:', (await pkpWallet.getBalance()).toString()); + } + + if (pkpWallet._isSigner !== true) { + return fail('pkpWallet should be signer'); + } + + // ==================== LitContracts Setup ==================== + const contractClient = new LitContracts({ + signer: pkpWallet, + }); + + await contractClient.connect(); + + // ==================== Test Logic ==================== + const mintCost = await contractClient.pkpNftContract.read.mintCost(); + + // -- minting a PKP using a PKP + const mintTx = + await contractClient.pkpNftContract.write.populateTransaction.mintNext(2, { + value: mintCost, + }); + + const signedMintTx = await pkpWallet.signTransaction(mintTx); + + const sentTx = await pkpWallet.sendTransaction(signedMintTx); + + // ==================== Post-Validation ==================== + if (mintCost === undefined || mintCost === null) { + return fail('mintCost should not be empty'); + } + + // ==================== Success ==================== + return success(`ContractsSDK mint a PKP using PKP Ethers wallet +hash: ${sentTx.hash}`); +} + +await testThis({ name: path.basename(import.meta.url), fn: main }); diff --git a/e2e-nodejs/group-contracts/test-contracts-write-mint-a-pkp-with-no-permissions.mjs b/e2e-nodejs/group-contracts/test-contracts-write-mint-a-pkp-with-no-permissions.mjs new file mode 100644 index 0000000000..c93fd1e6e5 --- /dev/null +++ b/e2e-nodejs/group-contracts/test-contracts-write-mint-a-pkp-with-no-permissions.mjs @@ -0,0 +1,44 @@ +import path from 'path'; +import { success, fail, testThis } from '../../tools/scripts/utils.mjs'; +import LITCONFIG from '../../lit.config.json' assert { type: 'json' }; +import { LitContracts } from '@lit-protocol/contracts-sdk'; +import { ethers } from 'ethers'; + +export async function main() { + // ========== Controller Setup =========== + const provider = new ethers.providers.JsonRpcProvider( + LITCONFIG.CHRONICLE_RPC + ); + + const controllerWallet = new ethers.Wallet( + LITCONFIG.CONTROLLER_PRIVATE_KEY, + provider + ); + + // ==================== LitContracts Setup ==================== + const contractClient = new LitContracts({ + signer: controllerWallet, + }); + + await contractClient.connect(); + + // ==================== Test Logic ==================== + const mintCost = await contractClient.pkpNftContract.read.mintCost(); + + // -- minting a PKP using a PKP + const mintTx = await contractClient.pkpNftContract.write.mintNext(2, { + value: mintCost, + }); + + const mintTxReceipt = await mintTx.wait(); + + // ==================== Post-Validation ==================== + if (mintCost === undefined || mintCost === null) { + return fail('mintCost should not be empty'); + } + + // ==================== Success ==================== + return success(`ContractsSDK mints a PKP ${mintTxReceipt.transactionHash}`); +} + +await testThis({ name: path.basename(import.meta.url), fn: main }); diff --git a/e2e-nodejs/index.mjs b/e2e-nodejs/index.mjs index 02a6cb499c..de608ea72b 100644 --- a/e2e-nodejs/index.mjs +++ b/e2e-nodejs/index.mjs @@ -99,6 +99,11 @@ async function main() { const group = file.split('/')[file.split('/').length - 2]; // Assuming group is the second last part of the file path + // skip the for loop if group is in IGNORE_DIRS + if (IGNORE_DIRS.includes(group)) { + continue; + } + if (group !== currentGroup) { console.log(`\nRunning tests in ${group}`); currentGroup = group; diff --git a/packages/constants/src/lib/enums.ts b/packages/constants/src/lib/enums.ts index cf040ee1c2..e13a1c24a0 100644 --- a/packages/constants/src/lib/enums.ts +++ b/packages/constants/src/lib/enums.ts @@ -37,6 +37,12 @@ export enum AuthMethodType { StytchTotpFactorOtp = 13, } +export enum AuthMethodScope { + NoPermissions = 0, + SignAnything = 1, + OnlySignMessages = 2, +} + /** * Supported provider types */ diff --git a/packages/contracts-sdk/src/lib/contracts-sdk.ts b/packages/contracts-sdk/src/lib/contracts-sdk.ts index 2dd6264b70..ebf8f66d97 100644 --- a/packages/contracts-sdk/src/lib/contracts-sdk.ts +++ b/packages/contracts-sdk/src/lib/contracts-sdk.ts @@ -1,7 +1,8 @@ -import { BytesLike, ethers } from 'ethers'; +import { BigNumberish, BytesLike, ethers } from 'ethers'; import { hexToDec, decToHex } from './hex2dec'; import bs58 from 'bs58'; import { isBrowser, isNode } from '@lit-protocol/misc'; +import { DiscordProvider, EthWalletProvider, GoogleProvider, WebAuthnProvider } from '@lit-protocol/lit-auth-client'; let CID: any; try { @@ -39,6 +40,8 @@ import * as stakingContract from '../abis/Staking.sol/Staking'; import { TokenInfo, derivedAddresses } from './addresses'; import { IPubkeyRouter } from '../abis/PKPNFT.sol/PKPNFT'; import { computeAddress } from 'ethers/lib/utils'; +import { AuthMethod } from '@lit-protocol/types'; +import { AuthMethodType } from '@lit-protocol/constants'; const DEFAULT_RPC = 'https://chain-rpc.litprotocol.com/http'; const BLOCK_EXPLORER = 'https://chain.litprotocol.com/'; @@ -488,6 +491,128 @@ export class LitContracts { this.connected = true; }; + mintWithAuth = async ({ authMethod, scopes, pubkey }: { + authMethod: AuthMethod, + scopes: string[] | number[] | BigNumberish[], + pubkey?: string // only applies to webauthn auth method + }) => { + + // -- validate + if (!this.connected) { + throw new Error( + 'Contracts are not connected. Please call connect() first' + ); + } + + if (!this.pkpNftContract) { + throw new Error('Contract is not available'); + } + + if (authMethod && !authMethod?.authMethodType) { + throw new Error('authMethodType is required'); + } + + if (authMethod && !authMethod?.accessToken) { + throw new Error('accessToken is required'); + } + + if (scopes.length <= 0) { + throw new Error(`❌ Permission scopes are required! +[0] No Permissions +[1] Sign Anything +[2] Only Sign Messages +Read more here: +https://developer.litprotocol.com/v3/sdk/wallets/auth-methods/#auth-method-scopes + `); + } + + // -- prepare + const _pubkey = pubkey ?? '0x'; + + // if scopes are list of strings, turn them into numbers + scopes = scopes.map((scope) => { + if (typeof scope === 'string') { + return ethers.BigNumber.from(scope) + } + if (typeof scope === 'number') { + return ethers.BigNumber.from(scope.toString()) + } + return scope; + }); + + let authId; + + switch (authMethod.authMethodType) { + case AuthMethodType.EthWallet: + authId = await EthWalletProvider.authMethodId(authMethod); + break; + case AuthMethodType.Discord: + authId = await DiscordProvider.authMethodId(authMethod); + break; + case AuthMethodType.WebAuthn: + authId = await WebAuthnProvider.authMethodId(authMethod); + break; + case AuthMethodType.GoogleJwt: + authId = await GoogleProvider.authMethodId(authMethod); + break; + case AuthMethodType.StytchOtp: + authId = await GoogleProvider.authMethodId(authMethod); + break; + default: + throw new Error(`Unsupported auth method type: ${authMethod.authMethodType}`); + } + + // -- go + const mintCost = await this.pkpNftContract.read.mintCost(); + + // -- start minting + const tx = await this.pkpHelperContract.write.mintNextAndAddAuthMethods( + 2, // key type + [authMethod.authMethodType], + [authId], + [_pubkey], + [[...scopes]], + true, + true, + { + value: mintCost + } + ); + + const receipt = await tx.wait(); + + let events = 'events' in receipt ? receipt.events : receipt.logs; + + if (!events) { + throw new Error('No events found in receipt'); + } + + let tokenId; + + tokenId = events[0].topics[1]; + console.warn('tokenId:', tokenId); + + let publicKey = await this.pkpNftContract.read.getPubkey( + tokenId + ); + + if (publicKey.startsWith('0x')) { + publicKey = publicKey.slice(2); + } + + const pubkeyBuffer = Buffer.from(publicKey, 'hex'); + + const ethAddress = computeAddress(pubkeyBuffer); + + return { + pkp: { + tokenId, + publicKey, + ethAddress, + }, + tx: receipt, + }; + } // getRandomPrivateKeySignerProvider = () => { // const privateKey = ethers.utils.hexlify(ethers.utils.randomBytes(32)); @@ -764,7 +889,7 @@ export class LitContracts { let tokenIdFromEvent; - tokenIdFromEvent = events[1].topics[1]; + tokenIdFromEvent = events[0].topics[1]; console.warn('tokenIdFromEvent:', tokenIdFromEvent); let publicKey = await this.pkpNftContract.read.getPubkey( @@ -1393,7 +1518,7 @@ export class LitContracts { const res: any = await tx.wait(); - const tokenIdFromEvent = res.events[0].topics[3]; + const tokenIdFromEvent = res.events[0].topics[1]; return { tx, tokenId: tokenIdFromEvent }; }, diff --git a/packages/lit-auth-client/src/lib/providers/AppleProvider.ts b/packages/lit-auth-client/src/lib/providers/AppleProvider.ts index 3f3d5e27f7..25a73a5476 100644 --- a/packages/lit-auth-client/src/lib/providers/AppleProvider.ts +++ b/packages/lit-auth-client/src/lib/providers/AppleProvider.ts @@ -104,6 +104,9 @@ export default class AppleProvider extends BaseProvider { * @returns {Promise} - Auth method id */ public async getAuthMethodId(authMethod: AuthMethod): Promise { + return AppleProvider.authMethodId(authMethod); + } + public static async authMethodId(authMethod: AuthMethod): Promise { const tokenPayload = jose.decodeJwt(authMethod.accessToken); const userId: string = tokenPayload['sub'] as string; const audience: string = tokenPayload['aud'] as string; diff --git a/packages/lit-auth-client/src/lib/providers/DiscordProvider.ts b/packages/lit-auth-client/src/lib/providers/DiscordProvider.ts index e924e154b2..d240e01a86 100644 --- a/packages/lit-auth-client/src/lib/providers/DiscordProvider.ts +++ b/packages/lit-auth-client/src/lib/providers/DiscordProvider.ts @@ -115,6 +115,33 @@ export default class DiscordProvider extends BaseProvider { return authMethodId; } + public static async authMethodId(authMethod: AuthMethod, clientId?: string): Promise { + + const _clientId = clientId || '1052874239658692668'; + + // -- get user id from access token + let userId; + const meResponse = await fetch('https://discord.com/api/users/@me', { + method: 'GET', + headers: { + authorization: `Bearer ${authMethod.accessToken}`, + }, + }); + if (meResponse.ok) { + const user = await meResponse.json(); + userId = user.id; + } else { + throw new Error('Unable to verify Discord account'); + } + + // -- get auth method id + const authMethodId = ethers.utils.keccak256( + ethers.utils.toUtf8Bytes(`${userId}:${_clientId}`) + ); + + return authMethodId; + } + /** * Fetch Discord user ID * diff --git a/packages/lit-auth-client/src/lib/providers/EthWalletProvider.ts b/packages/lit-auth-client/src/lib/providers/EthWalletProvider.ts index e9888887f0..07a0983828 100644 --- a/packages/lit-auth-client/src/lib/providers/EthWalletProvider.ts +++ b/packages/lit-auth-client/src/lib/providers/EthWalletProvider.ts @@ -110,6 +110,10 @@ export default class EthWalletProvider extends BaseProvider { * @returns {Promise} - Auth method id */ public async getAuthMethodId(authMethod: AuthMethod): Promise { + return EthWalletProvider.authMethodId(authMethod); + } + + public static async authMethodId(authMethod: AuthMethod): Promise { let address: string; try { diff --git a/packages/lit-auth-client/src/lib/providers/GoogleProvider.ts b/packages/lit-auth-client/src/lib/providers/GoogleProvider.ts index e27db144ee..e4791c97b4 100644 --- a/packages/lit-auth-client/src/lib/providers/GoogleProvider.ts +++ b/packages/lit-auth-client/src/lib/providers/GoogleProvider.ts @@ -104,6 +104,10 @@ export default class GoogleProvider extends BaseProvider { * @returns {Promise} - Auth method id */ public async getAuthMethodId(authMethod: AuthMethod): Promise { + return GoogleProvider.authMethodId(authMethod); + } + + public static async authMethodId(authMethod: AuthMethod): Promise { const tokenPayload = jose.decodeJwt(authMethod.accessToken); const userId: string = tokenPayload['sub'] as string; const audience: string = tokenPayload['aud'] as string; diff --git a/packages/lit-auth-client/src/lib/providers/StytchOtpProvider.ts b/packages/lit-auth-client/src/lib/providers/StytchOtpProvider.ts index 22af52e00c..f6e1d72c10 100644 --- a/packages/lit-auth-client/src/lib/providers/StytchOtpProvider.ts +++ b/packages/lit-auth-client/src/lib/providers/StytchOtpProvider.ts @@ -49,7 +49,7 @@ export class StytchOtpProvider extends BaseProvider { ); } - const parsedToken: StytchToken = this._parseJWT(accessToken); + const parsedToken: StytchToken = StytchOtpProvider._parseJWT(accessToken); const audience = (parsedToken['aud'] as string[])[0]; if (audience != this._params.appId) { reject(new Error('Parsed application id does not match parameters')); @@ -93,7 +93,11 @@ export class StytchOtpProvider extends BaseProvider { * @returns {Promise} - Auth method id */ public async getAuthMethodId(authMethod: AuthMethod): Promise { - const tokenBody = this._parseJWT(authMethod.accessToken); + return StytchOtpProvider.authMethodId(authMethod); + } + + public static async authMethodId(authMethod: AuthMethod): Promise { + const tokenBody = StytchOtpProvider._parseJWT(authMethod.accessToken); const userId = tokenBody['sub'] as string; const orgId = (tokenBody['aud'] as string[])[0]; const authMethodId = ethers.utils.keccak256( @@ -107,7 +111,7 @@ export class StytchOtpProvider extends BaseProvider { * @param jwt token to parse * @returns {string}- userId contained within the token message */ - private _parseJWT(jwt: string): StytchToken { + public static _parseJWT(jwt: string): StytchToken { const parts = jwt.split('.'); if (parts.length !== 3) { throw new Error('Invalid token length'); diff --git a/packages/lit-auth-client/src/lib/providers/WebAuthnProvider.ts b/packages/lit-auth-client/src/lib/providers/WebAuthnProvider.ts index ebbdc54854..7044ee7f61 100644 --- a/packages/lit-auth-client/src/lib/providers/WebAuthnProvider.ts +++ b/packages/lit-auth-client/src/lib/providers/WebAuthnProvider.ts @@ -153,8 +153,14 @@ export default class WebAuthnProvider extends BaseProvider { * @returns {Promise} - Auth method id */ public async getAuthMethodId(authMethod: AuthMethod): Promise { + return WebAuthnProvider.authMethodId(authMethod, this.rpName); + } + + public static async authMethodId(authMethod: AuthMethod, rpName?: string): Promise { let credentialId: string; + const rpNameToUse = rpName || 'lit'; + try { credentialId = JSON.parse(authMethod.accessToken).rawId; } catch (err) { @@ -164,7 +170,7 @@ export default class WebAuthnProvider extends BaseProvider { } const authMethodId = ethers.utils.keccak256( - ethers.utils.toUtf8Bytes(`${credentialId}:${this.rpName}`) + ethers.utils.toUtf8Bytes(`${credentialId}:${rpNameToUse}`) ); return authMethodId; }