UNPKG

@auth/core

Version:

Authentication for the Web.

189 lines (188 loc) 8.09 kB
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");