mirror of
https://github.com/better-auth/better-auth.git
synced 2026-05-23 23:52:05 -05:00
fix: redirection issue on callback
This commit is contained in:
@@ -7,6 +7,7 @@ import { createAuthEndpoint } from "../call";
|
||||
import { HIDE_ON_CLIENT_METADATA } from "../../client/client-utils";
|
||||
import { getAccountTokens } from "../../utils/getAccount";
|
||||
import { setSessionCookie } from "../../utils/cookies";
|
||||
import type { OAuth2Tokens } from "arctic";
|
||||
|
||||
export const callbackOAuth = createAuthEndpoint(
|
||||
"/callback/:id",
|
||||
@@ -19,123 +20,126 @@ export const callbackOAuth = createAuthEndpoint(
|
||||
metadata: HIDE_ON_CLIENT_METADATA,
|
||||
},
|
||||
async (c) => {
|
||||
const provider = c.context.options.socialProvider?.find(
|
||||
(p) => p.id === c.params.id,
|
||||
);
|
||||
if (!provider) {
|
||||
c.context.logger.error(
|
||||
"Oauth provider with id",
|
||||
c.params.id,
|
||||
"not found",
|
||||
);
|
||||
throw c.redirect(
|
||||
`${c.context.baseURL}/error?error=oauth_provider_not_found`,
|
||||
);
|
||||
}
|
||||
const codeVerifier = await c.getSignedCookie(
|
||||
c.context.authCookies.pkCodeVerifier.name,
|
||||
c.context.secret,
|
||||
);
|
||||
let tokens: OAuth2Tokens;
|
||||
try {
|
||||
const provider = c.context.options.socialProvider?.find(
|
||||
(p) => p.id === c.params.id,
|
||||
);
|
||||
if (!provider) {
|
||||
c.context.logger.error(
|
||||
"Oauth provider with id",
|
||||
c.params.id,
|
||||
"not found",
|
||||
);
|
||||
throw new APIError("NOT_FOUND");
|
||||
}
|
||||
const codeVerifier = await c.getSignedCookie(
|
||||
c.context.authCookies.pkCodeVerifier.name,
|
||||
c.context.secret,
|
||||
);
|
||||
const tokens = await provider.validateAuthorizationCode(
|
||||
tokens = await provider.validateAuthorizationCode(
|
||||
c.query.code,
|
||||
codeVerifier,
|
||||
`${c.context.baseURL}/callback/${provider.id}`,
|
||||
);
|
||||
if (!tokens) {
|
||||
c.context.logger.error("Code verification failed");
|
||||
throw new APIError("UNAUTHORIZED");
|
||||
}
|
||||
const user = await provider.getUserInfo(tokens).then((res) => res?.user);
|
||||
const id = generateId();
|
||||
const data = userSchema.safeParse({
|
||||
...user,
|
||||
id,
|
||||
});
|
||||
const parsedState = parseState(c.query.state);
|
||||
if (!parsedState.success) {
|
||||
c.context.logger.error("Unable to parse state");
|
||||
throw new APIError("BAD_REQUEST", {
|
||||
message: "invalid state",
|
||||
});
|
||||
}
|
||||
const { callbackURL, currentURL, dontRememberMe } = parsedState.data;
|
||||
if (!user || data.success === false) {
|
||||
if (currentURL) {
|
||||
throw c.redirect(`${currentURL}?error=oauth_validation_failed`);
|
||||
} else {
|
||||
throw new APIError("BAD_REQUEST");
|
||||
}
|
||||
}
|
||||
if (!callbackURL) {
|
||||
c.context.logger.error("Callback URL not found");
|
||||
throw new APIError("FORBIDDEN");
|
||||
}
|
||||
//find user in db
|
||||
const dbUser = await c.context.internalAdapter.findUserByEmail(
|
||||
user.email,
|
||||
);
|
||||
const userId = dbUser?.user.id;
|
||||
if (dbUser) {
|
||||
//check if user has already linked this provider
|
||||
const hasBeenLinked = dbUser.accounts.find(
|
||||
(a) => a.providerId === provider.id,
|
||||
);
|
||||
if (!hasBeenLinked && !user.emailVerified) {
|
||||
c.context.logger.error("User already exists");
|
||||
const url = new URL(currentURL || callbackURL);
|
||||
url.searchParams.set("error", "user_already_exists");
|
||||
throw c.redirect(url.toString());
|
||||
}
|
||||
|
||||
if (!hasBeenLinked && user.emailVerified) {
|
||||
await c.context.internalAdapter.linkAccount({
|
||||
providerId: provider.id,
|
||||
accountId: user.id,
|
||||
id: `${provider.id}:${user.id}`,
|
||||
userId: dbUser.user.id,
|
||||
...getAccountTokens(tokens),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
await c.context.internalAdapter.createOAuthUser(data.data, {
|
||||
...getAccountTokens(tokens),
|
||||
id: `${provider.id}:${user.id}`,
|
||||
providerId: provider.id,
|
||||
accountId: user.id,
|
||||
userId: id,
|
||||
});
|
||||
} catch (e) {
|
||||
const url = new URL(currentURL || callbackURL);
|
||||
url.searchParams.set("error", "unable_to_create_user");
|
||||
c.setHeader("Location", url.toString());
|
||||
throw c.redirect(url.toString());
|
||||
}
|
||||
}
|
||||
//this should never happen
|
||||
if (!userId && !id)
|
||||
throw new APIError("INTERNAL_SERVER_ERROR", {
|
||||
message: "Unable to create user",
|
||||
});
|
||||
//create session
|
||||
const session = await c.context.internalAdapter.createSession(
|
||||
userId || id,
|
||||
c.request,
|
||||
dontRememberMe,
|
||||
);
|
||||
try {
|
||||
await setSessionCookie(c, session.id, dontRememberMe);
|
||||
} catch (e) {
|
||||
c.context.logger.error("Unable to set session cookie", e);
|
||||
const url = new URL(currentURL || callbackURL);
|
||||
url.searchParams.set("error", "unable_to_create_session");
|
||||
throw c.redirect(url.toString());
|
||||
}
|
||||
throw c.redirect(callbackURL);
|
||||
} catch (e) {
|
||||
c.context.logger.error("Error in callback", e);
|
||||
c.context.logger.error("Code verification failed", e);
|
||||
throw c.redirect(
|
||||
`${c.context.baseURL}/error?error=oauth_code_verification_failed`,
|
||||
);
|
||||
}
|
||||
if (!tokens) {
|
||||
c.context.logger.error("Code verification failed");
|
||||
throw c.redirect(
|
||||
`${c.context.baseURL}/error?error=oauth_code_verification_failed`,
|
||||
);
|
||||
}
|
||||
const user = await provider.getUserInfo(tokens).then((res) => res?.user);
|
||||
const id = generateId();
|
||||
const data = userSchema.safeParse({
|
||||
...user,
|
||||
id,
|
||||
});
|
||||
const parsedState = parseState(c.query.state);
|
||||
if (!parsedState.success) {
|
||||
c.context.logger.error("Unable to parse state");
|
||||
throw c.redirect(
|
||||
`${c.context.baseURL}/error?error=invalid_state_parameter`,
|
||||
);
|
||||
}
|
||||
const { callbackURL, currentURL, dontRememberMe } = parsedState.data;
|
||||
if (!user || data.success === false) {
|
||||
throw c.redirect(
|
||||
`${c.context.baseURL}/error?error=oauth_validation_failed`,
|
||||
);
|
||||
}
|
||||
if (!callbackURL) {
|
||||
throw c.redirect(
|
||||
`${c.context.baseURL}/error?error=oauth_callback_url_not_found`,
|
||||
);
|
||||
}
|
||||
//find user in db
|
||||
const dbUser = await c.context.internalAdapter.findUserByEmail(user.email);
|
||||
const userId = dbUser?.user.id;
|
||||
if (dbUser) {
|
||||
//check if user has already linked this provider
|
||||
const hasBeenLinked = dbUser.accounts.find(
|
||||
(a) => a.providerId === provider.id,
|
||||
);
|
||||
if (!hasBeenLinked && !user.emailVerified) {
|
||||
c.context.logger.error("User already exists");
|
||||
const url = new URL(currentURL || callbackURL);
|
||||
url.searchParams.set("error", "user_already_exists");
|
||||
throw c.redirect(url.toString());
|
||||
}
|
||||
|
||||
if (!hasBeenLinked && user.emailVerified) {
|
||||
await c.context.internalAdapter.linkAccount({
|
||||
providerId: provider.id,
|
||||
accountId: user.id,
|
||||
id: `${provider.id}:${user.id}`,
|
||||
userId: dbUser.user.id,
|
||||
...getAccountTokens(tokens),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
await c.context.internalAdapter.createOAuthUser(data.data, {
|
||||
...getAccountTokens(tokens),
|
||||
id: `${provider.id}:${user.id}`,
|
||||
providerId: provider.id,
|
||||
accountId: user.id,
|
||||
userId: id,
|
||||
});
|
||||
} catch (e) {
|
||||
const url = new URL(currentURL || callbackURL);
|
||||
url.searchParams.set("error", "unable_to_create_user");
|
||||
c.setHeader("Location", url.toString());
|
||||
throw c.redirect(url.toString());
|
||||
}
|
||||
}
|
||||
//this should never happen
|
||||
if (!userId && !id)
|
||||
throw new APIError("INTERNAL_SERVER_ERROR", {
|
||||
message: "Unable to create user",
|
||||
});
|
||||
//create session
|
||||
const session = await c.context.internalAdapter.createSession(
|
||||
userId || id,
|
||||
c.request,
|
||||
dontRememberMe,
|
||||
);
|
||||
try {
|
||||
await setSessionCookie(c, session.id, dontRememberMe);
|
||||
} catch (e) {
|
||||
c.context.logger.error("Unable to set session cookie", e);
|
||||
const url = new URL(currentURL || callbackURL);
|
||||
url.searchParams.set("error", "unable_to_create_session");
|
||||
throw c.redirect(url.toString());
|
||||
}
|
||||
|
||||
throw c.redirect(callbackURL);
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user