Skip to content

Commit

Permalink
feat(react): multi-auth UI (#3870)
Browse files Browse the repository at this point in the history
## Problem solved

Short description of the bug fixed or feature added

## Changes made

- [ ] Public API changes: list the public API changes made if any
- [ ] Internal API changes: explain the internal logic changes

## How to test

- [ ] Automated tests: link to unit test file
- [ ] Manual tests: step by step instructions on how to test

## Contributor NFT

Paste in your wallet address below and we will airdrop you a special NFT when your pull request is merged.

```Address: ```

<!-- start pr-codex -->

---

## PR-Codex overview
This PR adds account linking functionality, SIWE authentication, and profile management features to the Connect UI in `thirdweb`.

### Detailed summary
- Added account linking to Connect UI
- Introduced `useProfiles` hook for fetching linked profiles
- Updated UI components and locales for profile management
- Enhanced authentication options with SIWE
- Improved wallet icons and authentication methods

> The following files were skipped due to too many changes: `packages/thirdweb/src/react/web/ui/ConnectWallet/icons/AddUserIcon.tsx`, `packages/thirdweb/src/react/web/wallets/shared/oauthSignIn.ts`, `packages/thirdweb/src/wallets/ecosystem/types.ts`, `packages/thirdweb/src/wallets/in-app/core/authentication/types.ts`, `packages/thirdweb/src/react/web/ui/ConnectWallet/Modal/AnyWalletConnectUI.tsx`, `packages/thirdweb/src/react/web/ui/ConnectWallet/icons/MultiUserIcon.tsx`, `packages/thirdweb/src/wallets/in-app/web/lib/auth/oauth.ts`, `packages/thirdweb/src/wallets/in-app/core/authentication/getLoginPath.ts`, `packages/thirdweb/src/react/web/wallets/ecosystem/EcosystemWalletFormUI.tsx`, `packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts`, `packages/thirdweb/src/react/core/hooks/others/useProfiles.ts`, `packages/thirdweb/src/react/web/ui/components/WalletImage.tsx`, `packages/thirdweb/src/react/web/wallets/ecosystem/EcosystemWalletConnectUI.tsx`, `packages/thirdweb/src/react/web/ui/ConnectWallet/screens/ManageWalletScreen.tsx`, `packages/thirdweb/src/react/web/wallets/in-app/InAppWalletConnectUI.tsx`, `packages/thirdweb/src/react/web/wallets/in-app/InAppWalletFormUI.tsx`, `packages/thirdweb/src/wallets/in-app/core/authentication/siwe.ts`, `packages/thirdweb/src/react/web/ui/ConnectWallet/screens/LinkProfileScreen.tsx`, `packages/thirdweb/src/wallets/in-app/web/lib/auth/otp.ts`, `packages/thirdweb/src/react/web/wallets/shared/ConnectWalletSocialOptions.tsx`, `packages/thirdweb/src/react/web/ui/ConnectWallet/Details.tsx`, `packages/thirdweb/src/react/web/ui/ConnectWallet/screens/LinkedProfilesScreen.tsx`, `packages/thirdweb/src/react/web/wallets/shared/SocialLogin.tsx`, `packages/thirdweb/src/wallets/in-app/web/lib/web-connector.ts`, `packages/thirdweb/src/wallets/in-app/native/native-connector.ts`, `packages/thirdweb/src/react/web/wallets/shared/PassKeyLogin.tsx`, `packages/thirdweb/src/react/web/wallets/shared/OTPLoginUI.tsx`, `packages/thirdweb/src/wallets/in-app/native/auth/native-auth.ts`

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
  • Loading branch information
gregfromstl committed Aug 8, 2024
1 parent f74d523 commit bbb4f1c
Show file tree
Hide file tree
Showing 54 changed files with 1,123 additions and 478 deletions.
14 changes: 14 additions & 0 deletions .changeset/afraid-peaches-admire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
"thirdweb": minor
---

Adds useProfiles hook to fetch linked profiles for the current wallet.

```jsx
import { useProfiles } from "thirdweb/react";

const { data: profiles } = useProfiles();

console.log("Type:", profiles[0].type); // "discord"
console.log("Email:", profiles[0].email); // "[email protected]"
```
19 changes: 19 additions & 0 deletions .changeset/chilled-ants-float.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
"thirdweb": minor
---

Adds SIWE authentication on in-app wallets

```ts
import { inAppWallet } from "thirdweb/wallets"

const wallet = inAppWallet();
const account = await wallet.connect({
client,
walletId: "io.metamask",
chainId: 1 // can be anything unless using smart accounts
});
```

This will give you a new in-app wallet, **not** the injected provider wallet.

5 changes: 5 additions & 0 deletions .changeset/wild-readers-relate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"thirdweb": minor
---

Adds account linking to the Connect UI
1 change: 1 addition & 0 deletions packages/thirdweb/src/exports/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export { useSendCalls } from "../react/core/hooks/wallets/useSendCalls.js";
export { useSwitchActiveWalletChain } from "../react/core/hooks/wallets/useSwitchActiveWalletChain.js";
export { useCallsStatus } from "../react/core/hooks/wallets/useCallsStatus.js";
export { useWalletBalance } from "../react/core/hooks/others/useWalletBalance.js";
export { useProfiles } from "../react/core/hooks/others/useProfiles.js";

// chain hooks
export { useChainMetadata } from "../react/core/hooks/others/useChainQuery.js";
Expand Down
2 changes: 1 addition & 1 deletion packages/thirdweb/src/exports/wallets/in-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ export type {

export { hasStoredPasskey } from "../../wallets/in-app/web/lib/auth/passkeys.js";

export { socialIcons } from "../../react/core/utils/socialIcons.js";
export { socialIcons } from "../../react/core/utils/walletIcon.js";
36 changes: 36 additions & 0 deletions packages/thirdweb/src/react/core/hooks/others/useProfiles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { type UseQueryResult, useQuery } from "@tanstack/react-query";
import type { Profile } from "../../../../wallets/in-app/core/authentication/types.js";
import { getProfiles } from "../../../../wallets/in-app/core/wallet/profiles.js";
import type { Wallet } from "../../../../wallets/interfaces/wallet.js";
import { useActiveWallet } from "../wallets/useActiveWallet.js";

/**
* @description Retrieves all linked profiles for the current wallet.
*
* @returns A React Query result containing the linked profiles for the connected in-app wallet.
*
* @note This hook will only run if the connected wallet supports multi-auth (in-app wallets).
*
* @example
* ```jsx
* import { use } from "thirdweb/react";
*
* const { data: profiles } = useProfiles();
*
* console.log("Type:", profiles[0].type); // "discord"
* console.log("Email:", profiles[0].email); // "[email protected]"
* ```
*
* @wallet
*/
export function useProfiles(): UseQueryResult<Profile[]> {
const wallet = useActiveWallet();

return useQuery({
queryKey: ["profiles", wallet?.id],
enabled: !!wallet && wallet.id === "inApp",
queryFn: async () => {
return getProfiles(wallet as Wallet<"inApp">);
},
});
}

Check warning on line 36 in packages/thirdweb/src/react/core/hooks/others/useProfiles.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/core/hooks/others/useProfiles.ts#L27-L36

Added lines #L27 - L36 were not covered by tests
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,28 @@ export const socialIcons = {
farcaster: farcasterIconUri,
telegram: telegramIconUri,
};

export function getWalletIcon(provider: string) {
switch (provider) {
case "google":
return googleIconUri;
case "apple":
return appleIconUri;
case "facebook":
return facebookIconUri;
case "phone":
return phoneIcon;
case "email":
return emailIcon;
case "passkey":
return passkeyIcon;
case "discord":
return discordIconUri;
case "farcaster":
return farcasterIconUri;
case "telegram":
return telegramIconUri;
default:
return genericWalletIcon;
}
}

Check warning on line 82 in packages/thirdweb/src/react/core/utils/walletIcon.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/core/utils/walletIcon.ts#L60-L82

Added lines #L60 - L82 were not covered by tests
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Chain } from "../../../../chains/types.js";
import type { ThirdwebClient } from "../../../../client/client.js";
import type { Theme } from "../../../core/design-system/index.js";
import type { TokenInfo } from "../../../core/utils/defaultTokens.js";
import { genericTokenIcon } from "../../../core/utils/socialIcons.js";
import { genericTokenIcon } from "../../../core/utils/walletIcon.js";
import { ChainIcon } from "./ChainIcon.js";
import { RNImage } from "./RNImage.js";

Expand Down
56 changes: 45 additions & 11 deletions packages/thirdweb/src/react/web/ui/ConnectWallet/Details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import type {
} from "../../../core/utils/defaultTokens.js";
import { hasSmartAccount } from "../../../core/utils/isSmartWallet.js";
import { useConnectedWalletDetails } from "../../../core/utils/wallet.js";
import { WalletUIStatesProvider } from "../../providers/wallet-ui-states-provider.js";
import { ChainIcon } from "../components/ChainIcon.js";
import { CopyIcon } from "../components/CopyIcon.js";
import { Img } from "../components/Img.js";
Expand All @@ -68,6 +69,7 @@ import { fadeInAnimation } from "../design-system/animations.js";
import { StyledButton } from "../design-system/elements.js";
import type { LocaleId } from "../types.js";
import { MenuButton, MenuLink } from "./MenuButton.js";
import { ScreenSetupContext, useSetupScreen } from "./Modal/screen.js";
import {
NetworkSelectorContent,
type NetworkSelectorProps,
Expand All @@ -84,6 +86,8 @@ import { getConnectLocale } from "./locale/getConnectLocale.js";
import type { ConnectLocale } from "./locale/types.js";
import { LazyBuyScreen } from "./screens/Buy/LazyBuyScreen.js";
import { WalletManagerScreen } from "./screens/Details/WalletManagerScreen.js";
import { LinkProfileScreen } from "./screens/LinkProfileScreen.js";
import { LinkedProfilesScreen } from "./screens/LinkedProfilesScreen.js";
import { ManageWalletScreen } from "./screens/ManageWalletScreen.js";
import { PrivateKey } from "./screens/PrivateKey.js";
import { ReceiveFunds } from "./screens/ReceiveFunds.js";
Expand Down Expand Up @@ -277,6 +281,12 @@ function DetailsModal(props: {

const disableSwitchChain = !activeWallet?.switchChain;

const screenSetup = useSetupScreen({
size: "compact",
welcomeScreen: undefined,
wallets: activeWallet ? [activeWallet] : [],
});

Check warning on line 289 in packages/thirdweb/src/react/web/ui/ConnectWallet/Details.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/Details.tsx#L284-L289

Added lines #L284 - L289 were not covered by tests
function closeModal() {
setIsOpen(false);
onModalUnmount(() => {
Expand Down Expand Up @@ -778,6 +788,25 @@ function DetailsModal(props: {
client={client}
/>
);
} else if (screen === "linked-profiles") {
content = (
<LinkedProfilesScreen
onBack={() => setScreen("manage-wallet")}
client={client}
locale={locale}
setScreen={setScreen}
/>
);
} else if (screen === "link-profile") {
content = (
<LinkProfileScreen
onBack={() => {
setScreen("linked-profiles");
}}
client={client}
locale={locale}
/>
);

Check warning on line 809 in packages/thirdweb/src/react/web/ui/ConnectWallet/Details.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/Details.tsx#L791-L809

Added lines #L791 - L809 were not covered by tests
}

// send funds
Expand Down Expand Up @@ -832,17 +861,21 @@ function DetailsModal(props: {

return (
<CustomThemeProvider theme={props.theme}>
<Modal
size={"compact"}
open={isOpen}
setOpen={(_open) => {
if (!_open) {
closeModal();
}
}}
>
{content}
</Modal>
<WalletUIStatesProvider theme={props.theme} isOpen={false}>
<ScreenSetupContext.Provider value={screenSetup}>
<Modal
size={"compact"}
open={isOpen}
setOpen={(_open) => {
if (!_open) {
closeModal();
}
}}
>
{content}
</Modal>
</ScreenSetupContext.Provider>
</WalletUIStatesProvider>

Check warning on line 878 in packages/thirdweb/src/react/web/ui/ConnectWallet/Details.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/Details.tsx#L864-L878

Added lines #L864 - L878 were not covered by tests
</CustomThemeProvider>
);
}
Expand Down Expand Up @@ -1191,6 +1224,7 @@ export type UseWalletDetailsModalOptions = {
* ```
*/
chains?: Chain[];

/**
* Show a "Request Testnet funds" link in Wallet Details Modal when user is connected to a testnet.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import type { Wallet } from "../../../../../wallets/interfaces/wallet.js";
import type { EcosystemWalletId } from "../../../../../wallets/wallet-types.js";
import { iconSize } from "../../../../core/design-system/index.js";
import { useWalletInfo } from "../../../../core/utils/wallet.js";
import EcosystemWalletConnectUI from "../../../wallets/ecosystem/EcosystemWalletConnectUI.js";
import { getInjectedWalletLocale } from "../../../wallets/injected/locale/getInjectedWalletLocale.js";
import { GetStartedScreen } from "../../../wallets/shared/GetStartedScreen.js";
import { LoadingScreen } from "../../../wallets/shared/LoadingScreen.js";
Expand All @@ -38,6 +37,9 @@ const CoinbaseSDKWalletConnectUI = /* @__PURE__ */ lazy(
const InAppWalletConnectUI = /* @__PURE__ */ lazy(
() => import("../../../wallets/in-app/InAppWalletConnectUI.js"),
);
const EcosystemWalletConnectUI = /* @__PURE__ */ lazy(
() => import("../../../wallets/ecosystem/EcosystemWalletConnectUI.js"),
);

/**
* @internal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export function useScreenContext() {
const ctx = useContext(ScreenSetupContext);
if (!ctx) {
throw new Error(
"useScreenContext must be used within a <ScreenProvider />",
"useScreenContext must be used within a <ScreenSetupContext.Provider />",

Check warning on line 73 in packages/thirdweb/src/react/web/ui/ConnectWallet/Modal/screen.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/Modal/screen.tsx#L73

Added line #L73 was not covered by tests
);
}
return ctx;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
radius,
spacing,
} from "../../../core/design-system/index.js";
import { genericWalletIcon } from "../../../core/utils/socialIcons.js";
import { genericWalletIcon } from "../../../core/utils/walletIcon.js";
import { useSetSelectionData } from "../../providers/wallet-ui-states-provider.js";
import { sortWallets } from "../../utils/sortWallets.js";
import { LoadingScreen } from "../../wallets/shared/LoadingScreen.js";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { IconFC } from "./types.js";

/**
* @internal
*/
export const AddUserIcon: IconFC = (props) => {
return (
<svg
width={props.size}
height={props.size}
viewBox="0 0 24 24"
fill="none"
role="presentation"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 15.5H7.5C6.10444 15.5 5.40665 15.5 4.83886 15.6722C3.56045 16.06 2.56004 17.0605 2.17224 18.3389C2 18.9067 2 19.6044 2 21M19 21V15M16 18H22M14.5 7.5C14.5 9.98528 12.4853 12 10 12C7.51472 12 5.5 9.98528 5.5 7.5C5.5 5.01472 7.51472 3 10 3C12.4853 3 14.5 5.01472 14.5 7.5Z"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
};

Check warning on line 25 in packages/thirdweb/src/react/web/ui/ConnectWallet/icons/AddUserIcon.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/icons/AddUserIcon.tsx#L7-L25

Added lines #L7 - L25 were not covered by tests
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { IconFC } from "./types.js";

/**
* @internal
*/
export const MultiUserIcon: IconFC = (props) => {
return (
<svg
width={props.size}
height={props.size}
viewBox="0 0 24 24"
fill="none"
role="presentation"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M22 21V19C22 17.1362 20.7252 15.5701 19 15.126M15.5 3.29076C16.9659 3.88415 18 5.32131 18 7C18 8.67869 16.9659 10.1159 15.5 10.7092M17 21C17 19.1362 17 18.2044 16.6955 17.4693C16.2895 16.4892 15.5108 15.7105 14.5307 15.3045C13.7956 15 12.8638 15 11 15H8C6.13623 15 5.20435 15 4.46927 15.3045C3.48915 15.7105 2.71046 16.4892 2.30448 17.4693C2 18.2044 2 19.1362 2 21M13.5 7C13.5 9.20914 11.7091 11 9.5 11C7.29086 11 5.5 9.20914 5.5 7C5.5 4.79086 7.29086 3 9.5 3C11.7091 3 13.5 4.79086 13.5 7Z"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
};

Check warning on line 25 in packages/thirdweb/src/react/web/ui/ConnectWallet/icons/MultiUserIcon.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/icons/MultiUserIcon.tsx#L7-L25

Added lines #L7 - L25 were not covered by tests
6 changes: 6 additions & 0 deletions packages/thirdweb/src/react/web/ui/ConnectWallet/locale/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ const connectLocaleDe: ConnectLocale = {
smartWallet: "Smart Wallet",
or: "Oder",
goBackButton: "Zurück",
passkeys: {
title: "Passkeys",
linkPasskey: "Passkey verknüpfen",
},
welcomeScreen: {
defaultTitle: "Dein Tor zur dezentralen Welt",
defaultSubtitle: "Verbinde ein Wallet, um loszulegen",
Expand Down Expand Up @@ -104,6 +108,8 @@ const connectLocaleDe: ConnectLocale = {
manageWallet: {
title: "Wallet verwalten",
connectAnApp: "App verbinden",
linkProfile: "Profil verknüpfen",
linkedProfiles: "Verknüpfte Profile",
exportPrivateKey: "PrivateKey exportieren",
},
viewFunds: {
Expand Down
6 changes: 6 additions & 0 deletions packages/thirdweb/src/react/web/ui/ConnectWallet/locale/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ const connectLocaleEn: ConnectLocale = {
smartWallet: "Smart Wallet",
or: "OR",
goBackButton: "Back",
passkeys: {
title: "Passkeys",
linkPasskey: "Link a Passkey",
},
welcomeScreen: {
defaultTitle: "Your gateway to the decentralized world",
defaultSubtitle: "Connect a wallet to get started",
Expand Down Expand Up @@ -102,6 +106,8 @@ const connectLocaleEn: ConnectLocale = {
},
manageWallet: {
title: "Manage Wallet",
linkedProfiles: "Linked Profiles",
linkProfile: "Link a Profile",
connectAnApp: "Connect an App",
exportPrivateKey: "Export Private Key",
},
Expand Down
6 changes: 6 additions & 0 deletions packages/thirdweb/src/react/web/ui/ConnectWallet/locale/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ const connectWalletLocalEs: ConnectLocale = {
smartWallet: "Cartera inteligente",
or: "O",
goBackButton: "Atras",
passkeys: {
title: "Clave de acceso",
linkPasskey: "Vincular una clave de acceso",
},
welcomeScreen: {
defaultTitle: "Tu puerta de entrada al mundo descentralizado",
defaultSubtitle: "Conecta una cartera para empezar",
Expand Down Expand Up @@ -104,6 +108,8 @@ const connectWalletLocalEs: ConnectLocale = {
},
manageWallet: {
title: "Gestionar Cartera",
linkedProfiles: "Perfiles vinculados",
linkProfile: "Vincular un perfil",
connectAnApp: "Conectar una Aplicación",
exportPrivateKey: "Exportar Clave Privada",
},
Expand Down
Loading

0 comments on commit bbb4f1c

Please sign in to comment.