From cc64ff01585d0b1194b476a22a279003400f4aea Mon Sep 17 00:00:00 2001 From: lwin Date: Tue, 6 Feb 2024 15:01:06 +0800 Subject: [PATCH] verify farcaster login and generate idtoken --- demo/react-app-no-modal/package-lock.json | 156 +++++++++--------- demo/react-app/src/services/web3auth.tsx | 4 - package-lock.json | 27 +-- .../base-evm-adapter/src/baseEvmAdapter.ts | 66 +++++--- .../adapters/farcaster-adapter/package.json | 7 +- .../adapters/farcaster-adapter/src/config.ts | 15 ++ .../farcaster-adapter/src/farcasterAdapter.ts | 101 ++++++++++-- .../adapters/farcaster-adapter/src/index.ts | 2 + .../farcaster-adapter/src/interface.ts | 14 ++ packages/base/src/adapter/IAdapter.ts | 18 ++ packages/base/src/adapter/utils.ts | 42 +++++ packages/base/src/constants.ts | 3 +- packages/modal/src/default.ts | 12 +- 13 files changed, 339 insertions(+), 128 deletions(-) create mode 100644 packages/adapters/farcaster-adapter/src/config.ts create mode 100644 packages/adapters/farcaster-adapter/src/interface.ts diff --git a/demo/react-app-no-modal/package-lock.json b/demo/react-app-no-modal/package-lock.json index 31d82c537..3045a18a6 100644 --- a/demo/react-app-no-modal/package-lock.json +++ b/demo/react-app-no-modal/package-lock.json @@ -44,18 +44,18 @@ }, "../../packages/adapters/coinbase-adapter": { "name": "@web3auth/coinbase-adapter", - "version": "5.2.0", + "version": "7.3.1", "license": "ISC", "dependencies": { - "@web3auth/base": "^5.2.0", - "@web3auth/base-evm-adapter": "^5.2.0" + "@web3auth/base": "^7.3.1", + "@web3auth/base-evm-adapter": "^7.3.1" }, "devDependencies": { - "@coinbase/wallet-sdk": "^3.6.6" + "@coinbase/wallet-sdk": "^3.7.2" }, "engines": { - "node": ">=16.18.1", - "npm": ">=8.x" + "node": ">=18.x", + "npm": ">=9.x" }, "peerDependencies": { "@babel/runtime": "^7.x", @@ -64,16 +64,16 @@ }, "../../packages/adapters/metamask-adapter": { "name": "@web3auth/metamask-adapter", - "version": "5.2.0", + "version": "7.3.1", "license": "ISC", "dependencies": { "@metamask/detect-provider": "^2.0.0", - "@web3auth/base": "^5.2.0", - "@web3auth/base-evm-adapter": "^5.2.0" + "@web3auth/base": "^7.3.1", + "@web3auth/base-evm-adapter": "^7.3.1" }, "engines": { - "node": ">=16.18.1", - "npm": ">=8.x" + "node": ">=18.x", + "npm": ">=9.x" }, "peerDependencies": { "@babel/runtime": "^7.x" @@ -81,21 +81,21 @@ }, "../../packages/adapters/openlogin-adapter": { "name": "@web3auth/openlogin-adapter", - "version": "5.2.1", + "version": "7.3.1", "license": "ISC", "dependencies": { - "@toruslabs/openlogin": "^4.5.2", - "@toruslabs/openlogin-utils": "^4.5.1", - "@web3auth/base": "^5.2.0", - "@web3auth/base-provider": "^5.2.0", + "@toruslabs/openlogin": "^6.2.2", + "@toruslabs/openlogin-utils": "^6.2.2", + "@web3auth/base": "^7.3.1", + "@web3auth/base-provider": "^7.3.1", "lodash.merge": "^4.6.2" }, "devDependencies": { - "@types/lodash.merge": "^4.6.7" + "@types/lodash.merge": "^4.6.9" }, "engines": { - "node": ">=16.18.1", - "npm": ">=8.x" + "node": ">=18.x", + "npm": ">=9.x" }, "peerDependencies": { "@babel/runtime": "^7.x", @@ -104,18 +104,18 @@ }, "../../packages/adapters/phantom-adapter": { "name": "@web3auth/phantom-adapter", - "version": "5.2.0", + "version": "7.3.1", "license": "ISC", "dependencies": { - "@web3auth/base": "^5.2.0", - "@web3auth/base-provider": "^5.2.0", - "@web3auth/base-solana-adapter": "^5.2.0", - "@web3auth/solana-provider": "^5.2.0", + "@web3auth/base": "^7.3.1", + "@web3auth/base-provider": "^7.3.1", + "@web3auth/base-solana-adapter": "^7.3.1", + "@web3auth/solana-provider": "^7.3.1", "bn.js": "^5.2.1" }, "engines": { - "node": ">=16.18.1", - "npm": ">=8.x" + "node": ">=18.x", + "npm": ">=9.x" }, "peerDependencies": { "@babel/runtime": "^7.x", @@ -124,16 +124,16 @@ }, "../../packages/adapters/torus-evm-adapter": { "name": "@web3auth/torus-evm-adapter", - "version": "5.2.0", + "version": "7.3.1", "license": "ISC", "dependencies": { - "@toruslabs/torus-embed": "^2.0.0", - "@web3auth/base": "^5.2.0", - "@web3auth/base-evm-adapter": "^5.2.0" + "@toruslabs/torus-embed": "^4.1.2", + "@web3auth/base": "^7.3.1", + "@web3auth/base-evm-adapter": "^7.3.1" }, "engines": { - "node": ">=16.18.1", - "npm": ">=8.x" + "node": ">=18.x", + "npm": ">=9.x" }, "peerDependencies": { "@babel/runtime": "^7.x" @@ -141,20 +141,20 @@ }, "../../packages/base": { "name": "@web3auth/base", - "version": "5.2.0", + "version": "7.3.1", "license": "ISC", "dependencies": { - "@toruslabs/http-helpers": "^4.0.0", - "@toruslabs/openlogin": "^4.5.2", - "@toruslabs/openlogin-jrpc": "^4.5.1", - "@toruslabs/openlogin-utils": "^4.5.1", - "jwt-decode": "^3.1.2", + "@toruslabs/http-helpers": "^6.0.0", + "@toruslabs/openlogin": "^6.2.2", + "@toruslabs/openlogin-jrpc": "^6.2.2", + "@toruslabs/openlogin-utils": "^6.2.2", + "jwt-decode": "^4.0.0", "loglevel": "^1.8.1", "ts-custom-error": "^3.3.1" }, "engines": { - "node": ">=16.18.1", - "npm": ">=8.x" + "node": ">=18.x", + "npm": ">=9.x" }, "peerDependencies": { "@babel/runtime": "^7.x" @@ -162,19 +162,20 @@ }, "../../packages/no-modal": { "name": "@web3auth/no-modal", - "version": "5.2.0", + "version": "7.3.1", "license": "ISC", "dependencies": { - "@toruslabs/openlogin": "^4.5.2", - "@toruslabs/openlogin-jrpc": "^4.5.1", - "@toruslabs/openlogin-utils": "^4.5.1", - "@web3auth/base": "^5.2.0", - "@web3auth/base-plugin": "^5.2.0", - "@web3auth/base-provider": "^5.2.0" + "@toruslabs/openlogin": "^6.2.2", + "@toruslabs/openlogin-jrpc": "^6.2.2", + "@toruslabs/openlogin-utils": "^6.2.2", + "@web3auth/base": "^7.3.1", + "@web3auth/base-plugin": "^7.3.1", + "@web3auth/base-provider": "^7.3.1", + "@web3auth/ui": "^7.3.1" }, "engines": { - "node": ">=16.18.1", - "npm": ">=8.x" + "node": ">=18.x", + "npm": ">=9.x" }, "peerDependencies": { "@babel/runtime": "^7.x" @@ -25004,11 +25005,11 @@ "@web3auth/base": { "version": "file:../../packages/base", "requires": { - "@toruslabs/http-helpers": "^4.0.0", - "@toruslabs/openlogin": "^4.5.2", - "@toruslabs/openlogin-jrpc": "^4.5.1", - "@toruslabs/openlogin-utils": "^4.5.1", - "jwt-decode": "^3.1.2", + "@toruslabs/http-helpers": "^6.0.0", + "@toruslabs/openlogin": "^6.2.2", + "@toruslabs/openlogin-jrpc": "^6.2.2", + "@toruslabs/openlogin-utils": "^6.2.2", + "jwt-decode": "^4.0.0", "loglevel": "^1.8.1", "ts-custom-error": "^3.3.1" } @@ -25016,57 +25017,58 @@ "@web3auth/coinbase-adapter": { "version": "file:../../packages/adapters/coinbase-adapter", "requires": { - "@coinbase/wallet-sdk": "^3.6.6", - "@web3auth/base": "^5.2.0", - "@web3auth/base-evm-adapter": "^5.2.0" + "@coinbase/wallet-sdk": "^3.7.2", + "@web3auth/base": "^7.3.1", + "@web3auth/base-evm-adapter": "^7.3.1" } }, "@web3auth/metamask-adapter": { "version": "file:../../packages/adapters/metamask-adapter", "requires": { "@metamask/detect-provider": "^2.0.0", - "@web3auth/base": "^5.2.0", - "@web3auth/base-evm-adapter": "^5.2.0" + "@web3auth/base": "^7.3.1", + "@web3auth/base-evm-adapter": "^7.3.1" } }, "@web3auth/no-modal": { "version": "file:../../packages/no-modal", "requires": { - "@toruslabs/openlogin": "^4.5.2", - "@toruslabs/openlogin-jrpc": "^4.5.1", - "@toruslabs/openlogin-utils": "^4.5.1", - "@web3auth/base": "^5.2.0", - "@web3auth/base-plugin": "^5.2.0", - "@web3auth/base-provider": "^5.2.0" + "@toruslabs/openlogin": "^6.2.2", + "@toruslabs/openlogin-jrpc": "^6.2.2", + "@toruslabs/openlogin-utils": "^6.2.2", + "@web3auth/base": "^7.3.1", + "@web3auth/base-plugin": "^7.3.1", + "@web3auth/base-provider": "^7.3.1", + "@web3auth/ui": "^7.3.1" } }, "@web3auth/openlogin-adapter": { "version": "file:../../packages/adapters/openlogin-adapter", "requires": { - "@toruslabs/openlogin": "^4.5.2", - "@toruslabs/openlogin-utils": "^4.5.1", - "@types/lodash.merge": "^4.6.7", - "@web3auth/base": "^5.2.0", - "@web3auth/base-provider": "^5.2.0", + "@toruslabs/openlogin": "^6.2.2", + "@toruslabs/openlogin-utils": "^6.2.2", + "@types/lodash.merge": "^4.6.9", + "@web3auth/base": "^7.3.1", + "@web3auth/base-provider": "^7.3.1", "lodash.merge": "^4.6.2" } }, "@web3auth/phantom-adapter": { "version": "file:../../packages/adapters/phantom-adapter", "requires": { - "@web3auth/base": "^5.2.0", - "@web3auth/base-provider": "^5.2.0", - "@web3auth/base-solana-adapter": "^5.2.0", - "@web3auth/solana-provider": "^5.2.0", + "@web3auth/base": "^7.3.1", + "@web3auth/base-provider": "^7.3.1", + "@web3auth/base-solana-adapter": "^7.3.1", + "@web3auth/solana-provider": "^7.3.1", "bn.js": "^5.2.1" } }, "@web3auth/torus-evm-adapter": { "version": "file:../../packages/adapters/torus-evm-adapter", "requires": { - "@toruslabs/torus-embed": "^2.0.0", - "@web3auth/base": "^5.2.0", - "@web3auth/base-evm-adapter": "^5.2.0" + "@toruslabs/torus-embed": "^4.1.2", + "@web3auth/base": "^7.3.1", + "@web3auth/base-evm-adapter": "^7.3.1" } }, "@webassemblyjs/ast": { diff --git a/demo/react-app/src/services/web3auth.tsx b/demo/react-app/src/services/web3auth.tsx index 2bb1f05f6..8fa19dbaf 100644 --- a/demo/react-app/src/services/web3auth.tsx +++ b/demo/react-app/src/services/web3auth.tsx @@ -164,10 +164,6 @@ export const Web3AuthProvider: FunctionComponent = ({ children, name: "apple", mainOption: true, }, - banana: { - name: "banana", - mainOption: true, - } }, // setting it to false will hide all social login methods from modal. showOnModal: true, diff --git a/package-lock.json b/package-lock.json index b0931a0a0..4629cc4f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5693,14 +5693,14 @@ } }, "node_modules/@toruslabs/openlogin": { - "version": "6.2.5", - "resolved": "https://registry.npmjs.org/@toruslabs/openlogin/-/openlogin-6.2.5.tgz", - "integrity": "sha512-8jykMP+aNYv+HUGOqVDtVRdQq/clI2M9PpUc0FSEw4VGKmjC8a/jxNaVxxs6LMrKu1wq07t3tJbGBtboeKxWhQ==", + "version": "6.2.9", + "resolved": "https://registry.npmjs.org/@toruslabs/openlogin/-/openlogin-6.2.9.tgz", + "integrity": "sha512-Wxb6mhDybZXpHwkZqS6T90JgZQXsrNvLLEE3Od+L19B2QuHA3kIhRFSK3CMTPGy5Tdqkwlv6HYGyCWIOco2dyQ==", "dependencies": { "@toruslabs/eccrypto": "^4.0.0", "@toruslabs/metadata-helpers": "^5.0.0", "@toruslabs/openlogin-session-manager": "^3.0.0", - "@toruslabs/openlogin-utils": "^6.2.5", + "@toruslabs/openlogin-utils": "^6.2.9", "@toruslabs/secure-pub-sub": "^0.0.1", "bowser": "^2.11.0", "events": "^3.3.0", @@ -5785,9 +5785,9 @@ } }, "node_modules/@toruslabs/openlogin-utils": { - "version": "6.2.5", - "resolved": "https://registry.npmjs.org/@toruslabs/openlogin-utils/-/openlogin-utils-6.2.5.tgz", - "integrity": "sha512-qOybDs8ZcpOSDM3lkMs+fjafDvucctwskDRyfilGvWYfgU2Bh7RWnKagaW/QqHPJEApypyOAUAiCNTl+m03KPQ==", + "version": "6.2.9", + "resolved": "https://registry.npmjs.org/@toruslabs/openlogin-utils/-/openlogin-utils-6.2.9.tgz", + "integrity": "sha512-Vkg5+o33fd/0ZerCusD/I0dzIFeohWupV55U7TUahODYPB832uVoPb2rCmiPQ55fUYRJuOjAEYrArOoJd0d2Yw==", "dependencies": { "@toruslabs/constants": "^13.1.0", "base64url": "^3.0.1" @@ -29270,9 +29270,9 @@ } }, "node_modules/viem": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/viem/-/viem-2.7.1.tgz", - "integrity": "sha512-izAX2KedTFnI2l0ZshtnlK2ZuDvSlKeuaanWyNwC4ffDgrCGtwX1bvVXO3Krh53lZgqvjd8UGpjGaBl3WqJ4yQ==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.7.3.tgz", + "integrity": "sha512-+eoi3ZLXX7csB8/mbluIS+d3rUvtDwg3+Iu2hT+plJAdENdin5fW+SCyWOegJCm2yDKSo5xnUDBHMa1TCYk+dQ==", "funding": [ { "type": "github", @@ -30411,9 +30411,14 @@ "version": "0.1.0", "license": "ISC", "dependencies": { + "@farcaster/auth-client": "^0.1.0", + "@toruslabs/openlogin": "^6.2.9", + "@toruslabs/openlogin-utils": "^6.2.9", "@web3auth/base": "^7.3.1", "@web3auth/base-evm-adapter": "^7.3.1", - "@web3auth/ethereum-provider": "file:../../providers/ethereum-provider" + "@web3auth/ethereum-provider": "file:../../providers/ethereum-provider", + "lodash.merge": "^4.6.2", + "viem": "^2.7.3" }, "engines": { "node": ">=18.x", diff --git a/packages/adapters/base-evm-adapter/src/baseEvmAdapter.ts b/packages/adapters/base-evm-adapter/src/baseEvmAdapter.ts index 5bf17ccb2..55d70c474 100644 --- a/packages/adapters/base-evm-adapter/src/baseEvmAdapter.ts +++ b/packages/adapters/base-evm-adapter/src/baseEvmAdapter.ts @@ -4,53 +4,79 @@ import { AdapterInitOptions, BaseAdapter, CHAIN_NAMESPACES, - checkIfTokenIsExpired, clearToken, getChainConfig, - getSavedToken, + log, saveToken, signChallenge, UserAuthInfo, verifySignedChallenge, + WALLET_ADAPTERS, WalletLoginError, } from "@web3auth/base"; +type FarcasterVerifyParams = { + nonce: string; + message: string; + signature: string; + domain: string; + account: string; +}; + export abstract class BaseEvmAdapter extends BaseAdapter { async init(_?: AdapterInitOptions): Promise { if (!this.chainConfig) this.chainConfig = getChainConfig(CHAIN_NAMESPACES.EIP155, 1); } - async authenticateUser(): Promise { - if (!this.provider || this.status !== ADAPTER_STATUS.CONNECTED) throw WalletLoginError.notConnectedError(); + async authenticateUser(args?: FarcasterVerifyParams): Promise { + if (!this.provider || (this.name !== WALLET_ADAPTERS.FARCASTER && this.status !== ADAPTER_STATUS.CONNECTED)) + throw WalletLoginError.notConnectedError(); const { chainNamespace, chainId } = this.chainConfig; const accounts = await this.provider.request({ method: "eth_accounts", }); - if (accounts && accounts.length > 0) { - const existingToken = getSavedToken(accounts[0] as string, this.name); - if (existingToken) { - const isExpired = checkIfTokenIsExpired(existingToken); - if (!isExpired) { - return { idToken: existingToken }; - } + if ((accounts && accounts.length > 0) || this.name === WALLET_ADAPTERS.FARCASTER) { + let account = null; + if (this.name === WALLET_ADAPTERS.FARCASTER) { + account = args.account; + } else if (accounts && accounts.length > 0) { + account = accounts[0]; + } else { + throw new Error("invalid account"); } + // const existingToken = getSavedToken(account as string, this.name); + // if (existingToken) { + // const isExpired = checkIfTokenIsExpired(existingToken); + // if (!isExpired) { + // return { idToken: existingToken }; + // } + // } + const payload = { - domain: window.location.origin, - uri: window.location.href, - address: accounts[0], + domain: this.name === WALLET_ADAPTERS.FARCASTER ? "example.com" : window.location.origin, + uri: this.name === WALLET_ADAPTERS.FARCASTER ? "https://example.com/login" : window.location.href, + address: account, chainId: parseInt(chainId, 16), version: "1", - nonce: Math.random().toString(36).slice(2), + nonce: args.nonce, issuedAt: new Date().toISOString(), + statement: this.name === WALLET_ADAPTERS.FARCASTER ? "Farcaster Connect" : "", }; const challenge = await signChallenge(payload, chainNamespace); + log.debug("challenge", challenge); - const signedMessage = await this.provider.request<[string, string], string>({ - method: "personal_sign", - params: [challenge, accounts[0]], - }); + let signedMessage: string = ""; + + if (this.name === WALLET_ADAPTERS.FARCASTER) { + signedMessage = args.signature; + } else { + signedMessage = await this.provider.request<[string, string], string>({ + method: "personal_sign", + params: [challenge, account], + }); + } const idToken = await verifySignedChallenge( chainNamespace, @@ -61,7 +87,7 @@ export abstract class BaseEvmAdapter extends BaseAdapter { this.clientId, this.web3AuthNetwork ); - saveToken(accounts[0] as string, this.name, idToken); + saveToken(account, this.name, idToken); return { idToken, }; diff --git a/packages/adapters/farcaster-adapter/package.json b/packages/adapters/farcaster-adapter/package.json index e56c8c10f..e5aad7f96 100644 --- a/packages/adapters/farcaster-adapter/package.json +++ b/packages/adapters/farcaster-adapter/package.json @@ -34,9 +34,14 @@ "@babel/runtime": "^7.x" }, "dependencies": { + "@farcaster/auth-client": "^0.1.0", + "@toruslabs/openlogin": "^6.2.9", + "@toruslabs/openlogin-utils": "^6.2.9", "@web3auth/base": "^7.3.1", "@web3auth/base-evm-adapter": "^7.3.1", - "@web3auth/ethereum-provider": "file:../../providers/ethereum-provider" + "@web3auth/ethereum-provider": "file:../../providers/ethereum-provider", + "lodash.merge": "^4.6.2", + "viem": "^2.7.3" }, "lint-staged": { "!(*d).ts": [ diff --git a/packages/adapters/farcaster-adapter/src/config.ts b/packages/adapters/farcaster-adapter/src/config.ts new file mode 100644 index 000000000..558b50435 --- /dev/null +++ b/packages/adapters/farcaster-adapter/src/config.ts @@ -0,0 +1,15 @@ +import { OPENLOGIN_NETWORK, UX_MODE } from "@toruslabs/openlogin-utils"; + +import { FarcasterAdapterOptions } from "./interface"; + +export const getOpenloginDefaultOptions = (): FarcasterAdapterOptions => { + return { + adapterSettings: { + network: OPENLOGIN_NETWORK.SAPPHIRE_MAINNET, + clientId: "", + uxMode: UX_MODE.POPUP, + }, + loginSettings: {}, + privateKeyProvider: undefined, + }; +}; diff --git a/packages/adapters/farcaster-adapter/src/farcasterAdapter.ts b/packages/adapters/farcaster-adapter/src/farcasterAdapter.ts index 7285fa8e1..38890ceee 100644 --- a/packages/adapters/farcaster-adapter/src/farcasterAdapter.ts +++ b/packages/adapters/farcaster-adapter/src/farcasterAdapter.ts @@ -1,3 +1,6 @@ +import { StatusAPIResponse } from "@farcaster/auth-client"; +import OpenLogin from "@toruslabs/openlogin"; +import { LoginParams, OPENLOGIN_NETWORK, OpenLoginOptions } from "@toruslabs/openlogin-utils"; import { ADAPTER_CATEGORY, ADAPTER_CATEGORY_TYPE, @@ -7,18 +10,25 @@ import { ADAPTER_STATUS_TYPE, AdapterInitOptions, AdapterNamespaceType, + BaseAdapterSettings, CHAIN_NAMESPACES, ChainNamespaceType, CustomChainConfig, + FarcasterVerifyResult, IProvider, log, UserInfo, + verifyFarcasterLogin, WALLET_ADAPTERS, + WalletInitializationError, } from "@web3auth/base"; import { BaseEvmAdapter } from "@web3auth/base-evm-adapter"; import { FarcasterAuthClientProvider } from "@web3auth/ethereum-provider"; -export class FarcasterAdapter extends BaseEvmAdapter { +import { getOpenloginDefaultOptions } from "./config"; +import { FarcasterAdapterOptions, LoginSettings, PrivateKeyProvider } from "./interface"; + +export class FarcasterAdapter extends BaseEvmAdapter { readonly adapterNamespace: AdapterNamespaceType = ADAPTER_NAMESPACES.EIP155; readonly currentChainNamespace: ChainNamespaceType = CHAIN_NAMESPACES.EIP155; @@ -29,15 +39,36 @@ export class FarcasterAdapter extends BaseEvmAdapter { public status: ADAPTER_STATUS_TYPE = ADAPTER_STATUS.NOT_READY; - fcProvider: FarcasterAuthClientProvider | null = null; + public openLoginInstance: OpenLogin | null = null; + + public fcProvider: FarcasterAuthClientProvider | null = null; + + public userInfo: Partial = {}; + + public privateKeyProvider: PrivateKeyProvider | null = null; + + public loginSettings: LoginSettings = { loginProvider: "" }; - userInfo: Partial = {}; + private openloginOptions: FarcasterAdapterOptions["adapterSettings"]; - // private farcasterStatus:; + constructor(params: FarcasterAdapterOptions) { + super(params); + this.setAdapterSettings({ + ...params.adapterSettings, + chainConfig: params.chainConfig, + clientId: params.clientId || "", + sessionTime: params.sessionTime, + web3AuthNetwork: params.web3AuthNetwork, + useCoreKitKey: params.useCoreKitKey, + privateKeyProvider: params.privateKeyProvider, + }); + this.loginSettings = params.loginSettings || { loginProvider: "" }; + this.privateKeyProvider = params.privateKeyProvider || null; + } get provider(): IProvider | null { - if (this.status !== ADAPTER_STATUS.NOT_READY && this.fcProvider) { - return this.fcProvider; + if (this.status !== ADAPTER_STATUS.NOT_READY && this.privateKeyProvider) { + return this.privateKeyProvider; } return null; } @@ -46,34 +77,50 @@ export class FarcasterAdapter extends BaseEvmAdapter { log.debug("initializing farcaster adapter"); super.init(options); super.checkInitializationRequirements(); + + if (!this.clientId) throw WalletInitializationError.invalidParams("clientId is required before openlogin's initialization"); + if (!this.openloginOptions) throw WalletInitializationError.invalidParams("openloginOptions is required before openlogin's initialization"); + this.fcProvider = new FarcasterAuthClientProvider({ config: { chainConfig: this.chainConfig } }); this.emit(ADAPTER_EVENTS.READY, WALLET_ADAPTERS.FARCASTER); this.status = ADAPTER_STATUS.READY; } - async connect(): Promise { + async connect(params: LoginParams): Promise { super.checkConnectionRequirements(); if (!this.fcProvider) throw new Error("Not able to connect to farcaster"); this.status = ADAPTER_STATUS.CONNECTING; - this.emit(ADAPTER_EVENTS.CONNECTING, { adapter: WALLET_ADAPTERS.FARCASTER }); + this.emit(ADAPTER_EVENTS.CONNECTING, { ...params, adapter: WALLET_ADAPTERS.FARCASTER }); - log.debug("connection farcaster appclient"); + this.openLoginInstance = new OpenLogin({ + ...this.openloginOptions, + clientId: this.clientId, + network: this.openloginOptions.network || this.web3AuthNetwork || OPENLOGIN_NETWORK.SAPPHIRE_MAINNET, + }); + // create channel const chanResponse = await this.fcProvider.createChannel({ siweUri: "https://example.com/login", domain: "example.com", }); + log.debug("farcaster channel response", chanResponse); if (chanResponse.url) { this.updateAdapterData({ farcasterConnectUri: chanResponse.url, farcasterLogin: true }); } + // get status from login const fcStatus = await this.fcProvider.watchStatus({ channelToken: chanResponse.channelToken }); log.debug("status", fcStatus); - this.userInfo = fcStatus.data as Partial; - // const s = fcStatus.data + + if (!fcStatus.error && !fcStatus.isError && fcStatus.data) { + const verifyResponse = await this.verifyLogin(fcStatus.data, "example.com"); + log.debug("verifyResponse", verifyResponse); + } else { + throw new Error(`error connecting to farcaster: ${fcStatus.error}`); + } this.status = ADAPTER_STATUS.CONNECTED; this.emit(ADAPTER_STATUS.CONNECTED, { adapter: WALLET_ADAPTERS.FARCASTER }); @@ -107,4 +154,36 @@ export class FarcasterAdapter extends BaseEvmAdapter { switchChain(_params: { chainId: string }): Promise { throw new Error("Method not implemented."); } + + // should be called only before initialization. + setAdapterSettings(adapterSettings: Partial & { privateKeyProvider?: PrivateKeyProvider }): void { + super.setAdapterSettings(adapterSettings); + const defaultOptions = getOpenloginDefaultOptions(); + log.info("setting adapter settings", adapterSettings); + this.openloginOptions = { + ...defaultOptions.adapterSettings, + ...this.openloginOptions, + ...adapterSettings, + }; + if (adapterSettings.web3AuthNetwork) { + this.openloginOptions.network = adapterSettings.web3AuthNetwork; + } + if (adapterSettings.privateKeyProvider) { + this.privateKeyProvider = adapterSettings.privateKeyProvider; + } + } + + private async verifyLogin(args: StatusAPIResponse, domain: string): Promise { + const res = await verifyFarcasterLogin( + args.nonce, + args.message, + args.signature, + domain, + this.name, + this.sessionTime, + this.clientId, + this.web3AuthNetwork + ); + return res; + } } diff --git a/packages/adapters/farcaster-adapter/src/index.ts b/packages/adapters/farcaster-adapter/src/index.ts index 275b1f6bd..667407f4e 100644 --- a/packages/adapters/farcaster-adapter/src/index.ts +++ b/packages/adapters/farcaster-adapter/src/index.ts @@ -1 +1,3 @@ +export * from "./config"; export * from "./farcasterAdapter"; +export * from "./interface"; diff --git a/packages/adapters/farcaster-adapter/src/interface.ts b/packages/adapters/farcaster-adapter/src/interface.ts new file mode 100644 index 000000000..09c71e5db --- /dev/null +++ b/packages/adapters/farcaster-adapter/src/interface.ts @@ -0,0 +1,14 @@ +import { BaseRedirectParams, LoginParams, OpenLoginOptions } from "@toruslabs/openlogin-utils"; +import { BaseAdapterSettings, IBaseProvider } from "@web3auth/base"; + +type MakeOptional = Omit & Partial>; + +export type LoginSettings = Partial & Partial; + +export type PrivateKeyProvider = IBaseProvider; + +export interface FarcasterAdapterOptions extends BaseAdapterSettings { + adapterSettings?: MakeOptional; + loginSettings?: LoginSettings; + privateKeyProvider?: PrivateKeyProvider; +} diff --git a/packages/base/src/adapter/IAdapter.ts b/packages/base/src/adapter/IAdapter.ts index 27902ab45..94ffa643c 100644 --- a/packages/base/src/adapter/IAdapter.ts +++ b/packages/base/src/adapter/IAdapter.ts @@ -49,6 +49,24 @@ export type CONNECTED_EVENT_DATA = { export type UserAuthInfo = { idToken: string }; +export type FarcasterSIWEMessage = { + domain: string; + address: string; + statement: string; + uri: string; + version: string; + nonce: string; + issuedAt: string; + chainId: number; + resources: string[]; +}; + +export type FarcasterVerifyResult = { + token: string; + fid: string; + userinfo: FarcasterSIWEMessage; +}; + export interface BaseAdapterSettings { clientId?: string; sessionTime?: number; diff --git a/packages/base/src/adapter/utils.ts b/packages/base/src/adapter/utils.ts index c2981ccb5..a45ae0c0e 100644 --- a/packages/base/src/adapter/utils.ts +++ b/packages/base/src/adapter/utils.ts @@ -6,6 +6,7 @@ import { ChainNamespaceType } from "../chain/IChainInterface"; import { authServer } from "../constants"; import log from "../loglevel"; import { storageAvailable } from "../utils"; +import { FarcasterSIWEMessage, FarcasterVerifyResult } from "."; export const checkIfTokenIsExpired = (token: string) => { const decoded = jwtDecode<{ exp: number }>(token); @@ -73,6 +74,47 @@ export const verifySignedChallenge = async ( return idTokenRes.token; }; +export const verifyFarcasterLogin = async ( + nonce: string, + message: string, + signature: string, + domain: string, + issuer: string, + timeout: number, + clientId?: string, + web3AuthNetwork?: OPENLOGIN_NETWORK_TYPE +): Promise => { + const body = { + nonce, + message, + signature, + domain, + issuer, + timeout, + }; + const idTokenRes = await post<{ success: boolean; token?: string; fid?: string; userinfo: FarcasterSIWEMessage; error?: string }>( + `${authServer}/siwf/verify`, + body, + { + headers: { + client_id: clientId, + wallet_provider: issuer, + web3auth_network: web3AuthNetwork, + }, + } + ); + if (!idTokenRes.success) { + log.error("Failed to authenticate user, ,message verification failed", idTokenRes.error); + throw new Error("Failed to authenticate user, ,message verification failed"); + } + + return { + token: idTokenRes.token, + fid: idTokenRes.fid, + userinfo: idTokenRes.userinfo, + }; +}; + export const getSavedToken = (userAddress: string, issuer: string) => { if (storageAvailable("localStorage")) { return localStorage.getItem(`${userAddress.toLowerCase()}_${issuer}`); diff --git a/packages/base/src/constants.ts b/packages/base/src/constants.ts index 1e3f46790..57c4a203a 100644 --- a/packages/base/src/constants.ts +++ b/packages/base/src/constants.ts @@ -1 +1,2 @@ -export const authServer = "https://authjs.web3auth.io"; +// export const authServer = "https://authjs.web3auth.io"; +export const authServer = "http://localhost:4023"; diff --git a/packages/modal/src/default.ts b/packages/modal/src/default.ts index 1ad77cc57..6337e2888 100644 --- a/packages/modal/src/default.ts +++ b/packages/modal/src/default.ts @@ -85,13 +85,19 @@ export const getDefaultAdapterModule = async (params: { }); return adapter; } else if (name === WALLET_ADAPTERS.FARCASTER) { - const { FarcasterAdapter } = await import("@web3auth/farcaster-adapter"); + const { FarcasterAdapter, getOpenloginDefaultOptions } = await import("@web3auth/farcaster-adapter"); + const privateKeyProvider: IBaseProvider = await getPrivateKeyProvider(finalChainConfig); + + const defaultOptions = getOpenloginDefaultOptions(); const adapter = new FarcasterAdapter({ - chainConfig: finalChainConfig, + ...defaultOptions, clientId, + useCoreKitKey, + chainConfig: { ...finalChainConfig }, + adapterSettings: { ...(defaultOptions.adapterSettings as OpenLoginOptions), clientId, network: web3AuthNetwork, whiteLabel: uiConfig }, sessionTime, web3AuthNetwork, - useCoreKitKey, + privateKeyProvider, }); return adapter; }