@auth/core
Version:
Authentication for the Web.
189 lines (188 loc) • 8.09 kB
JavaScript
import { UnknownAction } from "../errors.js";
import { SessionStore } from "./cookie.js";
import { init } from "./init.js";
import renderPage from "./pages/index.js";
import * as routes from "./routes/index.js";
/** @internal */
export async function AuthInternal(request, authOptions) {
const { action, providerId, error, method } = request;
const csrfDisabled = authOptions.skipCSRFCheck === skipCSRFCheck;
const { options, cookies } = await init({
authOptions,
action,
providerId,
url: request.url,
callbackUrl: request.body?.callbackUrl ?? request.query?.callbackUrl,
csrfToken: request.body?.csrfToken,
cookies: request.cookies,
isPost: method === "POST",
csrfDisabled,
});
const sessionStore = new SessionStore(options.cookies.sessionToken, request, options.logger);
if (method === "GET") {
const render = renderPage({ ...options, query: request.query, cookies });
const { pages } = options;
switch (action) {
case "providers":
return (await routes.providers(options.providers));
case "session": {
const session = await routes.session({ sessionStore, options });
if (session.cookies)
cookies.push(...session.cookies);
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
return { ...session, cookies };
}
case "csrf": {
if (csrfDisabled) {
options.logger.warn("csrf-disabled");
cookies.push({
name: options.cookies.csrfToken.name,
value: "",
options: { ...options.cookies.csrfToken.options, maxAge: 0 },
});
return { status: 404, cookies };
}
return {
headers: { "Content-Type": "application/json" },
body: { csrfToken: options.csrfToken },
cookies,
};
}
case "signin":
if (pages.signIn) {
let signinUrl = `${pages.signIn}${pages.signIn.includes("?") ? "&" : "?"}${new URLSearchParams({ callbackUrl: options.callbackUrl })}`;
if (error)
signinUrl = `${signinUrl}&${new URLSearchParams({ error })}`;
return { redirect: signinUrl, cookies };
}
return render.signin();
case "signout":
if (pages.signOut)
return { redirect: pages.signOut, cookies };
return render.signout();
case "callback":
if (options.provider) {
const callback = await routes.callback({
body: request.body,
query: request.query,
headers: request.headers,
cookies: request.cookies,
method,
options,
sessionStore,
});
if (callback.cookies)
cookies.push(...callback.cookies);
return { ...callback, cookies };
}
break;
case "verify-request":
if (pages.verifyRequest) {
return { redirect: pages.verifyRequest, cookies };
}
return render.verifyRequest();
case "error":
// These error messages are displayed in line on the sign in page
// TODO: verify these. We should redirect these to signin directly, instead of
// first to error and then to signin.
if ([
"Signin",
"OAuthCreateAccount",
"EmailCreateAccount",
"Callback",
"OAuthAccountNotLinked",
"SessionRequired",
].includes(error)) {
return { redirect: `${options.url}/signin?error=${error}`, cookies };
}
if (pages.error) {
return {
redirect: `${pages.error}${pages.error.includes("?") ? "&" : "?"}error=${error}`,
cookies,
};
}
return render.error({ error: error });
default:
}
}
else {
switch (action) {
case "signin":
if ((csrfDisabled || options.csrfTokenVerified) && options.provider) {
const signin = await routes.signin(request, options);
if (signin.cookies)
cookies.push(...signin.cookies);
return { ...signin, cookies };
}
return { redirect: `${options.url}/signin?csrf=true`, cookies };
case "signout":
if (csrfDisabled || options.csrfTokenVerified) {
const signout = await routes.signout(sessionStore, options);
if (signout.cookies)
cookies.push(...signout.cookies);
return { ...signout, cookies };
}
return { redirect: `${options.url}/signout?csrf=true`, cookies };
case "callback":
if (options.provider) {
// Verified CSRF Token required for credentials providers only
if (options.provider.type === "credentials" &&
!csrfDisabled &&
!options.csrfTokenVerified) {
return { redirect: `${options.url}/signin?csrf=true`, cookies };
}
const callback = await routes.callback({
body: request.body,
query: request.query,
headers: request.headers,
cookies: request.cookies,
method,
options,
sessionStore,
});
if (callback.cookies)
cookies.push(...callback.cookies);
return { ...callback, cookies };
}
break;
case "session": {
if (options.csrfTokenVerified || csrfDisabled) {
const session = await routes.session({
options,
sessionStore,
newSession: request.body?.data,
isUpdate: true,
});
if (session.cookies)
cookies.push(...session.cookies);
return { ...session, cookies };
}
// If CSRF token is invalid, return a 400 status code
// we should not redirect to a page as this is an API route
return { status: 400, cookies };
}
default:
}
}
throw new UnknownAction(`Cannot handle action: ${action}`);
}
/**
* :::danger
* This option is intended for framework authors.
* :::
*
* Auth.js comes with built-in {@link https://authjs.dev/concepts/security#csrf CSRF} protection, but
* if you are implementing a framework that is already protected against CSRF attacks, you can skip this check by
* passing this value to {@link AuthConfig.skipCSRFCheck}.
*/
export const skipCSRFCheck = Symbol("skip-csrf-check");
/**
* :::danger
* This option is intended for framework authors.
* :::
*
* Auth.js returns a web standard {@link Response} by default, but
* if you are implementing a framework you might want to get access to the raw internal response
* by passing this value to {@link AuthConfig.raw}.
*/
export const raw = Symbol("return-type-raw");