Skip to content

Commit 07ad025

Browse files
feat: Add email verification flow to in-app wallet (#5796)
1 parent 5293450 commit 07ad025

File tree

3 files changed

+80
-78
lines changed

3 files changed

+80
-78
lines changed

apps/playground-web/src/app/connect/in-app-wallet/page.tsx

Lines changed: 23 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ function AnyAuth() {
7474
/>
7575
</div>
7676
<div className="space-y-2">
77-
<h3 className="mb-3 font-medium text-lg">Custom Auth and UI</h3>
77+
<h3 className="mb-3 font-medium text-lg">Custom UI</h3>
7878
<p className="max-w-[600px]">
7979
Customize the login UI and integrate with your existing user base. No
8080
limits on customizations and auth methods.
@@ -85,63 +85,35 @@ function AnyAuth() {
8585
<CustomLoginForm />
8686
</div>
8787
}
88-
code={`import { useMutation } from "@tanstack/react-query";
89-
import { useState } from "react";
88+
code={`import { useState } from "react";
9089
import { useConnect } from "thirdweb/react";
91-
import { inAppWallet } from "thirdweb/wallets";
90+
import { inAppWallet, preAuthenticate } from "thirdweb/wallets/in-app";
9291
93-
export function CustomLoginForm() {
94-
const [email, setEmail] = useState("");
92+
export function CustomLoginUi() {
9593
const { connect, isConnecting, error } = useConnect();
9694
97-
const { mutate: loginWithCustomAuth } = useMutation({
98-
mutationFn: async (email: string) => {
99-
const wallet = await connect(async () => {
100-
const wallet = inAppWallet();
101-
await wallet.connect({
102-
strategy: "auth_endpoint",
103-
client,
104-
// your own custom auth payload here
105-
payload: JSON.stringify({
106-
userId: email,
107-
email,
108-
}),
109-
});
110-
return wallet;
95+
const preLogin = async (email: string) => {
96+
// send email verification code
97+
await preAuthenticate({
98+
client,
99+
strategy: "email",
100+
email,
101+
});
102+
};
103+
104+
const handleLogin = async (email: string, verificationCode: string) => {
105+
// verify email with verificationCode and connect
106+
connect(async () => {
107+
const wallet = inAppWallet();
108+
await wallet.connect({
109+
client,
110+
strategy: "email",
111+
email,
112+
verificationCode,
111113
});
112114
return wallet;
113-
}
114-
});
115-
116-
const handleSubmit = (e: React.FormEvent) => {
117-
e.preventDefault();
118-
loginWithCustomAuth(email);
115+
});
119116
};
120-
121-
return (
122-
<form onSubmit={handleSubmit}>
123-
<div>
124-
<label htmlFor="email">
125-
Email Address
126-
</label>
127-
<input
128-
type="email"
129-
id="email"
130-
value={email}
131-
onChange={(e) => setEmail(e.target.value)}
132-
placeholder="Enter your email"
133-
required
134-
/>
135-
<button
136-
type="submit"
137-
disabled={isConnecting || !email}
138-
>
139-
{isConnecting ? "Submitting..." : "Submit"}
140-
</button>
141-
{error && <p>{error.message}</p>}
142-
</div>
143-
</form>
144-
);
145117
}
146118
`}
147119
lang="tsx"
Lines changed: 55 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,46 @@
11
"use client";
22

33
import { THIRDWEB_CLIENT } from "@/lib/client";
4-
import { useMutation } from "@tanstack/react-query";
54
import { useState } from "react";
65
import { useActiveAccount, useConnect } from "thirdweb/react";
7-
import { inAppWallet } from "thirdweb/wallets";
6+
import { inAppWallet, preAuthenticate } from "thirdweb/wallets";
87
import { InAppConnectEmbed } from "./connect-button";
98

109
export function CustomLoginForm() {
1110
const [email, setEmail] = useState("");
11+
const [verificationCode, setVerificationCode] = useState("");
12+
const [screen, setScreen] = useState<"login" | "verify">("login");
1213
const { connect, isConnecting, error } = useConnect();
1314
const account = useActiveAccount();
1415

15-
const { mutate: loginWithCustomAuthEndpoint } = useMutation({
16-
mutationFn: async (email: string) => {
17-
const wallet = await connect(async () => {
18-
const wallet = inAppWallet();
19-
await wallet.connect({
20-
strategy: "auth_endpoint",
21-
client: THIRDWEB_CLIENT,
22-
payload: JSON.stringify({
23-
userId: email,
24-
email,
25-
}),
26-
});
27-
return wallet;
16+
const sendEmailVerificationCode = async (email: string) => {
17+
setScreen("verify");
18+
await preAuthenticate({
19+
client: THIRDWEB_CLIENT,
20+
strategy: "email",
21+
email,
22+
});
23+
};
24+
25+
const loginWithEmail = async (email: string, verificationCode: string) => {
26+
connect(async () => {
27+
const wallet = inAppWallet();
28+
await wallet.connect({
29+
strategy: "email",
30+
client: THIRDWEB_CLIENT,
31+
email,
32+
verificationCode,
2833
});
2934
return wallet;
30-
},
31-
});
32-
33-
const handleSubmit = (e: React.FormEvent) => {
34-
e.preventDefault();
35-
loginWithCustomAuthEndpoint(email);
35+
});
3636
};
37+
3738
if (account) {
3839
return <InAppConnectEmbed />;
3940
}
4041

41-
return (
42-
<form onSubmit={handleSubmit} className="mt-4">
42+
if (screen === "login") {
43+
return (
4344
<div className="flex flex-col space-y-2">
4445
<label htmlFor="email" className="font-medium text-sm">
4546
Email Address
@@ -55,13 +56,42 @@ export function CustomLoginForm() {
5556
/>
5657
<button
5758
type="submit"
59+
onClick={() => sendEmailVerificationCode(email)}
5860
className="rounded-lg bg-blue-500 px-4 py-2 text-white transition-colors enabled:hover:bg-blue-600"
5961
disabled={isConnecting || !email}
6062
>
6163
{isConnecting ? "Submitting..." : "Submit"}
6264
</button>
6365
{error && <p className="max-w-[300px] text-red-500">{error.message}</p>}
6466
</div>
65-
</form>
66-
);
67+
);
68+
}
69+
70+
if (screen === "verify") {
71+
return (
72+
<div className="flex flex-col space-y-2">
73+
<label htmlFor="verification-code" className="font-medium text-sm">
74+
Verification Code
75+
</label>
76+
<input
77+
type="text"
78+
id="verification-code"
79+
value={verificationCode}
80+
onChange={(e) => setVerificationCode(e.target.value)}
81+
className="rounded-lg border px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
82+
placeholder="Enter the code you received"
83+
required
84+
/>
85+
<button
86+
type="submit"
87+
onClick={() => loginWithEmail(email, verificationCode)}
88+
className="rounded-lg bg-blue-500 px-4 py-2 text-white transition-colors enabled:hover:bg-blue-600"
89+
disabled={isConnecting || !verificationCode}
90+
>
91+
{isConnecting ? "Submitting..." : "Submit"}
92+
</button>
93+
{error && <p className="max-w-[300px] text-red-500">{error.message}</p>}
94+
</div>
95+
);
96+
}
6797
}

apps/portal/src/app/connect/in-app-wallet/guides/build-your-own-ui/page.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,9 @@ const preLogin = async (email: string) => {
135135
});
136136
};
137137

138-
const handleLogin = async (email: string, verificationCode: string) => {
138+
const handleLogin = (email: string, verificationCode: string) => {
139139
// verify email and connect
140-
await connect(() => {
140+
connect(async () => {
141141
const wallet = inAppWallet();
142142
await wallet.connect({
143143
client,

0 commit comments

Comments
 (0)