这是用户在 2025-7-4 15:16 为 https://deepwiki.com/search/twitteremail-email-twitter-ema_239de604-a425-493f-b340-73fa340a2caf 保存的双语快照页面,由 沉浸式翻译 提供双语支持。了解如何保存?
return to better-auth/better-auth
如何同时支持twitter和email两种登录方式,并且 email 绑定了twitter 就可以直接使用推特,得到的是和email一样的userId
Deep
Searched across better-auth/better-auth

在better-auth中,你可以同时支持Twitter和Email两种登录方式,并且实现账户关联功能,让用户通过不同方式登录时获得相同的userId。

核心架构

Better Auth将**用户(User)账户(Account)**分离设计。一个User代表具有个人资料信息的用户,而Account代表连接到该用户的特定认证方法。 users-accounts.mdx:1-50

配置方法

你需要在betterAuth配置中同时启用email认证和Twitter社交登录:

1. Email/Password认证配置

通过emailAndPassword选项启用邮箱密码登录

2. Twitter社交登录配置

socialProviders数组中添加Twitter provider配置

Twitter provider使用OAuth 2.0流程,默认请求以下权限范围:users.readtweet.readoffline.accessusers.emailtwitter.ts:107-109

账户关联机制

Better Auth通过handleOAuthUserInfo函数实现自动账户关联: link-account.ts:41-88

关联流程

关联条件

账户能够自动关联需要满足以下条件: link-account.ts:48-66

  1. 用户邮箱地址匹配现有用户
  2. 账户尚未关联到其他用户
  3. 满足以下任一条件:
    • 邮箱已验证 (emailVerified = true)
    • 或者提供商在trustedProviders列表中
  4. 系统启用了账户关联功能

最终效果

当配置完成后:

  1. 首次注册:用户可以选择邮箱注册或Twitter登录,都会创建独立的User记录和对应的Account记录

  2. 账户关联

    • 如果用户先用邮箱注册,后用Twitter登录相同邮箱,系统会自动将Twitter账户关联到现有用户
    • 如果用户先用Twitter注册,后用相同邮箱注册,也会关联到同一用户
  3. 统一身份:无论用户通过哪种方式登录,都会获得相同的userId,确保用户数据的一致性

  4. 灵活登录:用户可以随时选择邮箱密码或Twitter任一方式登录,享受相同的用户体验

注意事项

  • Twitter provider需要配置正确的客户端ID和密钥
  • 确保Twitter应用有权限访问用户邮箱信息
  • 考虑设置trustedProviders以简化关联流程
  • 账户关联基于邮箱匹配,确保邮箱验证流程的安全性

通过这种设计,better-auth实现了多种认证方式的无缝整合,让用户享受灵活便捷的登录体验,同时保持统一的用户身份标识。

1---
2title: User & Accounts
3description: User and account management.
4---
5
6Beyond authenticating users, Better Auth also provides a set of methods to manage users. This includes, updating user information, changing passwords, and more.
7
8The user table stores the authentication data of the user [Click here to view the schema](/docs/concepts/database#user).
9
10The user table can be extended using [additional fields](/docs/concepts/database#extending-core-schema) or by plugins to store additional data.
11
12## Update User
13
14### Update User Information
15
16To update user information, you can use the `updateUser` function provided by the client. The `updateUser` function takes an object with the following properties:
17
18```ts
19await authClient.updateUser({
20    image: "https://example.com/image.jpg",
21    name: "John Doe",
22})
23```
24### Change Email
25
26To allow users to change their email, first enable the `changeEmail` feature, which is disabled by default. Set `changeEmail.enabled` to `true`:
27
28```ts
29export const auth = betterAuth({
30    user: {
31        changeEmail: {
32            enabled: true,
33        }
34    }
35})
36```
37
38For users with a verified email, provide the `sendChangeEmailVerification` function. This function triggers when a user changes their email, sending a verification email with a URL and token. If the current email isn't verified, the change happens immediately without verification.
39
40```ts
41export const auth = betterAuth({
42    user: {
43        changeEmail: {
44            enabled: true,
45            sendChangeEmailVerification: async ({ user, newEmail, url, token }, request) => {
46                await sendEmail({
47                    to: user.email, // verification email must be sent to the current user email to approve the change
48                    subject: 'Approve email change',
49                    text: `Click the link to approve the change: ${url}`
50                })
51            }
52        }
53    }
104		createAuthorizationURL(data) {
105			const _scopes = options.disableDefaultScope
106				? []
107				: ["users.read", "tweet.read", "offline.access", "users.email"];
108			options.scope && _scopes.push(...options.scope);
109			data.scopes && _scopes.push(...data.scopes);
110			return createAuthorizationURL({
111				id: "twitter",
112				options,
return to better-auth/better-auth
``` import { env } from "~/env"; import { db } from "~/server/modules/db"; import { betterAuth } from "better-auth"; import { bearer } from "better-auth/plugins"; import { nextCookies } from "better-a...
Fast
Searched across better-auth/better-auth

根据你的配置,你想要自定义OAuth用户信息处理,让email不使用Twitter绑定的email,而是让用户自定义。

在better-auth中,handleOAuthUserInfo是内部函数,不能直接在配置中覆盖。 link-account.ts:7-22 但你可以通过以下方式实现你的需求:

方案1:使用mapProfileToUser自定义用户映射

你已经在配置中使用了mapProfileToUser,可以扩展它来处理email:

export const auth = betterAuth({  
  // ... 其他配置  
  socialProviders: {  
    twitter: {  
      clientId: env.AUTH_TWITTER_ID,  
      clientSecret: env.AUTH_TWITTER_SECRET,  
      scope: [  
        "tweet.read",  
        "tweet.write",   
        "users.read",  
        "offline.access",  
        "media.write",  
      ],  
      overrideUserInfoOnSignIn: true,  
      mapProfileToUser: (profile) => {  
        return {  
          userName: profile.data.username,  
          // 不设置email,让用户后续自定义  
          email: undefined, // 或者设置为临时email  
        };  
      },  
    },  
  },  
  // 启用账户关联但不基于email  
  account: {  
    updateAccountOnSignIn: true,  
    accountLinking: {  
      enabled: false, // 禁用自动关联  
    }  
  },  
});

方案2:使用emailAndPassword插件配合手动关联

添加email/password认证,让用户可以设置自定义email:

import { emailAndPassword } from "better-auth/plugins";  
  
export const auth = betterAuth({  
  // ... 其他配置  
  emailAndPassword: {  
    enabled: true,  
    requireEmailVerification: false, // 可选  
  },  
  socialProviders: {  
    twitter: {  
      // ... Twitter配置  
      mapProfileToUser: (profile) => {  
        return {  
          userName: profile.data.username,  
          // 使用临时email或不设置email  
          email: `temp_${profile.data.id}@twitter.local`,  
        };  
      },  
    },  
  },  
  plugins: [  
    nextCookies(),   
    bearer(),  
    emailAndPassword()  
  ],  
});

方案3:后续手动关联账户

用户可以在登录后手动关联email账户: users-accounts.mdx:337-378

// 客户端代码  
await authClient.linkSocial({  
  provider: "credential", // email/password  
  callbackURL: "/profile"  
});

实现流程

  1. Twitter登录:用户通过Twitter登录,获得临时或无email的账户
  2. 设置自定义email:用户在个人资料页面设置自己的email
  3. 验证email:通过email验证流程确认email所有权
  4. 账户关联:将email账户关联到Twitter账户,实现统一userId

这种方式让用户完全控制自己的email设置,而不依赖Twitter提供的email信息。 link-account.ts:111-122

Notes

handleOAuthUserInfo函数是better-auth内部的账户关联逻辑,主要处理OAuth登录时的用户信息和账户关联。 sign-in.ts:274-292 虽然不能直接覆盖,但通过mapProfileToUser和账户关联配置可以实现类似的自定义效果。

Wiki pages you might want to explore:

334});
335```
336
337#### Manually Linking Accounts
338
339Users already signed in can manually link their account to additional social providers or credential-based accounts.
340
341- **Linking Social Accounts:** Use the `linkSocial` method on the client to link a social provider to the user's account.
342
343  ```ts
344  await authClient.linkSocial({
345      provider: "google", // Provider to link
346      callbackURL: "/callback" // Callback URL after linking completes
347  });
348  ```
349
350  You can also request specific scopes when linking a social account, which can be different from the scopes used during the initial authentication:
351
352  ```ts
353  await authClient.linkSocial({
354      provider: "google",
355      callbackURL: "/callback",
356      scopes: ["https://www.googleapis.com/auth/drive.readonly"] // Request additional scopes
357  });
358  ```
359
360  If you want your users to be able to link a social account with a different email address than the user, or if you want to use a provider that does not return email addresses, you will need to enable this in the account linking settings. 
361  ```ts title="auth.ts"
362  export const auth = betterAuth({
363      account: {
364          accountLinking: {
365              allowDifferentEmails: true
366          }
367      },
368  });
369  ```
370
371- **Linking Credential-Based Accounts:** To link a credential-based account (e.g., email and password), users can initiate a "forgot password" flow, or you can call the `setPassword` method on the server. 
372
373  ```ts
374  await auth.api.setPassword({
375      headers: /* headers containing the user's session token */,
376      password: /* new password */
377  });
378  ```
379
380<Callout>
381`setPassword` can't be called from the client for security reasons.
271					message: BASE_ERROR_CODES.USER_EMAIL_NOT_FOUND,
272				});
273			}
274			const data = await handleOAuthUserInfo(c, {
275				userInfo: {
276					...userInfo.user,
277					email: userInfo.user.email,
278					id: userInfo.user.id,
279					name: userInfo.user.name || "",
280					image: userInfo.user.image,
281					emailVerified: userInfo.user.emailVerified || false,
282				},
283				account: {
284					providerId: provider.id,
285					accountId: userInfo.user.id,
286					accessToken: c.body.idToken.accessToken,
287				},
288				callbackURL: c.body.callbackURL,
289				disableSignUp:
290					(provider.disableImplicitSignUp && !c.body.requestSignUp) ||
291					provider.disableSignUp,
292			});
293			if (data.error) {
294				throw new APIError("UNAUTHORIZED", {
295					message: data.error,
return to better-auth/better-auth
你这样修改,怎么让email login 和 twiiter login 串联呢, 假设我已经有了一个bing account page
Deep
Searched across better-auth/better-auth

Better-auth integrates email and Twitter login methods with account linking through a well-structured architecture that separates Users from Accounts, enabling multiple authentication methods to be linked to a single user profile.

Architecture Overview

Better-auth uses a User-Account separation model where Users represent individuals with profile information, while Accounts represent specific authentication methods (email/password, Twitter, etc.) linked to those users. users-accounts.mdx:1-50

Account Linking Implementation

Core API Endpoints

The system provides dedicated API endpoints for account linking functionality:

  • linkSocialAccount endpoint - Initiates the OAuth flow for linking social providers like Twitter account.ts:88
  • OAuth callback handling - Processes the redirect from Twitter and completes the account linking callback.ts:133-134

Account Linking Flow

The account linking process follows this pattern:

  1. Initiation: User triggers account linking from their management page
  2. OAuth Flow: System redirects to Twitter for authorization
  3. Callback Processing: Twitter redirects back with authorization code
  4. Account Creation: System validates and creates the linked account record link-account.ts:41-84

Twitter Provider Integration

The Twitter provider is implemented as a social provider that supports the account linking flow: twitter.ts:8-189

User Management Page Integration

Demo Implementation

The better-auth demo shows practical account management implementation:

  • Dashboard page that fetches user session and account data page.tsx:1-50
  • Account switching component for managing multiple linked accounts account-switch.tsx:1-50
  • User management components that handle profile and account linking user-card.tsx:1-50

Configuration Options

Account linking behavior can be configured through options: options.ts:471-495

Email and Twitter Integration

Email Authentication

Email authentication works as a primary account method that can have social providers linked to it.

Twitter Linking Process

When linking Twitter to an existing email account:

  1. User clicks "Link Twitter" in their account management page
  2. System calls the linkSocialAccount endpoint with Twitter as the provider
  3. User is redirected to Twitter for authorization
  4. After approval, Twitter redirects back to the callback endpoint
  5. System creates a new Account record linking the Twitter account to the existing User oauth.mdx:64

Security Considerations

The linking process includes state validation and user session verification to ensure secure account linking: callback.ts:162

Notes

  • The system allows multiple authentication methods per user while maintaining data consistency
  • Account linking is configurable and can be enabled/disabled per provider
  • The demo implementation provides a complete reference for building account management interfaces
  • All linking operations require user authentication and proper session management
  • The architecture supports both linking new accounts and unlinking existing ones through dedicated API endpoints
1---
2title: User & Accounts
3description: User and account management.
4---
5
6Beyond authenticating users, Better Auth also provides a set of methods to manage users. This includes, updating user information, changing passwords, and more.
7
8The user table stores the authentication data of the user [Click here to view the schema](/docs/concepts/database#user).
9
10The user table can be extended using [additional fields](/docs/concepts/database#extending-core-schema) or by plugins to store additional data.
11
12## Update User
13
14### Update User Information
15
16To update user information, you can use the `updateUser` function provided by the client. The `updateUser` function takes an object with the following properties:
17
18```ts
19await authClient.updateUser({
20    image: "https://example.com/image.jpg",
21    name: "John Doe",
22})
23```
24### Change Email
25
26To allow users to change their email, first enable the `changeEmail` feature, which is disabled by default. Set `changeEmail.enabled` to `true`:
27
28```ts
29export const auth = betterAuth({
30    user: {
31        changeEmail: {
32            enabled: true,
33        }
34    }
35})
36```
37
38For users with a verified email, provide the `sendChangeEmailVerification` function. This function triggers when a user changes their email, sending a verification email with a URL and token. If the current email isn't verified, the change happens immediately without verification.
39
40```ts
41export const auth = betterAuth({
42    user: {
43        changeEmail: {
44            enabled: true,
45            sendChangeEmailVerification: async ({ user, newEmail, url, token }, request) => {
46                await sendEmail({
47                    to: user.email, // verification email must be sent to the current user email to approve the change
48                    subject: 'Approve email change',
49                    text: `Click the link to approve the change: ${url}`
50                })
51            }
52        }
53    }
85			})),
86		);
87	},
88);
89export const linkSocialAccount = createAuthEndpoint(
90	"/link-social",
91	{
130				userInfo.id,
131			);
132
133			if (existingAccount) {
134				if (existingAccount.userId.toString() !== link.userId.toString()) {
135					return redirectOnError("account_already_linked_to_different_user");
136				}
137				const updateData = Object.fromEntries(
159					},
160					c,
161				);
162				if (!newAccount) {
163					return redirectOnError("unable_to_link_account");
164				}
165			}
5	refreshAccessToken,
6	validateAuthorizationCode,
7} from "../oauth2";
8
9export interface TwitterProfile {
10	data: {
11		/**
12		 * Unique identifier of this user. This is returned as a string in order to avoid complications with languages and tools
13		 * that cannot handle large integers.
14		 */
15		id: string;
16		/** The friendly name of this user, as shown on their profile. */
17		name: string;
18		/** The email address of this user. */
19		email?: string;
20		/** The Twitter handle (screen name) of this user. */
21		username: string;
22		/**
23		 * The location specified in the user's profile, if the user provided one.
24		 * As this is a freeform value, it may not indicate a valid location, but it may be fuzzily evaluated when performing searches with location queries.
25		 *
26		 * To return this field, add `user.fields=location` in the authorization request's query parameter.
27		 */
28		location?: string;
29		/**
30		 * This object and its children fields contain details about text that has a special meaning in the user's description.
31		 *
32		 *To return this field, add `user.fields=entities` in the authorization request's query parameter.
33		 */
34		entities?: {
35			/** Contains details about the user's profile website. */
36			url: {
37				/** Contains details about the user's profile website. */
38				urls: Array<{
39					/** The start position (zero-based) of the recognized user's profile website. All start indices are inclusive. */
40					start: number;
41					/** The end position (zero-based) of the recognized user's profile website. This end index is exclusive. */
42					end: number;
43					/** The URL in the format entered by the user. */
44					url: string;
45					/** The fully resolved URL. */
46					expanded_url: string;
47					/** The URL as displayed in the user's profile. */
48					display_url: string;
49				}>;
50			};
51			/** Contains details about URLs, Hashtags, Cashtags, or mentions located within a user's description. */
52			description: {
53				hashtags: Array<{
54					start: number;
55					end: number;
56					tag: string;
57				}>;
58			};
59		};
60		/**
61		 * Indicate if this user is a verified Twitter user.
62		 *
63		 * To return this field, add `user.fields=verified` in the authorization request's query parameter.
64		 */
65		verified?: boolean;
66		/**
67		 * The text of this user's profile description (also known as bio), if the user provided one.
68		 *
69		 * To return this field, add `user.fields=description` in the authorization request's query parameter.
70		 */
71		description?: string;
72		/**
73		 * The URL specified in the user's profile, if present.
74		 *
75		 * To return this field, add `user.fields=url` in the authorization request's query parameter.
76		 */
77		url?: string;
78		/** The URL to the profile image for this user, as shown on the user's profile. */
79		profile_image_url?: string;
80		protected?: boolean;
81		/**
82		 * Unique identifier of this user's pinned Tweet.
83		 *
84		 *  You can obtain the expanded object in `includes.tweets` by adding `expansions=pinned_tweet_id` in the authorization request's query parameter.
85		 */
86		pinned_tweet_id?: string;
87		created_at?: string;
88	};
89	includes?: {
90		tweets?: Array<{
91			id: string;
92			text: string;
93		}>;
94	};
95	[claims: string]: unknown;
96}
97
98export interface TwitterOption extends ProviderOptions<TwitterProfile> {}
99
100export const twitter = (options: TwitterOption) => {
101	return {
102		id: "twitter",
103		name: "Twitter",
104		createAuthorizationURL(data) {
105			const _scopes = options.disableDefaultScope
106				? []
107				: ["users.read", "tweet.read", "offline.access", "users.email"];
108			options.scope && _scopes.push(...options.scope);
109			data.scopes && _scopes.push(...data.scopes);
110			return createAuthorizationURL({
111				id: "twitter",
112				options,
113				authorizationEndpoint: "https://x.com/i/oauth2/authorize",
114				scopes: _scopes,
115				state: data.state,
116				codeVerifier: data.codeVerifier,
117				redirectURI: data.redirectURI,
118			});
119		},
120		validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
121			return validateAuthorizationCode({
122				code,
123				codeVerifier,
124				authentication: "basic",
125				redirectURI,
126				options,
127				tokenEndpoint: "https://api.x.com/2/oauth2/token",
128			});
129		},
130
131		refreshAccessToken: options.refreshAccessToken
132			? options.refreshAccessToken
133			: async (refreshToken) => {
134					return refreshAccessToken({
135						refreshToken,
136						options: {
137							clientId: options.clientId,
138							clientKey: options.clientKey,
139							clientSecret: options.clientSecret,
140						},
141						tokenEndpoint: "https://api.x.com/2/oauth2/token",
142					});
143				},
144		async getUserInfo(token) {
145			if (options.getUserInfo) {
146				return options.getUserInfo(token);
147			}
148			const { data: profile, error: profileError } =
149				await betterFetch<TwitterProfile>(
150					"https://api.x.com/2/users/me?user.fields=profile_image_url",
151					{
152						method: "GET",
153						headers: {
154							Authorization: `Bearer ${token.accessToken}`,
155						},
156					},
157				);
158
159			if (profileError) {
160				return null;
161			}
162
163			const { data: emailData, error: emailError } = await betterFetch<{
164				data: { confirmed_email: string };
165			}>("https://api.x.com/2/users/me?user.fields=confirmed_email", {
166				method: "GET",
167				headers: {
168					Authorization: `Bearer ${token.accessToken}`,
169				},
170			});
171			let emailVerified = false;
172			if (!emailError && emailData?.data?.confirmed_email) {
173				profile.data.email = emailData.data.confirmed_email;
174				emailVerified = true;
175			}
176			const userMap = await options.mapProfileToUser?.(profile);
177			return {
178				user: {
179					id: profile.data.id,
180					name: profile.data.name,
181					email: profile.data.email || profile.data.username || null,
182					image: profile.data.profile_image_url,
183					emailVerified: emailVerified,
184					...userMap,
185				},
186				data: profile,
187			};
188		},
189		options,
190	} satisfies OAuthProvider<TwitterProfile>;
191};
1import { auth } from "@/lib/auth";
2import { headers } from "next/headers";
3import { redirect } from "next/navigation";
4import UserCard from "./user-card";
5import { OrganizationCard } from "./organization-card";
6import AccountSwitcher from "@/components/account-switch";
7
8export default async function DashboardPage() {
9	const [session, activeSessions, deviceSessions, organization, subscriptions] =
10		await Promise.all([
11			auth.api.getSession({
12				headers: await headers(),
13			}),
14			auth.api.listSessions({
15				headers: await headers(),
16			}),
17			auth.api.listDeviceSessions({
18				headers: await headers(),
19			}),
20			auth.api.getFullOrganization({
21				headers: await headers(),
22			}),
23			auth.api.listActiveSubscriptions({
24				headers: await headers(),
25			}),
26		]).catch((e) => {
27			console.log(e);
28			throw redirect("/sign-in");
29		});
30	return (
31		<div className="w-full">
32			<div className="flex gap-4 flex-col">
33				<AccountSwitcher
34					sessions={JSON.parse(JSON.stringify(deviceSessions))}
35				/>
36				<UserCard
37					session={JSON.parse(JSON.stringify(session))}
38					activeSessions={JSON.parse(JSON.stringify(activeSessions))}
39					subscription={subscriptions.find(
40						(sub) => sub.status === "active" || sub.status === "trialing",
41					)}
42				/>
43				<OrganizationCard
44					session={JSON.parse(JSON.stringify(session))}
45					activeOrganization={JSON.parse(JSON.stringify(organization))}
46				/>
47			</div>
48		</div>
49	);
50}
51 
1"use client";
2
3import { useState } from "react";
4import { Button } from "@/components/ui/button";
5import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
6import {
7	Popover,
8	PopoverContent,
9	PopoverTrigger,
10} from "@/components/ui/popover";
11import {
12	Command,
13	CommandGroup,
14	CommandItem,
15	CommandList,
16	CommandSeparator,
17} from "@/components/ui/command";
18import { ChevronDown, PlusCircle } from "lucide-react";
19import { Session } from "@/lib/auth-types";
20import { client, useSession } from "@/lib/auth-client";
21import { useRouter } from "next/navigation";
22
23export default function AccountSwitcher({
24	sessions,
25}: {
26	sessions: Session[];
27}) {
28	const { data: currentUser } = useSession();
29	const [open, setOpen] = useState(false);
30	const router = useRouter();
31	return (
32		<Popover open={open} onOpenChange={setOpen}>
33			<PopoverTrigger asChild>
34				<Button
35					variant="outline"
36					role="combobox"
37					aria-expanded={open}
38					aria-label="Select a user"
39					className="w-[250px] justify-between"
40				>
41					<Avatar className="mr-2 h-6 w-6">
42						<AvatarImage
43							src={currentUser?.user.image || undefined}
44							alt={currentUser?.user.name}
45						/>
46						<AvatarFallback>{currentUser?.user.name.charAt(0)}</AvatarFallback>
47					</Avatar>
48					{currentUser?.user.name}
49					<ChevronDown className="ml-auto h-4 w-4 shrink-0 opacity-50" />
50				</Button>
51			</PopoverTrigger>
52			<PopoverContent className="w-[250px] p-0">
53				<Command>
1"use client";
2
3import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
4import { Button } from "@/components/ui/button";
5import {
6	Card,
7	CardContent,
8	CardFooter,
9	CardHeader,
10	CardTitle,
11} from "@/components/ui/card";
12import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert";
13import { Checkbox } from "@/components/ui/checkbox";
14import {
15	Dialog,
16	DialogContent,
17	DialogDescription,
18	DialogFooter,
19	DialogHeader,
20	DialogTitle,
21	DialogTrigger,
22} from "@/components/ui/dialog";
23import { Input } from "@/components/ui/input";
24import { Label } from "@/components/ui/label";
25import { PasswordInput } from "@/components/ui/password-input";
26import { client, signOut, useSession } from "@/lib/auth-client";
27import { Session } from "@/lib/auth-types";
28import { MobileIcon } from "@radix-ui/react-icons";
29import {
30	Edit,
31	Fingerprint,
32	Laptop,
33	Loader2,
34	LogOut,
35	Plus,
36	QrCode,
37	ShieldCheck,
38	ShieldOff,
39	StopCircle,
40	Trash,
41	X,
42} from "lucide-react";
43import Image from "next/image";
44import { useRouter } from "next/navigation";
45import { useState } from "react";
46import { toast } from "sonner";
47import { UAParser } from "ua-parser-js";
48import {
49	Table,
50	TableBody,
51	TableCell,
52	TableHead,
53	TableHeader,
468		 * @default true
469		 */
470		updateAccountOnSignIn?: boolean;
471		/**
472		 * Configuration for account linking.
473		 */
474		accountLinking?: {
475			/**
476			 * Enable account linking
477			 *
478			 * @default true
479			 */
480			enabled?: boolean;
481			/**
482			 * List of trusted providers
483			 */
484			trustedProviders?: Array<
485				LiteralUnion<SocialProviderList[number] | "email-password", string>
486			>;
487			/**
488			 * If enabled (true), this will allow users to manually linking accounts with different email addresses than the main user.
489			 *
490			 * @default false
491			 *
492			 * ⚠️ Warning: enabling this might lead to account takeovers, so proceed with caution.
493			 */
494			allowDifferentEmails?: boolean;
495			/**
496			 * If enabled (true), this will allow users to unlink all accounts.
497			 *
498			 * @default false
61
62server-side usage:
63
64```ts
65await auth.api.linkSocialAccount({
66  body: {
67    provider: "google", // or any other provider id