diff --git a/packages/better-auth/src/api/routes/callback.ts b/packages/better-auth/src/api/routes/callback.ts index 22a8bd5b43..319c6c25b0 100644 --- a/packages/better-auth/src/api/routes/callback.ts +++ b/packages/better-auth/src/api/routes/callback.ts @@ -6,10 +6,14 @@ import { getAwaitableValue } from "../../context/helpers"; import { setSessionCookie } from "../../cookies"; import { OAUTH_CALLBACK_ERROR_CODES } from "../../oauth2/error-codes"; import { missingEmailLogMessage } from "../../oauth2/errors"; -import { handleOAuthUserInfo } from "../../oauth2/link-account"; +import { + applyUpdateUserInfoOnLink, + handleOAuthUserInfo, +} from "../../oauth2/link-account"; import { generateState, parseState } from "../../oauth2/state"; import { setTokenUtil } from "../../oauth2/utils"; import { HIDE_METADATA } from "../../utils/hide-metadata"; +import { isAPIError } from "../../utils/is-api-error"; const schema = z.object({ code: z.string().optional(), @@ -274,6 +278,7 @@ export const callbackOAuth = createAuthEndpoint( ); } } + await applyUpdateUserInfoOnLink(c, link.userId, userInfo); let toRedirectTo: string; try { const url = callbackURL; @@ -294,20 +299,33 @@ export const callbackOAuth = createAuthEndpoint( ...tokens, scope: tokens.scopes?.join(","), }; - const result = await handleOAuthUserInfo(c, { - userInfo: { - ...userInfo, - id: providerAccountId, - email: userInfo.email, - name: userInfo.name || "", - }, - account: accountData, - callbackURL, - disableSignUp: - (provider.disableImplicitSignUp && !requestSignUp) || - provider.options?.disableSignUp, - overrideUserInfo: provider.options?.overrideUserInfoOnSignIn, - }); + let result: Awaited>; + try { + result = await handleOAuthUserInfo(c, { + userInfo: { + ...userInfo, + id: providerAccountId, + email: userInfo.email, + name: userInfo.name || "", + }, + account: accountData, + callbackURL, + disableSignUp: + (provider.disableImplicitSignUp && !requestSignUp) || + provider.options?.disableSignUp, + overrideUserInfo: provider.options?.overrideUserInfoOnSignIn, + }); + } catch (e) { + // A before-callback hook (for example the admin plugin's banned-user + // guard) can reject sign-in by throwing an APIError. Forward its + // machine-readable code and message to the per-flow errorURL instead + // of surfacing a raw error response. The code is app-defined, so it is + // forwarded verbatim rather than mapped onto OAUTH_CALLBACK_ERROR_CODES. + if (isAPIError(e) && e.body?.code) { + redirectOnError(e.body.code, e.body.message); + } + throw e; + } if (result.error) { c.context.logger.error(result.error.split(" ").join("_")); return redirectOnError(result.error.split(" ").join("_")); diff --git a/packages/better-auth/src/plugins/admin/admin.ts b/packages/better-auth/src/plugins/admin/admin.ts index bbdaa86515..1032ee26f6 100644 --- a/packages/better-auth/src/plugins/admin/admin.ts +++ b/packages/better-auth/src/plugins/admin/admin.ts @@ -113,15 +113,6 @@ export const admin = (options?: O | undefined) => { return; } - if (ctx.path.startsWith("/callback")) { - const redirectURI = - ctx.context.options.onAPIError?.errorURL || - `${ctx.context.baseURL}/error`; - throw ctx.redirect( - `${redirectURI}?error=banned&error_description=${opts.bannedUserMessage}`, - ); - } - throw APIError.from("FORBIDDEN", { message: opts.bannedUserMessage, code: "BANNED_USER",