Skip to content

Add Login component with Google authentication support #7107

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: Add_login_functionality_with_multiple_authentication_methods
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions apps/playground-web/src/app/connect/login/api/[...all]/route.ts
Original file line number Diff line number Diff line change
@@ -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,
}),
);
34 changes: 34 additions & 0 deletions apps/playground-web/src/app/connect/login/page.tsx
Original file line number Diff line number Diff line change
@@ -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",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The metadata description is currently set as "TODO". This placeholder should be replaced with a meaningful description that accurately represents the Login page functionality. A good description will improve SEO and provide users with context about the page's purpose when shared on social media or in search results.

Suggested change
description: "TODO",
description: "Sign in to access your Playground account and explore our interactive development environment. Secure login to all Playground features and services.",

Spotted by Diamond

Is this helpful? React 👍 or 👎 to let us know.

};

export default function Page() {
return (
<ThirdwebProvider>
<PageLayout
title="Login"
description={
<>Let users login to your app using any authentication provider.</>
}
docsLink="https://portal.thirdweb.com/typescript/v5/auth?utm_source=playground"
>
<div className="flex flex-col gap-14">
<CodeExample
lang="ts"
code={"TODO"}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code prop is currently set to "TODO", which needs to be replaced with actual code that demonstrates the Login component implementation. Consider adding a code snippet that shows how to use the Google authentication flow, matching what's being demonstrated in the BasicLoginExample component. This would provide users with a complete example they can reference.

Suggested change
code={"TODO"}
code={`import { useCallback } from 'react';
import { useGoogleLogin } from '@react-oauth/google';
import { Button } from '@your-ui-library/components';
export function LoginWithGoogle() {
const login = useGoogleLogin({
onSuccess: (tokenResponse) => {
console.log('Login successful:', tokenResponse);
// Handle successful login, e.g., store tokens, redirect user
},
onError: (error) => {
console.error('Login failed:', error);
// Handle login failure
},
flow: 'implicit',
});
const handleLogin = useCallback(() => {
login();
}, [login]);
return (
<Button onClick={handleLogin} variant="primary">
Sign in with Google
</Button>
);
}`}

Spotted by Diamond

Is this helpful? React 👍 or 👎 to let us know.

preview={<BasicLoginExample />}
/>
</div>
</PageLayout>
</ThirdwebProvider>
);
}
18 changes: 5 additions & 13 deletions apps/playground-web/src/app/engine/actions.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
73 changes: 73 additions & 0 deletions apps/playground-web/src/components/login/basic-example.tsx
Original file line number Diff line number Diff line change
@@ -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<Login.Client.LoginResult>();
const otpRef = useRef<HTMLInputElement>(null);

if (!account) {
return (
<div className="flex flex-col gap-4">
<Button
onClick={async () => {
const account = await Login.Client.login({
type: "google",
client: THIRDWEB_CLIENT,
baseURL: `${window.location.origin}/connect/login/api/auth`,
chain: sepolia,
});
setAccount(account);
}}
>
Login with Google
</Button>
</div>
);
}

if (account.status === "requires_otp") {
return (
<div className="flex flex-col gap-4">
<Input ref={otpRef} placeholder="OTP" />
<Button
onClick={async () => {
if (!otpRef.current?.value) {
return;
}
setAccount(await account.verifyOtp(otpRef.current?.value));
}}
>
Verify OTP
</Button>
</div>
);
}

return (
<div className="flex flex-col gap-4">
<p>Logged in as: {account.id}</p>
<Button
onClick={async () => {
const jwt = await account.getJWT();
alert(`JWT: ${jwt}`);
}}
>
Get JWT
</Button>
<Button
onClick={async () => {
await account.logout();
setAccount(undefined);
}}
>
Logout
</Button>
</div>
);
}
1 change: 1 addition & 0 deletions apps/playground-web/src/lib/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
2 changes: 1 addition & 1 deletion apps/playground-web/src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
Expand Down
15 changes: 15 additions & 0 deletions apps/playground-web/src/lib/server-wallet.ts
Original file line number Diff line number Diff line change
@@ -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,
});
2 changes: 1 addition & 1 deletion packages/thirdweb/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion packages/thirdweb/src/login/client/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { login, type LoginOptions as LoginParams } from "./login.js";
export { login, type LoginOptions, type LoginResult } from "./login.js";
9 changes: 8 additions & 1 deletion packages/thirdweb/src/login/client/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
}
) & {
client: ThirdwebClient;
chain: Chain;
options?: {
sponsorGas?: boolean;
redirectUrl?: string;
Expand All @@ -40,10 +41,12 @@
baseURL?: string;
};

export type LoginResult = Awaited<ReturnType<typeof login>>;

export async function login(loginOptions: LoginOptions) {
const IAW = inAppWallet({
auth: {
mode: "redirect",
mode: "popup",

Check warning on line 49 in packages/thirdweb/src/login/client/login.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/login/client/login.ts#L49

Added line #L49 was not covered by tests
options: [],
redirectUrl: loginOptions.options?.redirectUrl,
passkeyDomain: loginOptions.options?.passkeyDomain,
Expand All @@ -62,6 +65,7 @@
client: loginOptions.client,
strategy: "jwt",
jwt: loginOptions.jwt,
chain: loginOptions.chain,

Check warning on line 68 in packages/thirdweb/src/login/client/login.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/login/client/login.ts#L68

Added line #L68 was not covered by tests
});

return mapAccount(account, IAW, loginOptions.baseURL);
Expand All @@ -87,6 +91,7 @@
phoneNumber: loginOptions.phoneNumber,
verificationCode,
client: loginOptions.client,
chain: loginOptions.chain,

Check warning on line 94 in packages/thirdweb/src/login/client/login.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/login/client/login.ts#L94

Added line #L94 was not covered by tests
});

return mapAccount(account, IAW, loginOptions.baseURL);
Expand Down Expand Up @@ -115,6 +120,7 @@
email: loginOptions.email,
verificationCode,
client: loginOptions.client,
chain: loginOptions.chain,

Check warning on line 123 in packages/thirdweb/src/login/client/login.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/login/client/login.ts#L123

Added line #L123 was not covered by tests
});

return mapAccount(account, IAW, loginOptions.baseURL);
Expand All @@ -141,6 +147,7 @@
const account = await IAW.connect({
client: loginOptions.client,
strategy: loginOptions.type,
chain: loginOptions.chain,

Check warning on line 150 in packages/thirdweb/src/login/client/login.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/login/client/login.ts#L150

Added line #L150 was not covered by tests
});

return mapAccount(account, IAW, loginOptions.baseURL);
Expand Down
2 changes: 1 addition & 1 deletion packages/thirdweb/src/login/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading