UNPKG

better-auth

Version:

The most comprehensive authentication framework for TypeScript.

144 lines (142 loc) • 5.93 kB
import { setAccountCookie } from "../cookies/session-store.mjs"; import { setTokenUtil } from "./utils.mjs"; import { createEmailVerificationToken } from "../api/routes/email-verification.mjs"; import { APIError } from "../api/index.mjs"; import { isDevelopment, logger } from "@better-auth/core/env"; //#region src/oauth2/link-account.ts async function handleOAuthUserInfo(c, opts) { const { userInfo, account, callbackURL, disableSignUp, overrideUserInfo } = opts; const dbUser = await c.context.internalAdapter.findOAuthUser(userInfo.email.toLowerCase(), account.accountId, account.providerId).catch((e) => { logger.error("Better auth was unable to query your database.\nError: ", e); const errorURL = c.context.options.onAPIError?.errorURL || `${c.context.baseURL}/error`; throw c.redirect(`${errorURL}?error=internal_server_error`); }); let user = dbUser?.user; let isRegister = !user; if (dbUser) { const hasBeenLinked = dbUser.accounts.find((a) => a.providerId === account.providerId && a.accountId === account.accountId); if (!hasBeenLinked) { const trustedProviders = c.context.options.account?.accountLinking?.trustedProviders; if (!(opts.isTrustedProvider || trustedProviders?.includes(account.providerId)) && !userInfo.emailVerified || c.context.options.account?.accountLinking?.enabled === false) { if (isDevelopment()) logger.warn(`User already exist but account isn't linked to ${account.providerId}. To read more about how account linking works in Better Auth see https://www.better-auth.com/docs/concepts/users-accounts#account-linking.`); return { error: "account not linked", data: null }; } try { await c.context.internalAdapter.linkAccount({ providerId: account.providerId, accountId: userInfo.id.toString(), userId: dbUser.user.id, accessToken: await setTokenUtil(account.accessToken, c.context), refreshToken: await setTokenUtil(account.refreshToken, c.context), idToken: account.idToken, accessTokenExpiresAt: account.accessTokenExpiresAt, refreshTokenExpiresAt: account.refreshTokenExpiresAt, scope: account.scope }); } catch (e) { logger.error("Unable to link account", e); return { error: "unable to link account", data: null }; } if (userInfo.emailVerified && !dbUser.user.emailVerified && userInfo.email.toLowerCase() === dbUser.user.email) await c.context.internalAdapter.updateUser(dbUser.user.id, { emailVerified: true }); } else { if (c.context.options.account?.updateAccountOnSignIn !== false) { const updateData = Object.fromEntries(Object.entries({ idToken: account.idToken, accessToken: await setTokenUtil(account.accessToken, c.context), refreshToken: await setTokenUtil(account.refreshToken, c.context), accessTokenExpiresAt: account.accessTokenExpiresAt, refreshTokenExpiresAt: account.refreshTokenExpiresAt, scope: account.scope }).filter(([_, value]) => value !== void 0)); if (c.context.options.account?.storeAccountCookie) await setAccountCookie(c, { ...account, ...updateData }); if (Object.keys(updateData).length > 0) await c.context.internalAdapter.updateAccount(hasBeenLinked.id, updateData); } if (userInfo.emailVerified && !dbUser.user.emailVerified && userInfo.email.toLowerCase() === dbUser.user.email) await c.context.internalAdapter.updateUser(dbUser.user.id, { emailVerified: true }); } if (overrideUserInfo) { const { id: _, ...restUserInfo } = userInfo; user = await c.context.internalAdapter.updateUser(dbUser.user.id, { ...restUserInfo, email: userInfo.email.toLowerCase(), emailVerified: userInfo.email.toLowerCase() === dbUser.user.email ? dbUser.user.emailVerified || userInfo.emailVerified : userInfo.emailVerified }); } } else { if (disableSignUp) return { error: "signup disabled", data: null, isRegister: false }; try { const { id: _, ...restUserInfo } = userInfo; const accountData = { accessToken: await setTokenUtil(account.accessToken, c.context), refreshToken: await setTokenUtil(account.refreshToken, c.context), idToken: account.idToken, accessTokenExpiresAt: account.accessTokenExpiresAt, refreshTokenExpiresAt: account.refreshTokenExpiresAt, scope: account.scope, providerId: account.providerId, accountId: userInfo.id.toString() }; const { user: createdUser, account: createdAccount } = await c.context.internalAdapter.createOAuthUser({ ...restUserInfo, email: userInfo.email.toLowerCase() }, accountData); user = createdUser; if (c.context.options.account?.storeAccountCookie) await setAccountCookie(c, createdAccount); if (!userInfo.emailVerified && user && c.context.options.emailVerification?.sendOnSignUp && c.context.options.emailVerification?.sendVerificationEmail) { const token = await createEmailVerificationToken(c.context.secret, user.email, void 0, c.context.options.emailVerification?.expiresIn); const url = `${c.context.baseURL}/verify-email?token=${token}&callbackURL=${callbackURL}`; await c.context.runInBackgroundOrAwait(c.context.options.emailVerification.sendVerificationEmail({ user, url, token }, c.request)); } } catch (e) { logger.error(e); if (e instanceof APIError) return { error: e.message, data: null, isRegister: false }; return { error: "unable to create user", data: null, isRegister: false }; } } if (!user) return { error: "unable to create user", data: null, isRegister: false }; const session = await c.context.internalAdapter.createSession(user.id); if (!session) return { error: "unable to create session", data: null, isRegister: false }; return { data: { session, user }, error: null, isRegister }; } //#endregion export { handleOAuthUserInfo }; //# sourceMappingURL=link-account.mjs.map