diff --git a/.changeset/tasty-pumpkins-relax.md b/.changeset/tasty-pumpkins-relax.md
new file mode 100644
index 00000000000..0c8394d039b
--- /dev/null
+++ b/.changeset/tasty-pumpkins-relax.md
@@ -0,0 +1,5 @@
+---
+"thirdweb": minor
+---
+
+Add SiweOptions in useConnectModal
diff --git a/apps/playground-web/src/app/connect/auth/page.tsx b/apps/playground-web/src/app/connect/auth/page.tsx
index f2ab05f7cea..60eba5479fd 100644
--- a/apps/playground-web/src/app/connect/auth/page.tsx
+++ b/apps/playground-web/src/app/connect/auth/page.tsx
@@ -5,6 +5,7 @@ import { CodeExample } from "@/components/code/code-example";
import ThirdwebProvider from "@/components/thirdweb-provider";
import { metadataBase } from "@/lib/constants";
import type { Metadata } from "next";
+import { BasicAuthHookPreview } from "../../../components/auth/basic-auth-hook";
import { APIHeader } from "../../../components/blocks/APIHeader";
export const metadata: Metadata = {
@@ -46,6 +47,12 @@ export default function Page() {
+
+
+
+
);
@@ -101,6 +108,63 @@ export function AuthButton() {
);
}
+function BasicAuthHook() {
+ return (
+ <>
+
+
+ Auth with your own UI
+
+
+ Use the `useConnectModal` hook to add authentication to your app with
+ your own UI.
+
+
+
+ }
+ code={`"use client";
+
+import {
+ generatePayload,
+ isLoggedIn,
+ login,
+ logout,
+} from "@/app/connect/auth/server/actions/auth";
+import { THIRDWEB_CLIENT } from "@/lib/client";
+import { type SiweAuthOptions, useConnectModal } from "thirdweb/react";
+
+const auth: SiweAuthOptions = {
+ isLoggedIn: (address) => isLoggedIn(address),
+ doLogin: (params) => login(params),
+ getLoginPayload: ({ address }) => generatePayload({ address }),
+ doLogout: () => logout(),
+};
+
+export function AuthHook() {
+ const { connect } = useConnectModal();
+ const wallet = useActiveWallet();
+ const { isLoggedIn } = useSiweAuth(wallet, wallet?.getAccount(), auth);
+
+ const onClick = () => {
+ if (isLoggedIn) {
+ auth.doLogout();
+ } else {
+ connect({
+ auth,
+ });
+ }
+ };
+
+ return ;
+}
+`}
+ lang="tsx"
+ />
+ >
+ );
+}
+
function GatedContent() {
return (
<>
diff --git a/apps/playground-web/src/components/auth/auth-hook.tsx b/apps/playground-web/src/components/auth/auth-hook.tsx
new file mode 100644
index 00000000000..a384adc74ab
--- /dev/null
+++ b/apps/playground-web/src/components/auth/auth-hook.tsx
@@ -0,0 +1,52 @@
+"use client";
+
+import {
+ generatePayload,
+ isLoggedIn,
+ login,
+ logout,
+} from "@/app/connect/auth/server/actions/auth";
+import {
+ type SiweAuthOptions,
+ useActiveWallet,
+ useConnectModal,
+ useSiweAuth,
+} from "thirdweb/react";
+import { Button } from "../ui/button";
+
+const auth: SiweAuthOptions = {
+ isLoggedIn: (address) => isLoggedIn(address),
+ doLogin: (params) => login(params),
+ getLoginPayload: ({ address }) =>
+ generatePayload({ address, chainId: 84532 }),
+ doLogout: () => logout(),
+};
+
+export function AuthHook() {
+ const { connect } = useConnectModal();
+ const wallet = useActiveWallet();
+ const { isLoggedIn, doLogout } = useSiweAuth(
+ wallet,
+ wallet?.getAccount(),
+ auth,
+ );
+
+ const onClick = async () => {
+ if (isLoggedIn) {
+ await doLogout();
+ } else {
+ await connect({
+ auth,
+ onConnect: (wallet) => {
+ console.log("connected to", wallet);
+ },
+ });
+ }
+ };
+
+ return (
+
+ );
+}
diff --git a/apps/playground-web/src/components/auth/basic-auth-hook.tsx b/apps/playground-web/src/components/auth/basic-auth-hook.tsx
new file mode 100644
index 00000000000..1d2faf78b76
--- /dev/null
+++ b/apps/playground-web/src/components/auth/basic-auth-hook.tsx
@@ -0,0 +1,36 @@
+import type { RequestCookie } from "next/dist/compiled/@edge-runtime/cookies";
+import { cookies } from "next/headers";
+import { cn } from "../../lib/utils";
+import { AuthHook } from "./auth-hook";
+
+export async function BasicAuthHookPreview() {
+ const jwt = (await cookies()).get("jwt");
+ return (
+
+
+ {jwt && !!jwt.value && (
+
+
+ {Object.keys(jwt).map((key) => (
+
+ | {key} |
+
+ {jwt[key as keyof RequestCookie]}
+ |
+
+ ))}
+
+
+ )}
+
+ );
+}
diff --git a/apps/playground-web/src/components/sign-in/modal.tsx b/apps/playground-web/src/components/sign-in/modal.tsx
index 96d46b0a4d1..f824a9f9bd5 100644
--- a/apps/playground-web/src/components/sign-in/modal.tsx
+++ b/apps/playground-web/src/components/sign-in/modal.tsx
@@ -1,6 +1,7 @@
"use client";
import {
+ type ConnectButtonProps,
useActiveAccount,
useActiveWallet,
useConnectModal,
@@ -10,14 +11,49 @@ import { shortenAddress } from "thirdweb/utils";
import { THIRDWEB_CLIENT } from "../../lib/client";
import { Button } from "../ui/button";
-export function ModalPreview() {
+const playgroundAuth: ConnectButtonProps["auth"] = {
+ async doLogin() {
+ try {
+ localStorage.setItem("playground-loggedin", "true");
+ } catch {
+ // ignore
+ }
+ },
+ async doLogout() {
+ localStorage.removeItem("playground-loggedin");
+ },
+ async getLoginPayload(params) {
+ return {
+ domain: "",
+ address: params.address,
+ statement: "",
+ version: "",
+ nonce: "",
+ issued_at: "",
+ expiration_time: "",
+ invalid_before: "",
+ };
+ },
+ async isLoggedIn() {
+ try {
+ return !!localStorage.getItem("playground-loggedin");
+ } catch {
+ return false;
+ }
+ },
+};
+
+export function ModalPreview({ enableAuth }: { enableAuth?: boolean }) {
const account = useActiveAccount();
const wallet = useActiveWallet();
const connectMutation = useConnectModal();
const { disconnect } = useDisconnect();
const connect = async () => {
- const wallet = await connectMutation.connect({ client: THIRDWEB_CLIENT });
+ const wallet = await connectMutation.connect({
+ client: THIRDWEB_CLIENT,
+ auth: enableAuth ? playgroundAuth : undefined,
+ });
return wallet;
};
diff --git a/packages/thirdweb/src/react/web/ui/ConnectWallet/Modal/ConnectModalContent.tsx b/packages/thirdweb/src/react/web/ui/ConnectWallet/Modal/ConnectModalContent.tsx
index 9b9a82be125..b54dbfb5ea7 100644
--- a/packages/thirdweb/src/react/web/ui/ConnectWallet/Modal/ConnectModalContent.tsx
+++ b/packages/thirdweb/src/react/web/ui/ConnectWallet/Modal/ConnectModalContent.tsx
@@ -235,6 +235,7 @@ export const ConnectModalContent = (props: {
const signatureScreen = (
void) | undefined;
+ onClose?: (() => void) | undefined;
modalSize: "compact" | "wide";
termsOfServiceUrl?: string;
privacyPolicyUrl?: string;
@@ -42,6 +43,7 @@ export const SignatureScreen: React.FC<{
const {
onDone,
modalSize,
+ onClose,
termsOfServiceUrl,
privacyPolicyUrl,
connectLocale,
@@ -145,6 +147,7 @@ export const SignatureScreen: React.FC<{
variant="secondary"
data-testid="disconnect-button"
onClick={() => {
+ onClose?.();
disconnect(wallet);
}}
style={{
diff --git a/packages/thirdweb/src/react/web/ui/ConnectWallet/useConnectModal.tsx b/packages/thirdweb/src/react/web/ui/ConnectWallet/useConnectModal.tsx
index 104b11b97b9..e6a59b2ed1a 100644
--- a/packages/thirdweb/src/react/web/ui/ConnectWallet/useConnectModal.tsx
+++ b/packages/thirdweb/src/react/web/ui/ConnectWallet/useConnectModal.tsx
@@ -6,6 +6,7 @@ import type { Wallet } from "../../../../wallets/interfaces/wallet.js";
import type { SmartWalletOptions } from "../../../../wallets/smart/types.js";
import type { AppMetadata } from "../../../../wallets/types.js";
import type { Theme } from "../../../core/design-system/index.js";
+import type { SiweAuthOptions } from "../../../core/hooks/auth/useSiweAuth.js";
import { SetRootElementContext } from "../../../core/providers/RootElementContext.js";
import { WalletUIStatesProvider } from "../../providers/wallet-ui-states-provider.js";
import { canFitWideModal } from "../../utils/canFitWideModal.js";
@@ -62,6 +63,7 @@ export function useConnectModal() {
{
+ if (props.auth) return;
resolve(w);
cleanup();
}}
@@ -129,8 +131,7 @@ function Modal(
onClose={props.onClose}
shouldSetActive={props.setActive === undefined ? true : props.setActive}
accountAbstraction={props.accountAbstraction}
- // TODO: not set up in `useConnectModal` for some reason?
- auth={undefined}
+ auth={props.auth}
chain={props.chain}
client={props.client}
connectLocale={props.connectLocale}
@@ -432,6 +433,14 @@ export type UseConnectModalOptions = {
* If you want to hide the branding, set this prop to `false`
*/
showThirdwebBranding?: boolean;
+
+ /**
+ * Enable SIWE (Sign in with Ethererum) by passing an object of type `SiweAuthOptions` to
+ * enforce the users to sign a message after connecting their wallet to authenticate themselves.
+ *
+ * Refer to the [`SiweAuthOptions`](https://portal.thirdweb.com/references/typescript/v5/SiweAuthOptions) for more details
+ */
+ auth?: SiweAuthOptions;
};
// TODO: consilidate Button/Embed/Modal props into one type with extras