From 45ba5a3ae19764eaa65864ed6a9c9fb6c8d15ddf Mon Sep 17 00:00:00 2001 From: ikethirdweb Date: Tue, 17 Oct 2023 11:13:54 -0400 Subject: [PATCH 01/13] custom jwt flow --- .../embedded-wallet/embedded-connector.ts | 41 +++++++++++++-- .../embedded-wallet/embedded/auth.ts | 50 ++++++++++++++++++- .../embedded/helpers/constants.ts | 1 + .../connectors/embedded-wallet/types.ts | 7 +++ .../wallets/embedded/embedded-wallet.tsx | 1 - 5 files changed, 94 insertions(+), 6 deletions(-) diff --git a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded-connector.ts b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded-connector.ts index d481f5ec446..23330a71c65 100644 --- a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded-connector.ts +++ b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded-connector.ts @@ -1,4 +1,5 @@ import { + AuthOptions, EmbeddedWalletConnectionArgs, EmbeddedWalletConnectorOptions, OauthOption, @@ -7,7 +8,12 @@ import type { Chain } from "@thirdweb-dev/chains"; import { Connector, normalizeChainId } from "@thirdweb-dev/wallets"; import { providers, Signer } from "ethers"; import { utils } from "ethers"; -import { sendEmailOTP, socialLogin, validateEmailOTP } from "./embedded/auth"; +import { + customJwt, + sendEmailOTP, + socialLogin, + validateEmailOTP, +} from "./embedded/auth"; import { getEthersSigner } from "./embedded/signer"; import { logoutUser } from "./embedded/helpers/auth/logout"; import { @@ -30,11 +36,16 @@ export class EmbeddedWalletConnector extends Connector, + ) { const connected = await this.isConnected(); if (!connected) { - throw new Error("Not connected"); + // const; } if (options?.chainId) { @@ -114,6 +125,30 @@ export class EmbeddedWalletConnector extends Connector { clearConnectedEmail(); await logoutUser(this.options.clientId); diff --git a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/auth.ts b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/auth.ts index 48209d3c139..40431bf86f9 100644 --- a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/auth.ts +++ b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/auth.ts @@ -20,8 +20,11 @@ import { isDeviceSharePresentForUser } from "./helpers/storage/local"; import { getCognitoUser, setCognitoUser } from "./helpers/storage/state"; import { SendEmailOtpReturnType } from "@thirdweb-dev/wallets"; import { InAppBrowser } from "react-native-inappbrowser-reborn"; -import { OauthOption } from "../types"; -import { ROUTE_HEADLESS_GOOGLE_LOGIN } from "./helpers/constants"; +import { AuthOptions, OauthOption } from "../types"; +import { + ROUTE_AUTH_JWT_CALLBACK, + ROUTE_HEADLESS_GOOGLE_LOGIN, +} from "./helpers/constants"; export async function sendEmailOTP( email: string, @@ -196,3 +199,46 @@ export async function socialLogin(oauthOptions: OauthOption, clientId: string) { ); } } + +export async function customJwt(authOptions: AuthOptions, clientId: string) { + const { jwt, authProvider } = authOptions; + + const resp = await fetch(ROUTE_AUTH_JWT_CALLBACK, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + jwt, + authProvider, + developerClientId: clientId, + }), + }); + if (!resp.ok) { + const { error } = await resp.json(); + throw new Error(`JWT authentication error: ${error} `); + } + + try { + const { verifiedToken, verifiedTokenJwtString } = await resp.json(); + + const toStoreToken: AuthStoredTokenWithCookieReturnType["storedToken"] = { + jwtToken: verifiedToken.rawToken, + authProvider: verifiedToken.authProvider, + authDetails: { + ...verifiedToken.authDetails, + email: verifiedToken.authDetails.email, + }, + developerClientId: verifiedToken.developerClientId, + cookieString: verifiedTokenJwtString, + shouldStoreCookieString: false, + isNewUser: verifiedToken.isNewUser, + }; + + await postPaperAuth(toStoreToken, clientId); + + return { verifiedToken, email: verifiedToken.authDetails.email }; + } catch (e) { + throw new Error( + `Malformed response from post authentication: ${JSON.stringify(e)}`, + ); + } +} diff --git a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/helpers/constants.ts b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/helpers/constants.ts index d9dd68f9aeb..7ce3ca771b9 100644 --- a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/helpers/constants.ts +++ b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/helpers/constants.ts @@ -29,6 +29,7 @@ export const ROUTE_INIT_RECOVERY_CODE_FREE_WALLET = `${ROUTE_2022_08_12_API_BASE export const ROUTE_STORE_USER_SHARES = `${ROUTE_2022_08_12_API_BASE_PATH}/embedded-wallet/store-shares`; export const ROUTE_GET_USER_SHARES = `${ROUTE_2022_08_12_API_BASE_PATH}/embedded-wallet/get-shares`; export const ROUTE_VERIFY_THIRDWEB_CLIENT_ID = `${ROUTE_2022_08_12_API_BASE_PATH}/embedded-wallet/verify-thirdweb-client-id`; +export const ROUTE_AUTH_JWT_CALLBACK = `${ROUTE_2022_08_12_API_BASE_PATH}/embedded-wallet/jwt-callback`; export const ROUTE_HEADLESS_GOOGLE_LOGIN = `${BASE_URL}/api/2022-08-12/embedded-wallet/headless-login-link`; diff --git a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/types.ts b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/types.ts index 4e24868830c..3cdbec88880 100644 --- a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/types.ts +++ b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/types.ts @@ -47,6 +47,13 @@ export interface EmbeddedWalletConnectorOptions { styles?: PaperConstructorType["styles"]; } +export interface AuthOptions { + jwt: string; + authProvider: string; + recoveryCode: string; +} + export interface EmbeddedWalletConnectionArgs { email?: string; + authOptions?: AuthOptions; } diff --git a/packages/react-native/src/evm/wallets/wallets/embedded/embedded-wallet.tsx b/packages/react-native/src/evm/wallets/wallets/embedded/embedded-wallet.tsx index b97917f6fbe..0b7cc9bb342 100644 --- a/packages/react-native/src/evm/wallets/wallets/embedded/embedded-wallet.tsx +++ b/packages/react-native/src/evm/wallets/wallets/embedded/embedded-wallet.tsx @@ -14,7 +14,6 @@ export type EmbeddedWalletConfig = { // @default true - set false to disable email?: boolean; - // @default { providers: ['google'] } - set false to disable oauthOptions?: | { providers: OAuthProvider[]; From b360aaadd9332dd57612fbd4c051a498a6f74c74 Mon Sep 17 00:00:00 2001 From: ikethirdweb Date: Tue, 17 Oct 2023 15:35:32 -0400 Subject: [PATCH 02/13] params --- .../src/evm/wallets/connectors/embedded-wallet/types.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/types.ts b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/types.ts index 3cdbec88880..fe288f98cca 100644 --- a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/types.ts +++ b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/types.ts @@ -48,9 +48,9 @@ export interface EmbeddedWalletConnectorOptions { } export interface AuthOptions { - jwt: string; - authProvider: string; - recoveryCode: string; + jwtToken: string; + authProvider: string; // AuthProvider.CustomJwt + recoveryCode?: string; } export interface EmbeddedWalletConnectionArgs { From 28345e4590b8378ccfe22b4d1b985470341d9b73 Mon Sep 17 00:00:00 2001 From: ikethirdweb Date: Wed, 18 Oct 2023 16:39:48 -0400 Subject: [PATCH 03/13] jwt --- .../embedded-wallet/embedded-connector.ts | 48 +++++++++---- .../embedded-wallet/embedded/auth.ts | 6 +- .../connectors/embedded-wallet/jwks.ts | 10 +++ .../connectors/embedded-wallet/types.ts | 25 +++++-- .../wallets/embedded/EmbeddedConnectionUI.tsx | 10 ++- .../embedded/EmbeddedSocialConnection.tsx | 9 ++- .../wallets/embedded/EnterPassword.tsx | 71 +++++++++++++++++++ 7 files changed, 149 insertions(+), 30 deletions(-) create mode 100644 packages/react-native/src/evm/wallets/connectors/embedded-wallet/jwks.ts create mode 100644 packages/react-native/src/evm/wallets/wallets/embedded/EnterPassword.tsx diff --git a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded-connector.ts b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded-connector.ts index 23330a71c65..cd2dfa91e53 100644 --- a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded-connector.ts +++ b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded-connector.ts @@ -21,6 +21,7 @@ import { getConnectedEmail, saveConnectedEmail, } from "./embedded/helpers/storage/local"; +import { AuthProvider } from "@paperxyz/embedded-wallet-service-sdk"; export class EmbeddedWalletConnector extends Connector { private options: EmbeddedWalletConnectorOptions; @@ -36,16 +37,38 @@ export class EmbeddedWalletConnector extends Connector, - ) { + async connect(options?: { chainId?: number } & EmbeddedWalletConnectionArgs) { const connected = await this.isConnected(); - if (!connected) { - // const; + if (connected) { + return this.getAddress(); + } + + switch (options?.loginType) { + case "headless_google_oauth": + { + await socialLogin( + { + provider: AuthProvider.GOOGLE, + redirectUrl: options.redirectUrl, + }, + this.options.clientId, + ); + } + break; + case "headless_email_otp_verification": { + await this.validateEmailOtp(options.otp); + break; + } + case "custom_jwt": { + await this.customJwt({ + jwtToken: options.jwtToken, + encryptionKey: options.encryptionKey, + }); + break; + } + default: + throw new Error("Invalid login type"); } if (options?.chainId) { @@ -125,7 +148,7 @@ export class EmbeddedWalletConnector extends Connector> = ({ setTimeout(() => { (selectionData.emailWallet as EmbeddedWallet) - .validateEmailOTP(otp) + .connect({ + loginType: "headless_email_otp_verification", + otp, + email: selectionData.email, + }) .then(async (response) => { - if (response?.success) { + if (response) { if (onLocallyConnected) { onLocallyConnected(selectionData.emailWallet); } else { @@ -75,7 +79,7 @@ export const EmbeddedConnectionUI: React.FC> = ({ } } else { clearCode(); - setErrorMessage(response?.error || "Error validating the code"); + setErrorMessage(response || "Error validating the code"); setCheckingOtp(false); setFocusedIndex(undefined); } diff --git a/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSocialConnection.tsx b/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSocialConnection.tsx index f2492e7f84d..15bf8751d48 100644 --- a/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSocialConnection.tsx +++ b/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSocialConnection.tsx @@ -23,9 +23,12 @@ export const EmbeddedSocialConnection: React.FC< setTimeout(() => { (selectionData.emailWallet as EmbeddedWallet) - .socialLogin(selectionData?.oauthOptions) + .connect({ + loginType: "headless_google_oauth", + redirectUrl: selectionData.oauthOptions?.redirectUrl, + }) .then(async (response) => { - if (response?.success) { + if (response) { if (onLocallyConnected) { onLocallyConnected(selectionData.emailWallet); } else { @@ -34,7 +37,7 @@ export const EmbeddedSocialConnection: React.FC< } } else { setErrorMessage( - response?.error || "Error login in. Please try again later.", + response || "Error login in. Please try again later.", ); } }) diff --git a/packages/react-native/src/evm/wallets/wallets/embedded/EnterPassword.tsx b/packages/react-native/src/evm/wallets/wallets/embedded/EnterPassword.tsx new file mode 100644 index 00000000000..5b687939a3f --- /dev/null +++ b/packages/react-native/src/evm/wallets/wallets/embedded/EnterPassword.tsx @@ -0,0 +1,71 @@ +import { useAddress } from "@thirdweb-dev/react-core"; +import React, { useState } from "react"; +import { ActivityIndicator } from "react-native"; +import { ConnectWalletHeader } from "../../../components/ConnectWalletFlow/ConnectingWallet/ConnectingWalletHeader"; +import { Box, BaseButton, Text } from "../../../components/base"; +import { shortenWalletAddress } from "../../../utils/addresses"; +import { useGlobalTheme } from "../../../providers/ui-context-provider"; +import { PasswordInput } from "../../../components/PasswordInput"; + +export type EnterPasswordProps = { + goBack: () => void; + close: () => void; + email: string; +}; + +export const EnterPassword = ({ close, goBack, email }: EnterPasswordProps) => { + const theme = useGlobalTheme(); + const [errorMessage, setErrorMessage] = useState(); + const [checkingPass, setCheckingPass] = useState(false); + const [password, setPassword] = useState(""); + + const onNextPress = async () => {}; + + const onForgotPress = () => {}; + + return ( + + Enter password} + subHeaderText={`Enter the password for email: ${email}`} + onBackPress={goBack} + onClose={close} + /> + + + + + + Forgot password + + + + {errorMessage ? ( + + {errorMessage} + + ) : ( + + )} + + {checkingPass ? ( + + ) : ( + + Next + + )} + + + ); +}; From 8471b2ecc33533941207a363c644b1929f930502 Mon Sep 17 00:00:00 2001 From: ikethirdweb Date: Wed, 18 Oct 2023 17:46:20 -0400 Subject: [PATCH 04/13] recovery --- .../wallets/embedded/AccountRecovery.tsx | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 packages/react-native/src/evm/wallets/wallets/embedded/AccountRecovery.tsx diff --git a/packages/react-native/src/evm/wallets/wallets/embedded/AccountRecovery.tsx b/packages/react-native/src/evm/wallets/wallets/embedded/AccountRecovery.tsx new file mode 100644 index 00000000000..5c2c268f89e --- /dev/null +++ b/packages/react-native/src/evm/wallets/wallets/embedded/AccountRecovery.tsx @@ -0,0 +1,91 @@ +import { useAddress } from "@thirdweb-dev/react-core"; +import React, { useState } from "react"; +import { ActivityIndicator } from "react-native"; +import { ConnectWalletHeader } from "../../../components/ConnectWalletFlow/ConnectingWallet/ConnectingWalletHeader"; +import { Box, BaseButton, Text, TextInput } from "../../../components/base"; +import { shortenWalletAddress } from "../../../utils/addresses"; +import { useGlobalTheme } from "../../../providers/ui-context-provider"; +import { PasswordInput } from "../../../components/PasswordInput"; + +export type EnterPasswordProps = { + goBack: () => void; + close: () => void; + email: string; +}; + +export const AccountRecovery = ({ + close, + goBack, + email, +}: EnterPasswordProps) => { + const theme = useGlobalTheme(); + const [errorMessage, setErrorMessage] = useState(); + const [checkingPass, setCheckingPass] = useState(false); + const [password, setPassword] = useState(""); + + const onNextPress = async () => {}; + + const onForgotPress = () => {}; + + return ( + + Enter account recovery code + } + subHeaderText={ + "You should have a copy of this in the email address associated with your account" + } + onBackPress={goBack} + onClose={close} + /> + + {errorMessage ? ( + + {errorMessage} + + ) : ( + + )} + + {checkingPass ? ( + + ) : ( + + Next + + )} + + + ); +}; From 36e5bc1e6a39eb350b801d2c84c1bdb0c1c54527 Mon Sep 17 00:00:00 2001 From: ikethirdweb Date: Wed, 18 Oct 2023 19:08:38 -0400 Subject: [PATCH 05/13] recovery code ui --- packages/react-native/package.json | 11 +- .../embedded-wallet/embedded-connector.ts | 4 +- .../connectors/embedded-wallet/types.ts | 4 +- .../wallets/embedded/AccountRecovery.tsx | 32 +++--- .../wallets/embedded/BackupAccount.tsx | 108 ++++++++++++++++++ .../wallets/embedded/EmbeddedConnectionUI.tsx | 2 +- .../embedded/EmbeddedSocialConnection.tsx | 2 +- .../wallets/embedded/EnterPassword.tsx | 100 +++++++++++----- pnpm-lock.yaml | 86 ++++++-------- 9 files changed, 237 insertions(+), 112 deletions(-) create mode 100644 packages/react-native/src/evm/wallets/wallets/embedded/BackupAccount.tsx diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 9d4d58eeb4f..cf26d28226f 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -30,6 +30,7 @@ "@magic-sdk/provider": "17.2.0", "@magic-sdk/react-native-bare": "^18.5.0", "@paperxyz/embedded-wallet-service-sdk": "^1.2.4", + "@react-native-community/checkbox": "^0.5.16", "@shopify/restyle": "^2.4.2", "@tanstack/react-query": "^4.33.0", "@thirdweb-dev/chains": "workspace:*", @@ -82,15 +83,15 @@ "typescript": "^5.1.6" }, "optionalDependencies": { + "amazon-cognito-identity-js": "^6.3.3", + "react-native-aes-gcm-crypto": "^0.2.2", "react-native-camera": "^4.2.1", "react-native-device-info": "10.6.0", - "react-native-safe-area-context": "4.5.3", - "react-native-webview": "12.1.0", - "amazon-cognito-identity-js": "^6.3.3", + "react-native-inappbrowser-reborn": "^3.7.0", "react-native-quick-base64": "^2.0.7", "react-native-quick-crypto": "^0.6.1", - "react-native-aes-gcm-crypto": "^0.2.2", - "react-native-inappbrowser-reborn": "^3.7.0" + "react-native-safe-area-context": "4.5.3", + "react-native-webview": "12.1.0" }, "peerDependencies": { "ethers": ">=5.5.1 <6", diff --git a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded-connector.ts b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded-connector.ts index cd2dfa91e53..e12ed90f787 100644 --- a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded-connector.ts +++ b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded-connector.ts @@ -45,7 +45,7 @@ export class EmbeddedWalletConnector extends Connector void; close: () => void; - email: string; }; -export const AccountRecovery = ({ - close, - goBack, - email, -}: EnterPasswordProps) => { +export const AccountRecovery = ({ close, goBack }: EnterPasswordProps) => { const theme = useGlobalTheme(); const [errorMessage, setErrorMessage] = useState(); - const [checkingPass, setCheckingPass] = useState(false); - const [password, setPassword] = useState(""); + const [checkingRecoveryCode, setCheckingRecoveryCode] = + useState(false); + const [recoveryCode, setRecoveryCode] = useState(""); - const onNextPress = async () => {}; - - const onForgotPress = () => {}; + const onNextPress = async () => { + setCheckingRecoveryCode(true); + // Call enter recovery code + setErrorMessage("test"); + }; return ( @@ -41,9 +36,9 @@ export const AccountRecovery = ({ /> - {checkingPass ? ( + {checkingRecoveryCode ? ( ) : ( diff --git a/packages/react-native/src/evm/wallets/wallets/embedded/BackupAccount.tsx b/packages/react-native/src/evm/wallets/wallets/embedded/BackupAccount.tsx new file mode 100644 index 00000000000..83bdd6a2a59 --- /dev/null +++ b/packages/react-native/src/evm/wallets/wallets/embedded/BackupAccount.tsx @@ -0,0 +1,108 @@ +import React, { useEffect, useState } from "react"; +import { ConnectWalletHeader } from "../../../components/ConnectWalletFlow/ConnectingWallet/ConnectingWalletHeader"; +import { Box, BaseButton, Text, Toast } from "../../../components/base"; +import { useGlobalTheme } from "../../../providers/ui-context-provider"; +import CopyIcon from "../../../assets/copy"; +import CheckBox from "@react-native-community/checkbox"; +import * as Clipboard from "expo-clipboard"; + +export type EnterPasswordProps = { + goBack: () => void; + close: () => void; +}; + +export const AccountRecovery = ({ close, goBack }: EnterPasswordProps) => { + const theme = useGlobalTheme(); + const [toggleCheckBox, setToggleCheckBox] = useState(false); + const [errorMessage] = useState(); + const [codeCopied, setCodeCopied] = useState(false); + + useEffect(() => { + const timeout = setTimeout(() => { + if (codeCopied) { + setCodeCopied(false); + } + }, 2000); + + return () => clearTimeout(timeout); + }, [codeCopied]); + + const onNextPress = async () => {}; + + const onCodeCopyPress = async (code: string) => { + console.log("code", code); + + await Clipboard.setStringAsync(code); + setCodeCopied(true); + }; + + const codes = ["testing", "testing2", "testing3", "testing4", "testing5"]; + + return ( + + Backup your account} + subHeaderText={ + "Copy or download these codes and keep them safe. You will need these to recover access to your account if you forget your password. These have also been sent to the email address associated with your account" + } + onBackPress={goBack} + onClose={close} + /> + + {codes.map((code) => ( + { + onCodeCopyPress(code); + }} + p="md" + > + {code} + + + ))} + + + {errorMessage ? ( + + {errorMessage} + + ) : ( + + )} + + + setToggleCheckBox(newValue)} + /> + + Learn More + + + + + Next + + + + {codeCopied === true ? ( + + ) : null} + + ); +}; diff --git a/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedConnectionUI.tsx b/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedConnectionUI.tsx index fc3875ff8b5..e0e31752662 100644 --- a/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedConnectionUI.tsx +++ b/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedConnectionUI.tsx @@ -65,7 +65,7 @@ export const EmbeddedConnectionUI: React.FC> = ({ setTimeout(() => { (selectionData.emailWallet as EmbeddedWallet) .connect({ - loginType: "headless_email_otp_verification", + loginType: "email_otp_verification", otp, email: selectionData.email, }) diff --git a/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSocialConnection.tsx b/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSocialConnection.tsx index 15bf8751d48..bff69b7859a 100644 --- a/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSocialConnection.tsx +++ b/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSocialConnection.tsx @@ -24,7 +24,7 @@ export const EmbeddedSocialConnection: React.FC< setTimeout(() => { (selectionData.emailWallet as EmbeddedWallet) .connect({ - loginType: "headless_google_oauth", + loginType: "google_oauth", redirectUrl: selectionData.oauthOptions?.redirectUrl, }) .then(async (response) => { diff --git a/packages/react-native/src/evm/wallets/wallets/embedded/EnterPassword.tsx b/packages/react-native/src/evm/wallets/wallets/embedded/EnterPassword.tsx index 5b687939a3f..297957654bb 100644 --- a/packages/react-native/src/evm/wallets/wallets/embedded/EnterPassword.tsx +++ b/packages/react-native/src/evm/wallets/wallets/embedded/EnterPassword.tsx @@ -1,9 +1,7 @@ -import { useAddress } from "@thirdweb-dev/react-core"; import React, { useState } from "react"; import { ActivityIndicator } from "react-native"; import { ConnectWalletHeader } from "../../../components/ConnectWalletFlow/ConnectingWallet/ConnectingWalletHeader"; import { Box, BaseButton, Text } from "../../../components/base"; -import { shortenWalletAddress } from "../../../utils/addresses"; import { useGlobalTheme } from "../../../providers/ui-context-provider"; import { PasswordInput } from "../../../components/PasswordInput"; @@ -11,39 +9,68 @@ export type EnterPasswordProps = { goBack: () => void; close: () => void; email: string; + type: "create_password" | "enter_password"; }; -export const EnterPassword = ({ close, goBack, email }: EnterPasswordProps) => { +export const EnterPassword = ({ + close, + goBack, + email, + type, +}: EnterPasswordProps) => { const theme = useGlobalTheme(); const [errorMessage, setErrorMessage] = useState(); const [checkingPass, setCheckingPass] = useState(false); const [password, setPassword] = useState(""); - const onNextPress = async () => {}; + const isCreatePassword = type === "create_password"; + + const onNextPress = async () => { + setCheckingPass(true); + if (isCreatePassword) { + // Call create password + console.log("password", password); + } else { + // Call enter password + setErrorMessage("test"); + } + }; const onForgotPress = () => {}; + const onLearnMorePress = () => {}; + return ( Enter password} - subHeaderText={`Enter the password for email: ${email}`} + middleContent={ + + {isCreatePassword ? "Create password" : "Enter password"} + + } + subHeaderText={ + isCreatePassword + ? "Set a password for your account" + : `Enter the password for email: ${email}` + } onBackPress={goBack} onClose={close} /> - - - Forgot password - - + {isCreatePassword ? null : ( + + + Forgot password + + + )} {errorMessage ? ( @@ -52,20 +79,33 @@ export const EnterPassword = ({ close, goBack, email }: EnterPasswordProps) => { ) : ( )} - - {checkingPass ? ( - - ) : ( - - Next - - )} - + + {isCreatePassword ? ( + + + Learn More + + + ) : null} + + {checkingPass ? ( + + ) : ( + + Next + + )} + + ); }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 63dcee7bd33..c1db17b9706 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -629,7 +629,7 @@ importers: dependencies: '@tanstack/react-query': specifier: ^4.33.0 - version: 4.33.0(react-native@0.71.11)(react@18.2.0) + version: 4.33.0(react-dom@18.2.0)(react@18.2.0) '@thirdweb-dev/auth': specifier: workspace:* version: link:../auth @@ -757,6 +757,9 @@ importers: '@paperxyz/embedded-wallet-service-sdk': specifier: ^1.2.4 version: 1.2.4 + '@react-native-community/checkbox': + specifier: ^0.5.16 + version: 0.5.16(react-native@0.71.11)(react@18.2.0) '@shopify/restyle': specifier: ^2.4.2 version: 2.4.2(react-native@0.71.11)(react@18.2.0) @@ -1438,7 +1441,7 @@ importers: version: 4.1.1 abitype: specifier: ^0.2.5 - version: 0.2.5(typescript@5.1.6) + version: 0.2.5(typescript@5.1.6)(zod@3.22.3) babel-plugin-transform-inline-environment-variables: specifier: ^0.4.4 version: 0.4.4 @@ -1587,7 +1590,7 @@ packages: '@ethersproject/properties': 5.7.0 '@ethersproject/providers': 5.7.2 '@types/debug': 4.1.7 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) ethers: 5.7.2 transitivePeerDependencies: - bufferutil @@ -1602,7 +1605,7 @@ packages: '@ethersproject/abi': 5.7.0 '@ethersproject/providers': 5.7.2 '@openzeppelin/contracts': 4.9.3 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) ethers: 5.7.2 transitivePeerDependencies: - bufferutil @@ -4728,7 +4731,7 @@ packages: '@babel/traverse': 7.22.8 '@babel/types': 7.22.5 convert-source-map: 1.9.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 7.5.4 @@ -6233,7 +6236,7 @@ packages: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.22.7 '@babel/types': 7.22.5 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -7118,7 +7121,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) espree: 9.6.0 globals: 13.20.0 ignore: 5.2.4 @@ -7857,7 +7860,7 @@ packages: engines: {node: '>=10.10.0'} dependencies: '@humanwhocodes/object-schema': 1.2.1 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -8672,7 +8675,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@types/debug': 4.1.7 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) semver: 7.5.4 superstruct: 1.0.3 transitivePeerDependencies: @@ -10297,6 +10300,20 @@ packages: merge-options: 3.0.4 react-native: 0.71.11(@babel/core@7.22.9)(@babel/preset-env@7.22.9)(react@18.2.0) + /@react-native-community/checkbox@0.5.16(react-native@0.71.11)(react@18.2.0): + resolution: {integrity: sha512-j4fmWe77EAayGnKJ52BljlN8apLT3xjxG/pJOA6HZ4ew63FiXmnY7VtxTzmvDKgSPrETdQc2lmx5mdXTAufJnw==} + peerDependencies: + react: '*' + react-native: '>= 0.62' + react-native-windows: '>=0.62' + peerDependenciesMeta: + react-native-windows: + optional: true + dependencies: + react: 18.2.0 + react-native: 0.71.11(@babel/core@7.22.9)(@babel/preset-env@7.22.9)(react@18.2.0) + dev: false + /@react-native-community/cli-clean@10.1.1: resolution: {integrity: sha512-iNsrjzjIRv9yb5y309SWJ8NDHdwYtnCpmxZouQDyOljUdC9MwdZ4ChbtA4rwQyAwgOVfS9F/j56ML3Cslmvrxg==} dependencies: @@ -13251,19 +13268,6 @@ packages: resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} dev: false - /abitype@0.2.5(typescript@5.1.6): - resolution: {integrity: sha512-t1iiokWYpkrziu4WL2Gb6YdGvaP9ZKs7WnA39TI8TsW2E99GVRgDPW/xOKhzoCdyxOYt550CNYEFluCwGaFHaA==} - engines: {pnpm: '>=7'} - peerDependencies: - typescript: '>=4.7.4' - zod: '>=3.19.1' - peerDependenciesMeta: - zod: - optional: true - dependencies: - typescript: 5.1.6 - dev: true - /abitype@0.2.5(typescript@5.1.6)(zod@3.22.3): resolution: {integrity: sha512-t1iiokWYpkrziu4WL2Gb6YdGvaP9ZKs7WnA39TI8TsW2E99GVRgDPW/xOKhzoCdyxOYt550CNYEFluCwGaFHaA==} engines: {pnpm: '>=7'} @@ -13276,7 +13280,6 @@ packages: dependencies: typescript: 5.1.6 zod: 3.22.3 - dev: false /abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} @@ -13393,7 +13396,7 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -13401,7 +13404,7 @@ packages: resolution: {integrity: sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==} engines: {node: '>= 8.0.0'} dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) depd: 2.0.0 humanize-ms: 1.2.1 transitivePeerDependencies: @@ -13867,7 +13870,7 @@ packages: /axios@0.21.4: resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==} dependencies: - follow-redirects: 1.15.2 + follow-redirects: 1.15.2(debug@4.3.4) transitivePeerDependencies: - debug dev: false @@ -13883,7 +13886,7 @@ packages: /axios@0.27.2: resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} dependencies: - follow-redirects: 1.15.2 + follow-redirects: 1.15.2(debug@4.3.4) form-data: 4.0.0 transitivePeerDependencies: - debug @@ -15968,17 +15971,6 @@ packages: dependencies: ms: 2.1.3 - /debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.2 - /debug@4.3.4(supports-color@8.1.1): resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -17560,7 +17552,7 @@ packages: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.0 @@ -17792,7 +17784,7 @@ packages: asn1.js: 5.4.1 aws-sdk: 2.1336.0 bn.js: 5.2.1 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) ethers: 5.7.2 transitivePeerDependencies: - bufferutil @@ -18546,16 +18538,6 @@ packages: resolution: {integrity: sha512-G4oeDNpNGyIrweF9EnoHatncAihMT0tQgV6NMdyM5I7fhrz9Pr13PJ2KLQ673O4wj9KooTdBpeeYHdDNAQoyyw==} engines: {node: '>=0.4.0'} - /follow-redirects@1.15.2: - resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - dev: false - /follow-redirects@1.15.2(debug@4.3.4): resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} engines: {node: '>=4.0'} @@ -19596,7 +19578,7 @@ packages: engines: {node: '>= 6'} dependencies: agent-base: 6.0.2 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -26371,7 +26353,7 @@ packages: resolution: {integrity: sha512-wfI3pk7EE80lCIXprqh7ym48IHYdwmAAzESdbU8Q9l7pnRCk9LEhpbOTNKjz6FARLm/Bl5m+4F0ABxOkYUujSQ==} engines: {node: '>=12'} dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) extend: 3.0.2 transitivePeerDependencies: - supports-color From 6aefb6aa0e450d9c0d04875ec3e66b803ba5b05b Mon Sep 17 00:00:00 2001 From: ikethirdweb Date: Thu, 19 Oct 2023 13:22:23 -0400 Subject: [PATCH 06/13] custom jwt working --- .../ChooseWallet/ChooseWallet.tsx | 4 +- .../embedded-wallet/embedded/auth.ts | 10 +++-- .../embedded/helpers/auth/middleware.ts | 42 +++++++++++++++++++ .../connectors/embedded-wallet/types.ts | 5 +++ .../wallets/embedded/EmbeddedSelectionUI.tsx | 8 +++- .../wallets/embedded/embedded-wallet.tsx | 5 ++- 6 files changed, 68 insertions(+), 6 deletions(-) diff --git a/packages/react-native/src/evm/components/ConnectWalletFlow/ChooseWallet/ChooseWallet.tsx b/packages/react-native/src/evm/components/ConnectWalletFlow/ChooseWallet/ChooseWallet.tsx index 273f390d499..055bc8ab0de 100644 --- a/packages/react-native/src/evm/components/ConnectWalletFlow/ChooseWallet/ChooseWallet.tsx +++ b/packages/react-native/src/evm/components/ConnectWalletFlow/ChooseWallet/ChooseWallet.tsx @@ -41,7 +41,9 @@ export function ChooseWallet({ const guestWallet = wallets.find((w) => w.id === walletIds.localWallet); const emailWallet = wallets.find( - (w) => w.id === walletIds.magicLink || w.id === walletIds.embeddedWallet, + (w) => + w.id === walletIds.magicLink || + (w.id === walletIds.embeddedWallet && w.selectUI), ); const connectionWallets = wallets .filter( diff --git a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/auth.ts b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/auth.ts index 102279106e5..25467fd7d1d 100644 --- a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/auth.ts +++ b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/auth.ts @@ -15,7 +15,11 @@ import { cognitoEmailSignIn, cognitoEmailSignUp, } from "./helpers/auth/cognitoAuth"; -import { postPaperAuth, prePaperAuth } from "./helpers/auth/middleware"; +import { + postPaperAuth, + postPaperAuthUserManaged, + prePaperAuth, +} from "./helpers/auth/middleware"; import { isDeviceSharePresentForUser } from "./helpers/storage/local"; import { getCognitoUser, setCognitoUser } from "./helpers/storage/state"; import { SendEmailOtpReturnType } from "@thirdweb-dev/wallets"; @@ -201,7 +205,7 @@ export async function socialLogin(oauthOptions: OauthOption, clientId: string) { } export async function customJwt(authOptions: AuthOptions, clientId: string) { - const { jwtToken } = authOptions; + const { jwtToken, encryptionKey } = authOptions; const resp = await fetch(ROUTE_AUTH_JWT_CALLBACK, { method: "POST", @@ -233,7 +237,7 @@ export async function customJwt(authOptions: AuthOptions, clientId: string) { isNewUser: verifiedToken.isNewUser, }; - await postPaperAuth(toStoreToken, clientId); + await postPaperAuthUserManaged(toStoreToken, clientId, encryptionKey); return { verifiedToken, email: verifiedToken.authDetails.email }; } catch (e) { diff --git a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/helpers/auth/middleware.ts b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/helpers/auth/middleware.ts index 36a24cbd6de..894ac6c4824 100644 --- a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/helpers/auth/middleware.ts +++ b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/helpers/auth/middleware.ts @@ -61,3 +61,45 @@ export async function postPaperAuth( return storedToken; } + +export async function postPaperAuthUserManaged( + storedToken: AuthStoredTokenWithCookieReturnType["storedToken"], + clientId: string, + encryptionKey: string, +) { + if (storedToken.shouldStoreCookieString) { + await setAuthTokenClient(storedToken.cookieString, clientId); + } + + await setWallerUserDetails({ + clientId, + userId: storedToken.authDetails.userWalletId, + email: storedToken.authDetails.email, + }); + + if (storedToken.isNewUser) { + console.log("========== New User =========="); + await setUpNewUserWallet(encryptionKey, clientId); + } else { + try { + // existing device share + await getDeviceShare(clientId); + console.log("========== Existing user with device share =========="); + } catch (e) { + // trying to recreate device share from recovery code to derive wallet + console.log("========== Existing user on new device =========="); + + try { + await setUpShareForNewDevice({ + clientId, + recoveryCode: encryptionKey, + }); + } catch (error) { + console.error("Error setting up wallet on device", error); + throw error; + } + } + } + + return storedToken; +} diff --git a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/types.ts b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/types.ts index f9ff70b6ad1..f2ee83c98c0 100644 --- a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/types.ts +++ b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/types.ts @@ -6,6 +6,11 @@ import type { AuthProvider, } from "@paperxyz/embedded-wallet-service-sdk"; +export type CustomAuth = { + jwtToken: string; + encryptionKey: string; +}; + export type OauthOptions = { providers: AuthProvider[]; redirectUrl: string; diff --git a/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSelectionUI.tsx b/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSelectionUI.tsx index c6d17bbf45d..88c961f162e 100644 --- a/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSelectionUI.tsx +++ b/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSelectionUI.tsx @@ -22,8 +22,9 @@ export const EmailSelectionUI: React.FC< SelectUIProps & { oauthOptions?: OauthOptions; email?: boolean; + custom_auth?: boolean; } -> = ({ onSelect, walletConfig, oauthOptions, email }) => { +> = ({ onSelect, walletConfig, oauthOptions, email, custom_auth }) => { const theme = useGlobalTheme(); const [emailInput, setEmailInput] = useState(""); const [errorMessage, setErrorMessage] = useState(""); @@ -41,6 +42,11 @@ export const EmailSelectionUI: React.FC< setEmailWallet(emailWalletInstance); }, [createWalletInstance, walletConfig]); + if (custom_auth) { + // No UI for custom auth + return null; + } + const validateEmail = (emailToValidate: string) => { const pattern = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i; return pattern.test(emailToValidate); diff --git a/packages/react-native/src/evm/wallets/wallets/embedded/embedded-wallet.tsx b/packages/react-native/src/evm/wallets/wallets/embedded/embedded-wallet.tsx index 0b7cc9bb342..544475104ea 100644 --- a/packages/react-native/src/evm/wallets/wallets/embedded/embedded-wallet.tsx +++ b/packages/react-native/src/evm/wallets/wallets/embedded/embedded-wallet.tsx @@ -20,6 +20,8 @@ export type EmbeddedWalletConfig = { redirectUrl: string; } | false; + + custom_auth?: boolean; }; export const embeddedWallet = ( @@ -36,6 +38,7 @@ export const embeddedWallet = ( } : undefined } + custom_auth={config?.custom_auth} // you cannot disable both email and oauth email={!config?.oauthOptions && !config?.email ? true : config?.email} /> @@ -50,7 +53,7 @@ export const embeddedWallet = ( clientId: options.clientId || "", }); }, - selectUI: selectUI, + selectUI: config?.custom_auth ? undefined : selectUI, connectUI: EmbeddedConnectionUI, }; }; From acfe23c0c40890ce58a9563cb82895f5a6c42022 Mon Sep 17 00:00:00 2001 From: ikethirdweb Date: Thu, 19 Oct 2023 13:30:05 -0400 Subject: [PATCH 07/13] clean --- .../src/evm/wallets/connectors/embedded-wallet/jwks.ts | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 packages/react-native/src/evm/wallets/connectors/embedded-wallet/jwks.ts diff --git a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/jwks.ts b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/jwks.ts deleted file mode 100644 index a990ac33ae8..00000000000 --- a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/jwks.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const keys = { - keys: [ - { - kty: "RSA", - n: "tuYJTS0bXLO629xwe6LpGs_es-I6jbCwpEXmYfzRcWYGd0Z4l0czwmv7FJ8eycOaNFt2Hjb3nhwHZGcjbLd1SWXiiRpVnmL-1Y01AAPOWECqxifO3C5Jco5En5SO7p8tDsdx9LD8mxMnplyv3X8pBzYDrNlTlpLTPksS3yPqA01MXHlH_xhEY2G7g7sUHt9jb9uPWALofH3TeztjPPiU6kQLQ8VvlTkz0V2DMZYPnvLkRzt6myguwwNEQwzEzzH4ELtf8tez0lx5RE6qGGAezLIH_cnPmRevOEcxoQP3u0bvZcl0P5M7eOLPolw2B2_pFBpaqGMFxr7yCcwmde0HRw", - e: "AQAB", - kid: "0", - }, - ], -}; From 2ba78b2627d6e7f7d3b101f456e4bafe659bbcc5 Mon Sep 17 00:00:00 2001 From: ikethirdweb Date: Thu, 19 Oct 2023 17:57:54 -0400 Subject: [PATCH 08/13] start adding recovery flow --- .../embedded-wallet/embedded-connector.ts | 6 +-- .../embedded-wallet/embedded/auth.ts | 41 +++++++++++-------- .../embedded/helpers/api/fetchers.ts | 1 + .../connectors/embedded-wallet/types.ts | 6 +-- .../wallets/embedded/EmbeddedConnectionUI.tsx | 2 +- .../embedded/EmbeddedSocialConnection.tsx | 2 +- 6 files changed, 32 insertions(+), 26 deletions(-) diff --git a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded-connector.ts b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded-connector.ts index e12ed90f787..d4243f749bc 100644 --- a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded-connector.ts +++ b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded-connector.ts @@ -45,7 +45,7 @@ export class EmbeddedWalletConnector extends Connector>; try { - cognitoUser = await cognitoEmailSignIn(email, clientId); - } catch (e) { - await cognitoEmailSignUp(email, clientId); - cognitoUser = await cognitoEmailSignIn(email, clientId); - } - setCognitoUser(cognitoUser); - - let result: Awaited>; - try { - result = await getEmbeddedWalletUserDetail({ + userDetails = await getEmbeddedWalletUserDetail({ email, clientId, }); @@ -63,19 +53,34 @@ export async function sendEmailOTP( ); } - return result.isNewUser + if ( + userDetails.recoveryShareManagement === RecoveryShareManagement.AWS_MANAGED + ) { + // AWS Auth flow + let cognitoUser: CognitoUser; + try { + cognitoUser = await cognitoEmailSignIn(email, clientId); + } catch (e) { + await cognitoEmailSignUp(email, clientId); + cognitoUser = await cognitoEmailSignIn(email, clientId); + } + setCognitoUser(cognitoUser); + } else { + } + + return userDetails.isNewUser ? { - isNewUser: result.isNewUser, + isNewUser: userDetails.isNewUser, isNewDevice: true, - recoveryShareManagement: RecoveryShareManagement.AWS_MANAGED, + recoveryShareManagement: userDetails.recoveryShareManagement, } : { - isNewUser: result.isNewUser, + isNewUser: userDetails.isNewUser, isNewDevice: !(await isDeviceSharePresentForUser( clientId, - result.walletUserId ?? "", + userDetails.walletUserId ?? "", )), - recoveryShareManagement: RecoveryShareManagement.AWS_MANAGED, + recoveryShareManagement: userDetails.recoveryShareManagement, }; } diff --git a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/helpers/api/fetchers.ts b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/helpers/api/fetchers.ts index 14521368055..1db382b0669 100644 --- a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/helpers/api/fetchers.ts +++ b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/helpers/api/fetchers.ts @@ -93,6 +93,7 @@ export async function getEmbeddedWalletUserDetail(args: { const result = (await resp.json()) as | { isNewUser: true; + recoveryShareManagement: RecoveryShareManagement; } | { isNewUser: false; diff --git a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/types.ts b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/types.ts index f2ee83c98c0..0709ac76ddf 100644 --- a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/types.ts +++ b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/types.ts @@ -61,16 +61,16 @@ export type EmbeddedWalletConnectionArgs = { chainId?: number; } & ( | { - loginType: "google_oauth"; + loginType: "headless_google_oauth"; redirectUrl: string; } | { - loginType: "email_otp_verification"; + loginType: "headless_email_otp_verification"; email: string; otp: string; } | { - loginType: "custom_jwt"; + loginType: "custom_jwt_auth"; jwtToken: string; encryptionKey: string; } diff --git a/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedConnectionUI.tsx b/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedConnectionUI.tsx index e0e31752662..fc3875ff8b5 100644 --- a/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedConnectionUI.tsx +++ b/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedConnectionUI.tsx @@ -65,7 +65,7 @@ export const EmbeddedConnectionUI: React.FC> = ({ setTimeout(() => { (selectionData.emailWallet as EmbeddedWallet) .connect({ - loginType: "email_otp_verification", + loginType: "headless_email_otp_verification", otp, email: selectionData.email, }) diff --git a/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSocialConnection.tsx b/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSocialConnection.tsx index bff69b7859a..15bf8751d48 100644 --- a/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSocialConnection.tsx +++ b/packages/react-native/src/evm/wallets/wallets/embedded/EmbeddedSocialConnection.tsx @@ -24,7 +24,7 @@ export const EmbeddedSocialConnection: React.FC< setTimeout(() => { (selectionData.emailWallet as EmbeddedWallet) .connect({ - loginType: "google_oauth", + loginType: "headless_google_oauth", redirectUrl: selectionData.oauthOptions?.redirectUrl, }) .then(async (response) => { From 32fcd278a1bc3d4a5f26ce284f94afbffdb076b4 Mon Sep 17 00:00:00 2001 From: ikethirdweb Date: Fri, 20 Oct 2023 11:52:22 -0400 Subject: [PATCH 09/13] revert pass --- .../embedded-wallet/embedded/auth.ts | 41 ++++++++----------- .../embedded/helpers/api/fetchers.ts | 1 - 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/auth.ts b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/auth.ts index 19846e18d19..25467fd7d1d 100644 --- a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/auth.ts +++ b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/auth.ts @@ -41,9 +41,19 @@ export async function sendEmailOTP( email, }); - let userDetails: Awaited>; + // AWS Auth flow + let cognitoUser: CognitoUser; try { - userDetails = await getEmbeddedWalletUserDetail({ + cognitoUser = await cognitoEmailSignIn(email, clientId); + } catch (e) { + await cognitoEmailSignUp(email, clientId); + cognitoUser = await cognitoEmailSignIn(email, clientId); + } + setCognitoUser(cognitoUser); + + let result: Awaited>; + try { + result = await getEmbeddedWalletUserDetail({ email, clientId, }); @@ -53,34 +63,19 @@ export async function sendEmailOTP( ); } - if ( - userDetails.recoveryShareManagement === RecoveryShareManagement.AWS_MANAGED - ) { - // AWS Auth flow - let cognitoUser: CognitoUser; - try { - cognitoUser = await cognitoEmailSignIn(email, clientId); - } catch (e) { - await cognitoEmailSignUp(email, clientId); - cognitoUser = await cognitoEmailSignIn(email, clientId); - } - setCognitoUser(cognitoUser); - } else { - } - - return userDetails.isNewUser + return result.isNewUser ? { - isNewUser: userDetails.isNewUser, + isNewUser: result.isNewUser, isNewDevice: true, - recoveryShareManagement: userDetails.recoveryShareManagement, + recoveryShareManagement: RecoveryShareManagement.AWS_MANAGED, } : { - isNewUser: userDetails.isNewUser, + isNewUser: result.isNewUser, isNewDevice: !(await isDeviceSharePresentForUser( clientId, - userDetails.walletUserId ?? "", + result.walletUserId ?? "", )), - recoveryShareManagement: userDetails.recoveryShareManagement, + recoveryShareManagement: RecoveryShareManagement.AWS_MANAGED, }; } diff --git a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/helpers/api/fetchers.ts b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/helpers/api/fetchers.ts index 1db382b0669..14521368055 100644 --- a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/helpers/api/fetchers.ts +++ b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/helpers/api/fetchers.ts @@ -93,7 +93,6 @@ export async function getEmbeddedWalletUserDetail(args: { const result = (await resp.json()) as | { isNewUser: true; - recoveryShareManagement: RecoveryShareManagement; } | { isNewUser: false; From 7c1002cedff87b959ed9c59f5a1e0eec969320cc Mon Sep 17 00:00:00 2001 From: ikethirdweb Date: Fri, 20 Oct 2023 17:15:44 -0400 Subject: [PATCH 10/13] changeset --- .changeset/cold-dryers-dream.md | 58 +++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 .changeset/cold-dryers-dream.md diff --git a/.changeset/cold-dryers-dream.md b/.changeset/cold-dryers-dream.md new file mode 100644 index 00000000000..dc8fb5ce30f --- /dev/null +++ b/.changeset/cold-dryers-dream.md @@ -0,0 +1,58 @@ +--- +"@thirdweb-dev/react-native": patch +--- + +Custom JWT support in React Native + +Enables passing a custom JWT to the embeddedWallet: + +```javascript +import { + EmbeddedWallet, + embeddedWallet, + ThirdwebProvider, + useCreateWalletInstance, + useSetConnectedWallet, +} from '@thirdweb-dev/react-native'; +import {Button} from 'react-native'; +import React from 'react'; + +const App = () => { + return ( + + + + ); +}; + +const AppInner = () => { + const createInstance = useCreateWalletInstance(); + const setConnectedWallet = useSetConnectedWallet(); + + const triggerConnect = async () => { + const embeddedWalletConfig = embeddedWallet(); + + if (embeddedWalletConfig) { + const instance = createInstance(embeddedWalletConfig); + + if (instance) { + await (instance as EmbeddedWallet).connect({ + loginType: 'custom_jwt_auth', + encryptionKey: 'hello', + jwtToken: 'customJwt' || '', + }); + setConnectedWallet(instance); // this sets the active wallet on the provider enabling all thirdweb hooks + } + } + }; + + return