diff --git a/.changeset/dull-cooks-fry.md b/.changeset/dull-cooks-fry.md new file mode 100644 index 0000000000..01cf92ec24 --- /dev/null +++ b/.changeset/dull-cooks-fry.md @@ -0,0 +1,8 @@ +--- +'@clerk/clerk-js': minor +'@clerk/elements': minor +'@clerk/clerk-react': minor +'@clerk/types': minor +--- + +Support OKW Wallet Web3 provider and authentication strategy diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 47e67ba8d0..41b3f9929d 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -14,6 +14,7 @@ import type { AuthenticateWithCoinbaseWalletParams, AuthenticateWithGoogleOneTapParams, AuthenticateWithMetamaskParams, + AuthenticateWithOKXWalletParams, Clerk as ClerkInterface, ClerkAPIError, ClerkAuthenticateWithWeb3Params, @@ -77,6 +78,7 @@ import { errorThrower, generateSignatureWithCoinbaseWallet, generateSignatureWithMetamask, + generateSignatureWithOKXWallet, getClerkQueryParam, getWeb3Identifier, hasExternalAccountSignUpError, @@ -1542,6 +1544,18 @@ export class Clerk implements ClerkInterface { }); }; + public authenticateWithOKXWallet = async (props: AuthenticateWithOKXWalletParams = {}): Promise => { + if (__BUILD_DISABLE_RHC__) { + clerkUnsupportedEnvironmentWarning('OKX Wallet'); + return; + } + + await this.authenticateWithWeb3({ + ...props, + strategy: 'web3_okx_wallet_signature', + }); + }; + public authenticateWithWeb3 = async ({ redirectUrl, signUpContinueUrl, @@ -1561,7 +1575,11 @@ export class Clerk implements ClerkInterface { const provider = strategy.replace('web3_', '').replace('_signature', '') as Web3Provider; const identifier = await getWeb3Identifier({ provider }); const generateSignature = - provider === 'metamask' ? generateSignatureWithMetamask : generateSignatureWithCoinbaseWallet; + provider === 'metamask' + ? generateSignatureWithMetamask + : provider === 'coinbase_wallet' + ? generateSignatureWithCoinbaseWallet + : generateSignatureWithOKXWallet; const navigate = (to: string) => customNavigate && typeof customNavigate === 'function' ? customNavigate(to) : this.navigate(to); diff --git a/packages/clerk-js/src/core/resources/SignIn.ts b/packages/clerk-js/src/core/resources/SignIn.ts index 2b5e210080..4a3c1f1812 100644 --- a/packages/clerk-js/src/core/resources/SignIn.ts +++ b/packages/clerk-js/src/core/resources/SignIn.ts @@ -41,8 +41,10 @@ import type { import { generateSignatureWithCoinbaseWallet, generateSignatureWithMetamask, + generateSignatureWithOKXWallet, getCoinbaseWalletIdentifier, getMetamaskIdentifier, + getOKXWalletIdentifier, windowNavigate, } from '../../utils'; import { @@ -123,6 +125,9 @@ export class SignIn extends BaseResource implements SignInResource { case 'web3_coinbase_wallet_signature': config = { web3WalletId: factor.web3WalletId } as Web3SignatureConfig; break; + case 'web3_okx_wallet_signature': + config = { web3WalletId: factor.web3WalletId } as Web3SignatureConfig; + break; case 'reset_password_phone_code': config = { phoneNumberId: factor.phoneNumberId } as ResetPasswordPhoneCodeFactorConfig; break; @@ -323,6 +328,20 @@ export class SignIn extends BaseResource implements SignInResource { }); }; + public authenticateWithOKXWallet = async (): Promise => { + if (__BUILD_DISABLE_RHC__) { + clerkUnsupportedEnvironmentWarning('OKX Wallet'); + return this; + } + + const identifier = await getOKXWalletIdentifier(); + return this.authenticateWithWeb3({ + identifier, + generateSignature: generateSignatureWithOKXWallet, + strategy: 'web3_okx_wallet_signature', + }); + }; + public authenticateWithPasskey = async (params?: AuthenticateWithPasskeyParams): Promise => { const { flow } = params || {}; diff --git a/packages/clerk-js/src/core/resources/SignUp.ts b/packages/clerk-js/src/core/resources/SignUp.ts index be227ce3c8..5586e77e86 100644 --- a/packages/clerk-js/src/core/resources/SignUp.ts +++ b/packages/clerk-js/src/core/resources/SignUp.ts @@ -27,8 +27,10 @@ import type { import { generateSignatureWithCoinbaseWallet, generateSignatureWithMetamask, + generateSignatureWithOKXWallet, getCoinbaseWalletIdentifier, getMetamaskIdentifier, + getOKXWalletIdentifier, windowNavigate, } from '../../utils'; import { getCaptchaToken, retrieveCaptchaInfo } from '../../utils/captcha'; @@ -295,6 +297,26 @@ export class SignUp extends BaseResource implements SignUpResource { }); }; + public authenticateWithOKXWallet = async ( + params?: SignUpAuthenticateWithWeb3Params & { + legalAccepted?: boolean; + }, + ): Promise => { + if (__BUILD_DISABLE_RHC__) { + clerkUnsupportedEnvironmentWarning('OKX Wallet'); + return this; + } + + const identifier = await getOKXWalletIdentifier(); + return this.authenticateWithWeb3({ + identifier, + generateSignature: generateSignatureWithOKXWallet, + unsafeMetadata: params?.unsafeMetadata, + strategy: 'web3_okx_wallet_signature', + legalAccepted: params?.legalAccepted, + }); + }; + public authenticateWithRedirect = async ({ redirectUrl, redirectUrlComplete, diff --git a/packages/clerk-js/src/ui/common/constants.ts b/packages/clerk-js/src/ui/common/constants.ts index 792a541203..c1ab8ea7e5 100644 --- a/packages/clerk-js/src/ui/common/constants.ts +++ b/packages/clerk-js/src/ui/common/constants.ts @@ -96,6 +96,10 @@ export const WEB3_PROVIDERS: Web3Providers = Object.freeze({ id: 'coinbase_wallet', name: 'Coinbase Wallet', }, + okx_wallet: { + id: 'okx_wallet', + name: 'OKX Wallet', + }, }); export function getWeb3ProviderData(name: Web3Provider): Web3ProviderData | undefined | null { diff --git a/packages/clerk-js/src/utils/injectedWeb3Providers.ts b/packages/clerk-js/src/utils/injectedWeb3Providers.ts index 091a7c2fbb..cd91072a55 100644 --- a/packages/clerk-js/src/utils/injectedWeb3Providers.ts +++ b/packages/clerk-js/src/utils/injectedWeb3Providers.ts @@ -1,4 +1,4 @@ -import type { MetamaskWeb3Provider } from '@clerk/types'; +import type { MetamaskWeb3Provider, OKXWalletWeb3Provider } from '@clerk/types'; //https://eips.ethereum.org/EIPS/eip-6963 @@ -27,12 +27,13 @@ interface EIP6963ProviderDetail { } type EIP6963AnnounceProviderEvent = CustomEvent; -type InjectedWeb3Provider = MetamaskWeb3Provider; +type InjectedWeb3Provider = MetamaskWeb3Provider | OKXWalletWeb3Provider; class InjectedWeb3Providers { #providers: EIP6963ProviderDetail[] = []; #providerIdMap: Record = { metamask: 'MetaMask', + okx_wallet: 'OKX Wallet', } as const; static #instance: InjectedWeb3Providers | null = null; diff --git a/packages/clerk-js/src/utils/web3.ts b/packages/clerk-js/src/utils/web3.ts index 018c0901f6..2bec8b08f8 100644 --- a/packages/clerk-js/src/utils/web3.ts +++ b/packages/clerk-js/src/utils/web3.ts @@ -48,6 +48,10 @@ export async function getCoinbaseWalletIdentifier(): Promise { return await getWeb3Identifier({ provider: 'coinbase_wallet' }); } +export async function getOKXWalletIdentifier(): Promise { + return await getWeb3Identifier({ provider: 'okx_wallet' }); +} + type GenerateSignatureParams = { identifier: string; nonce: string; @@ -61,6 +65,10 @@ export async function generateSignatureWithCoinbaseWallet(params: GenerateSignat return await generateWeb3Signature({ ...params, provider: 'coinbase_wallet' }); } +export async function generateSignatureWithOKXWallet(params: GenerateSignatureParams): Promise { + return await generateWeb3Signature({ ...params, provider: 'okx_wallet' }); +} + async function getEthereumProvider(provider: Web3Provider) { if (provider === 'coinbase_wallet') { const CoinbaseWalletSDK = await import('@coinbase/wallet-sdk').then(mod => mod.CoinbaseWalletSDK); diff --git a/packages/elements/src/internals/machines/sign-in/start.machine.ts b/packages/elements/src/internals/machines/sign-in/start.machine.ts index 4c266e6ad3..1379942055 100644 --- a/packages/elements/src/internals/machines/sign-in/start.machine.ts +++ b/packages/elements/src/internals/machines/sign-in/start.machine.ts @@ -32,6 +32,9 @@ export const SignInStartMachine = setup({ if (strategy === 'web3_coinbase_wallet_signature') { return parent.getSnapshot().context.clerk.client.signIn.authenticateWithCoinbaseWallet(); } + if (strategy === 'web3_okx_wallet_signature') { + return parent.getSnapshot().context.clerk.client.signIn.authenticateWithOKXWallet(); + } throw new ClerkElementsRuntimeError(`Unsupported Web3 strategy: ${strategy}`); }, ), diff --git a/packages/elements/src/internals/machines/sign-in/verification.machine.ts b/packages/elements/src/internals/machines/sign-in/verification.machine.ts index eeedd8c2de..bb04445e56 100644 --- a/packages/elements/src/internals/machines/sign-in/verification.machine.ts +++ b/packages/elements/src/internals/machines/sign-in/verification.machine.ts @@ -495,6 +495,17 @@ export const SignInFirstFactorMachine = SignInVerificationMachine.provide({ break; } + case 'web3_okx_wallet_signature': { + const signature = fields.get('signature')?.value as string | undefined; + assertIsDefined(signature, 'Web3 OKX Wallet signature'); + + attemptParams = { + strategy, + signature, + } satisfies Web3Attempt; + + break; + } default: throw new ClerkElementsRuntimeError(`Invalid strategy: ${strategy}`); } diff --git a/packages/elements/src/internals/machines/sign-up/start.machine.ts b/packages/elements/src/internals/machines/sign-up/start.machine.ts index 6e31223798..315207acc1 100644 --- a/packages/elements/src/internals/machines/sign-up/start.machine.ts +++ b/packages/elements/src/internals/machines/sign-up/start.machine.ts @@ -44,6 +44,9 @@ export const SignUpStartMachine = setup({ if (strategy === 'web3_coinbase_wallet_signature') { return parent.getSnapshot().context.clerk.client.signUp.authenticateWithCoinbaseWallet(); } + if (strategy === 'web3_okx_wallet_signature') { + return parent.getSnapshot().context.clerk.client.signUp.authenticateWithOKXWallet(); + } throw new ClerkElementsRuntimeError(`Unsupported Web3 strategy: ${strategy}`); }, ), diff --git a/packages/elements/src/react/hooks/use-third-party-provider.hook.ts b/packages/elements/src/react/hooks/use-third-party-provider.hook.ts index 565112b15b..820dd4f4ab 100644 --- a/packages/elements/src/react/hooks/use-third-party-provider.hook.ts +++ b/packages/elements/src/react/hooks/use-third-party-provider.hook.ts @@ -71,6 +71,10 @@ export const useThirdPartyProvider = < return ref.send({ type: 'AUTHENTICATE.WEB3', strategy: 'web3_coinbase_wallet_signature' }); } + if (provider === 'okx_wallet') { + return ref.send({ type: 'AUTHENTICATE.WEB3', strategy: 'web3_okx_wallet_signature' }); + } + return ref.send({ type: 'AUTHENTICATE.OAUTH', strategy: `oauth_${provider}` }); }, [provider, isProviderEnabled, isSaml, ref], diff --git a/packages/elements/src/react/utils/map-scope-to-strategy.ts b/packages/elements/src/react/utils/map-scope-to-strategy.ts index 7ecf03a3bc..fc3f2b9d7b 100644 --- a/packages/elements/src/react/utils/map-scope-to-strategy.ts +++ b/packages/elements/src/react/utils/map-scope-to-strategy.ts @@ -13,6 +13,9 @@ export function mapScopeToStrategy(scope: T): if (scope === 'provider:coinbase_wallet') { return 'web3_coinbase_wallet_signature'; } + if (scope === 'provider:okx_wallet') { + return 'web3_okx_wallet_signature'; + } if (scope === 'provider:saml') { return 'saml'; diff --git a/packages/react/src/isomorphicClerk.ts b/packages/react/src/isomorphicClerk.ts index 6ae4033f8b..ebf6990ddb 100644 --- a/packages/react/src/isomorphicClerk.ts +++ b/packages/react/src/isomorphicClerk.ts @@ -9,6 +9,7 @@ import type { AuthenticateWithCoinbaseWalletParams, AuthenticateWithGoogleOneTapParams, AuthenticateWithMetamaskParams, + AuthenticateWithOKXWalletParams, Clerk, ClerkAuthenticateWithWeb3Params, ClerkOptions, @@ -128,6 +129,7 @@ type IsomorphicLoadedClerk = Without< // TODO: Align Promise unknown authenticateWithMetamask: (params: AuthenticateWithMetamaskParams) => Promise; authenticateWithCoinbaseWallet: (params: AuthenticateWithCoinbaseWalletParams) => Promise; + authenticateWithOKXWallet: (params: AuthenticateWithOKXWalletParams) => Promise; authenticateWithWeb3: (params: ClerkAuthenticateWithWeb3Params) => Promise; authenticateWithGoogleOneTap: ( params: AuthenticateWithGoogleOneTapParams, @@ -1139,6 +1141,15 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { } }; + authenticateWithOKXWallet = async (params: AuthenticateWithOKXWalletParams): Promise => { + const callback = () => this.clerkjs?.authenticateWithOKXWallet(params); + if (this.clerkjs && this.#loaded) { + return callback() as Promise; + } else { + this.premountMethodCalls.set('authenticateWithOKXWallet', callback); + } + }; + authenticateWithWeb3 = async (params: ClerkAuthenticateWithWeb3Params): Promise => { const callback = () => this.clerkjs?.authenticateWithWeb3(params); if (this.clerkjs && this.#loaded) { diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts index 9163ef2ec0..37fc952139 100644 --- a/packages/types/src/clerk.ts +++ b/packages/types/src/clerk.ts @@ -559,6 +559,11 @@ export interface Clerk { */ authenticateWithCoinbaseWallet: (params?: AuthenticateWithCoinbaseWalletParams) => Promise; + /** + * Authenticates user using their OKX Wallet browser extension + */ + authenticateWithOKXWallet: (params?: AuthenticateWithOKXWalletParams) => Promise; + /** * Authenticates user using their Web3 Wallet browser extension */ @@ -1385,6 +1390,14 @@ export interface AuthenticateWithCoinbaseWalletParams { legalAccepted?: boolean; } +export interface AuthenticateWithOKXWalletParams { + customNavigate?: (to: string) => Promise; + redirectUrl?: string; + signUpContinueUrl?: string; + unsafeMetadata?: SignUpUnsafeMetadata; + legalAccepted?: boolean; +} + export interface AuthenticateWithGoogleOneTapParams { token: string; legalAccepted?: boolean; diff --git a/packages/types/src/signIn.ts b/packages/types/src/signIn.ts index 136150917e..c19fbf87e5 100644 --- a/packages/types/src/signIn.ts +++ b/packages/types/src/signIn.ts @@ -103,6 +103,8 @@ export interface SignInResource extends ClerkResource { authenticateWithCoinbaseWallet: () => Promise; + authenticateWithOKXWallet: () => Promise; + authenticateWithPasskey: (params?: AuthenticateWithPasskeyParams) => Promise; createEmailLinkFlow: () => CreateEmailLinkFlowReturn; diff --git a/packages/types/src/signUp.ts b/packages/types/src/signUp.ts index d608d452fb..b98fec79a2 100644 --- a/packages/types/src/signUp.ts +++ b/packages/types/src/signUp.ts @@ -99,6 +99,7 @@ export interface SignUpResource extends ClerkResource { authenticateWithMetamask: (params?: SignUpAuthenticateWithWeb3Params) => Promise; authenticateWithCoinbaseWallet: (params?: SignUpAuthenticateWithWeb3Params) => Promise; + authenticateWithOKXWallet: (params?: SignUpAuthenticateWithWeb3Params) => Promise; } export type SignUpStatus = 'missing_requirements' | 'complete' | 'abandoned'; diff --git a/packages/types/src/web3.ts b/packages/types/src/web3.ts index 78aede815c..deab9048d2 100644 --- a/packages/types/src/web3.ts +++ b/packages/types/src/web3.ts @@ -8,8 +8,9 @@ export interface Web3ProviderData { export type MetamaskWeb3Provider = 'metamask'; export type CoinbaseWalletWeb3Provider = 'coinbase_wallet'; +export type OKXWalletWeb3Provider = 'okx_wallet'; -export type Web3Provider = MetamaskWeb3Provider | CoinbaseWalletWeb3Provider; +export type Web3Provider = MetamaskWeb3Provider | CoinbaseWalletWeb3Provider | OKXWalletWeb3Provider; export const WEB3_PROVIDERS: Web3ProviderData[] = [ { @@ -22,6 +23,11 @@ export const WEB3_PROVIDERS: Web3ProviderData[] = [ strategy: 'web3_coinbase_wallet_signature', name: 'Coinbase Wallet', }, + { + provider: 'okx_wallet', + strategy: 'web3_okx_wallet_signature', + name: 'OKX Wallet', + }, ]; interface getWeb3ProviderDataProps {