fix: redirection issue on callback

This commit is contained in:
Bereket Engida
2024-09-08 10:28:22 +03:00
parent 9cc621fc1c
commit ca00d50dad

View File

@@ -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);
},
);