From 6b261b413a91f15219584eca6888cd5a075d2c4a Mon Sep 17 00:00:00 2001 From: Jonas Daniels Date: Wed, 21 May 2025 01:50:58 -0700 Subject: [PATCH] Add Login component with Google authentication support --- .../app/connect/login/api/[...all]/route.ts | 13 ++++ .../src/app/connect/login/page.tsx | 34 +++++++++ apps/playground-web/src/app/engine/actions.ts | 18 ++--- .../src/components/login/basic-example.tsx | 73 +++++++++++++++++++ apps/playground-web/src/lib/client.ts | 1 + apps/playground-web/src/lib/constants.ts | 2 +- apps/playground-web/src/lib/server-wallet.ts | 15 ++++ packages/thirdweb/package.json | 2 +- packages/thirdweb/src/login/client/index.ts | 2 +- packages/thirdweb/src/login/client/login.ts | 9 ++- packages/thirdweb/src/login/index.ts | 2 +- 11 files changed, 153 insertions(+), 18 deletions(-) create mode 100644 apps/playground-web/src/app/connect/login/api/[...all]/route.ts create mode 100644 apps/playground-web/src/app/connect/login/page.tsx create mode 100644 apps/playground-web/src/components/login/basic-example.tsx create mode 100644 apps/playground-web/src/lib/server-wallet.ts diff --git a/apps/playground-web/src/app/connect/login/api/[...all]/route.ts b/apps/playground-web/src/app/connect/login/api/[...all]/route.ts new file mode 100644 index 00000000000..1ed3b8bfd6a --- /dev/null +++ b/apps/playground-web/src/app/connect/login/api/[...all]/route.ts @@ -0,0 +1,13 @@ +import { THIRDWEB_CLIENT } from "@/lib/client"; +import { getDomain } from "@/lib/constants"; +import { SERVER_WALLET } from "@/lib/server-wallet"; + +import { Login } from "thirdweb"; + +export const { GET, POST } = Login.Server.toNextJsHandler( + Login.Server.createAuthHandler({ + client: THIRDWEB_CLIENT, + domain: getDomain() ?? "", + serverWallet: SERVER_WALLET, + }), +); diff --git a/apps/playground-web/src/app/connect/login/page.tsx b/apps/playground-web/src/app/connect/login/page.tsx new file mode 100644 index 00000000000..d035c8dccd6 --- /dev/null +++ b/apps/playground-web/src/app/connect/login/page.tsx @@ -0,0 +1,34 @@ +import { CodeExample } from "@/components/code/code-example"; +import ThirdwebProvider from "@/components/thirdweb-provider"; +import { metadataBase } from "@/lib/constants"; +import type { Metadata } from "next"; +import { PageLayout } from "../../../components/blocks/APIHeader"; +import { BasicLoginExample } from "../../../components/login/basic-example"; + +export const metadata: Metadata = { + metadataBase, + title: "Login | thirdweb Connect", + description: "TODO", +}; + +export default function Page() { + return ( + + Let users login to your app using any authentication provider. + } + docsLink="https://portal.thirdweb.com/typescript/v5/auth?utm_source=playground" + > +
+ } + /> +
+
+
+ ); +} diff --git a/apps/playground-web/src/app/engine/actions.ts b/apps/playground-web/src/app/engine/actions.ts index aacca6fef93..5eeb701cec2 100644 --- a/apps/playground-web/src/app/engine/actions.ts +++ b/apps/playground-web/src/app/engine/actions.ts @@ -1,19 +1,11 @@ "use server"; +import { THIRDWEB_CLIENT } from "@/lib/client"; +import { SERVER_WALLET } from "@/lib/server-wallet"; import { Engine, defineChain, encode, getContract } from "thirdweb"; import { multicall } from "thirdweb/extensions/common"; import * as ERC20 from "thirdweb/extensions/erc20"; import * as ERC1155 from "thirdweb/extensions/erc1155"; -import { THIRDWEB_CLIENT } from "../../lib/client"; -const BACKEND_WALLET_ADDRESS = process.env.ENGINE_BACKEND_WALLET as string; -const ENGINE_VAULT_ACCESS_TOKEN = process.env - .ENGINE_VAULT_ACCESS_TOKEN as string; - -const serverWallet = Engine.serverWallet({ - address: BACKEND_WALLET_ADDRESS, - client: THIRDWEB_CLIENT, - vaultAccessToken: ENGINE_VAULT_ACCESS_TOKEN, -}); export async function airdrop_tokens_with_engine(params: { contractAddress: string; @@ -44,7 +36,7 @@ export async function airdrop_tokens_with_engine(params: { data, }); - const res = await serverWallet.enqueueTransaction({ transaction: tx }); + const res = await SERVER_WALLET.enqueueTransaction({ transaction: tx }); return res.transactionId; } @@ -82,7 +74,7 @@ export async function mint_erc1155_nft_with_engine(params: MintNFTParams) { to: params.toAddress, supply: BigInt(params.metadataWithSupply.supply), }); - const res = await serverWallet.enqueueTransaction({ transaction: tx }); + const res = await SERVER_WALLET.enqueueTransaction({ transaction: tx }); return res.transactionId; } @@ -106,7 +98,7 @@ export async function claim_erc1155_nft_with_engine(params: ClaimNFTParams) { tokenId: BigInt(params.tokenId), quantity: BigInt(params.quantity), }); - const res = await serverWallet.enqueueTransaction({ transaction: tx }); + const res = await SERVER_WALLET.enqueueTransaction({ transaction: tx }); return res.transactionId; } diff --git a/apps/playground-web/src/components/login/basic-example.tsx b/apps/playground-web/src/components/login/basic-example.tsx new file mode 100644 index 00000000000..e3b56c35143 --- /dev/null +++ b/apps/playground-web/src/components/login/basic-example.tsx @@ -0,0 +1,73 @@ +"use client"; + +import { THIRDWEB_CLIENT } from "@/lib/client"; +import { useRef, useState } from "react"; +import { Login } from "thirdweb"; +import { sepolia } from "thirdweb/chains"; +import { Button } from "../ui/button"; +import { Input } from "../ui/input"; + +export function BasicLoginExample() { + const [account, setAccount] = useState(); + const otpRef = useRef(null); + + if (!account) { + return ( +
+ +
+ ); + } + + if (account.status === "requires_otp") { + return ( +
+ + +
+ ); + } + + return ( +
+

Logged in as: {account.id}

+ + +
+ ); +} diff --git a/apps/playground-web/src/lib/client.ts b/apps/playground-web/src/lib/client.ts index f8494c4d607..b3d664a7e56 100644 --- a/apps/playground-web/src/lib/client.ts +++ b/apps/playground-web/src/lib/client.ts @@ -11,6 +11,7 @@ setThirdwebDomains({ analytics: process.env.NEXT_PUBLIC_ANALYTICS_URL, insight: process.env.NEXT_PUBLIC_INSIGHT_URL, bridge: process.env.NEXT_PUBLIC_BRIDGE_URL, + engineCloud: process.env.NEXT_PUBLIC_ENGINE_CLOUD_URL, }); const isDev = diff --git a/apps/playground-web/src/lib/constants.ts b/apps/playground-web/src/lib/constants.ts index 81706550735..6f0b78b3fdd 100644 --- a/apps/playground-web/src/lib/constants.ts +++ b/apps/playground-web/src/lib/constants.ts @@ -4,7 +4,7 @@ export const metadataBase = process.env.VERCEL_ENV ? new URL("https://playground.thirdweb.com") : undefined; -const getDomain = () => { +export const getDomain = () => { if (process.env.VERCEL_ENV === "production") { return "thirdweb.com"; } diff --git a/apps/playground-web/src/lib/server-wallet.ts b/apps/playground-web/src/lib/server-wallet.ts new file mode 100644 index 00000000000..84caf7660a3 --- /dev/null +++ b/apps/playground-web/src/lib/server-wallet.ts @@ -0,0 +1,15 @@ +import "server-only"; +import { Engine } from "thirdweb"; +import { sepolia } from "thirdweb/chains"; +import { THIRDWEB_CLIENT } from "./client"; + +const BACKEND_WALLET_ADDRESS = process.env.ENGINE_BACKEND_WALLET as string; +const ENGINE_VAULT_ACCESS_TOKEN = process.env + .ENGINE_VAULT_ACCESS_TOKEN as string; + +export const SERVER_WALLET = Engine.serverWallet({ + address: BACKEND_WALLET_ADDRESS, + client: THIRDWEB_CLIENT, + vaultAccessToken: ENGINE_VAULT_ACCESS_TOKEN, + chain: sepolia, +}); diff --git a/packages/thirdweb/package.json b/packages/thirdweb/package.json index 6bb4a97d9fc..44fb1f780da 100644 --- a/packages/thirdweb/package.json +++ b/packages/thirdweb/package.json @@ -297,7 +297,7 @@ "knip": "knip", "build:generate": "bun scripts/generate/generate.ts", "build:generate-wallets": "bun scripts/wallets/generate.ts", - "dev": "tsc --project ./tsconfig.build.json --module es2020 --outDir ./dist/esm --watch", + "dev": "tsc --project ./tsconfig.build.json --module nodenext --moduleResolution nodenext --outDir ./dist/esm --watch", "dev:cjs": "printf '{\"type\":\"commonjs\"}' > ./dist/cjs/package.json && tsc --noCheck --project ./tsconfig.build.json --module commonjs --outDir ./dist/cjs --verbatimModuleSyntax false --watch", "dev:esm": "printf '{\"type\": \"module\",\"sideEffects\":false}' > ./dist/esm/package.json && tsc --noCheck --project ./tsconfig.build.json --module es2020 --outDir ./dist/esm --watch", "build": "pnpm clean && pnpm build:types && pnpm build:cjs && pnpm build:esm", diff --git a/packages/thirdweb/src/login/client/index.ts b/packages/thirdweb/src/login/client/index.ts index af3e537ef7e..9bb3a81d4ea 100644 --- a/packages/thirdweb/src/login/client/index.ts +++ b/packages/thirdweb/src/login/client/index.ts @@ -1 +1 @@ -export { login, type LoginOptions as LoginParams } from "./login.js"; +export { login, type LoginOptions, type LoginResult } from "./login.js"; diff --git a/packages/thirdweb/src/login/client/login.ts b/packages/thirdweb/src/login/client/login.ts index af758d42324..aa97ccf9e51 100644 --- a/packages/thirdweb/src/login/client/login.ts +++ b/packages/thirdweb/src/login/client/login.ts @@ -31,6 +31,7 @@ export type LoginOptions = ( } ) & { client: ThirdwebClient; + chain: Chain; options?: { sponsorGas?: boolean; redirectUrl?: string; @@ -40,10 +41,12 @@ export type LoginOptions = ( baseURL?: string; }; +export type LoginResult = Awaited>; + export async function login(loginOptions: LoginOptions) { const IAW = inAppWallet({ auth: { - mode: "redirect", + mode: "popup", options: [], redirectUrl: loginOptions.options?.redirectUrl, passkeyDomain: loginOptions.options?.passkeyDomain, @@ -62,6 +65,7 @@ export async function login(loginOptions: LoginOptions) { client: loginOptions.client, strategy: "jwt", jwt: loginOptions.jwt, + chain: loginOptions.chain, }); return mapAccount(account, IAW, loginOptions.baseURL); @@ -87,6 +91,7 @@ export async function login(loginOptions: LoginOptions) { phoneNumber: loginOptions.phoneNumber, verificationCode, client: loginOptions.client, + chain: loginOptions.chain, }); return mapAccount(account, IAW, loginOptions.baseURL); @@ -115,6 +120,7 @@ export async function login(loginOptions: LoginOptions) { email: loginOptions.email, verificationCode, client: loginOptions.client, + chain: loginOptions.chain, }); return mapAccount(account, IAW, loginOptions.baseURL); @@ -141,6 +147,7 @@ export async function login(loginOptions: LoginOptions) { const account = await IAW.connect({ client: loginOptions.client, strategy: loginOptions.type, + chain: loginOptions.chain, }); return mapAccount(account, IAW, loginOptions.baseURL); diff --git a/packages/thirdweb/src/login/index.ts b/packages/thirdweb/src/login/index.ts index dcc46893a9e..970a2ca664a 100644 --- a/packages/thirdweb/src/login/index.ts +++ b/packages/thirdweb/src/login/index.ts @@ -2,7 +2,7 @@ export * as Client from "./client/index.js"; export * as Server from "./server/index.js"; // client -export { login, type LoginOptions as LoginParams } from "./client/login.js"; +export { login, type LoginOptions, type LoginResult } from "./client/login.js"; // server export {