better-auth
Version:
The most comprehensive authentication framework for TypeScript.
144 lines (142 loc) • 5.93 kB
JavaScript
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