Skip to content

Commit

Permalink
[SDK] Feature: Add SiweOptions for useConnectModal (#6317)
Browse files Browse the repository at this point in the history
Co-authored-by: Joaquim Verges <[email protected]>
  • Loading branch information
jcheese1 and joaquim-verges authored Feb 25, 2025
1 parent cf202c0 commit a19c0c9
Show file tree
Hide file tree
Showing 8 changed files with 210 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/tasty-pumpkins-relax.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"thirdweb": minor
---

Add SiweOptions in useConnectModal
64 changes: 64 additions & 0 deletions apps/playground-web/src/app/connect/auth/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -46,6 +47,12 @@ export default function Page() {
<section className="space-y-8">
<SmartAccountAuth />
</section>

<div className="h-14" />

<section className="space-y-8">
<BasicAuthHook />
</section>
</main>
</ThirdwebProvider>
);
Expand Down Expand Up @@ -101,6 +108,63 @@ export function AuthButton() {
);
}

function BasicAuthHook() {
return (
<>
<div className="space-y-2">
<h2 className="font-semibold text-2xl tracking-tight sm:text-3xl">
Auth with your own UI
</h2>
<p className="max-w-[600px]">
Use the `useConnectModal` hook to add authentication to your app with
your own UI.
</p>
</div>

<CodeExample
preview={<BasicAuthHookPreview />}
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 <Button type="button" onClick={onClick}>{isLoggedIn ? "Sign out" : "Sign in"}</Button>;
}
`}
lang="tsx"
/>
</>
);
}

function GatedContent() {
return (
<>
Expand Down
52 changes: 52 additions & 0 deletions apps/playground-web/src/components/auth/auth-hook.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Button type="button" onClick={onClick}>
{isLoggedIn ? "Sign out" : "Sign in"}
</Button>
);
}
36 changes: 36 additions & 0 deletions apps/playground-web/src/components/auth/basic-auth-hook.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="flex flex-col gap-5">
<div className="mx-auto">
<AuthHook />
</div>
{jwt && !!jwt.value && (
<table className="table-auto border-collapse rounded-lg backdrop-blur">
<tbody>
{Object.keys(jwt).map((key) => (
<tr key={key} className="">
<td className="rounded border p-2">{key}</td>
<td
className={cn(
"max-h-[200px] max-w-[250px] overflow-y-auto whitespace-normal break-words border p-2",
{
"text-xs": key === "value",
},
)}
>
{jwt[key as keyof RequestCookie]}
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
);
}
40 changes: 38 additions & 2 deletions apps/playground-web/src/components/sign-in/modal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";

import {
type ConnectButtonProps,
useActiveAccount,
useActiveWallet,
useConnectModal,
Expand All @@ -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;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ export const ConnectModalContent = (props: {
const signatureScreen = (
<SignatureScreen
onDone={onClose}
onClose={onClose}
modalSize={props.size}
termsOfServiceUrl={props.meta.termsOfServiceUrl}
privacyPolicyUrl={props.meta.privacyPolicyUrl}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type Status = "signing" | "failed" | "idle";

export const SignatureScreen: React.FC<{
onDone: (() => void) | undefined;
onClose?: (() => void) | undefined;
modalSize: "compact" | "wide";
termsOfServiceUrl?: string;
privacyPolicyUrl?: string;
Expand All @@ -42,6 +43,7 @@ export const SignatureScreen: React.FC<{
const {
onDone,
modalSize,
onClose,
termsOfServiceUrl,
privacyPolicyUrl,
connectLocale,
Expand Down Expand Up @@ -145,6 +147,7 @@ export const SignatureScreen: React.FC<{
variant="secondary"
data-testid="disconnect-button"
onClick={() => {
onClose?.();
disconnect(wallet);
}}
style={{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -62,6 +63,7 @@ export function useConnectModal() {
<Modal
{...props}
onConnect={(w) => {
if (props.auth) return;
resolve(w);
cleanup();
}}
Expand Down Expand Up @@ -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}
Expand Down Expand Up @@ -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

0 comments on commit a19c0c9

Please sign in to comment.