import { Auth, type AuthConfig, createActionURL, setEnvDefaults, } from "@auth/core"; import CredentialsProvider from "@auth/core/providers/credentials"; import GoogleProvider from "@auth/core/providers/google"; import type { Session } from "@auth/core/types"; // TODO: stop using universal-middleware and directly integrate server middlewares instead and/or use vike-server https://vike.dev/server. (Bati generates boilerplates that use universal-middleware https://github.com/magne4000/universal-middleware to make Bati's internal logic easier. This is temporary and will be removed soon.) import type { Get, UniversalHandler, UniversalMiddleware, } from "@universal-middleware/core"; import { env } from "./env.js"; import { getDb } from "../database/index.js"; const POSTGRES_CONNECTION_STRING = "postgres://neondb_owner:npg_sOVmj8vWq2zG@ep-withered-king-adiz9gpi-pooler.c-2.us-east-1.aws.neon.tech:5432/neondb?sslmode=require&channel_binding=true"; if (!globalThis.crypto) { /** * Polyfill needed if Auth.js code runs on node18 */ Object.defineProperty(globalThis, "crypto", { value: await import("node:crypto").then( (crypto) => crypto.webcrypto as Crypto ), writable: false, configurable: true, }); } const authjsConfig = { basePath: "/api/auth", trustHost: Boolean( env.AUTH_TRUST_HOST ?? env.VERCEL ?? env.NODE_ENV !== "production" ), // TODO: Replace secret {@see https://authjs.dev/reference/core#secret} secret: "buginoo", providers: [ // TODO: Choose and implement providers CredentialsProvider({ name: "Credentials", credentials: { username: { label: "Username", type: "text", placeholder: "jsmith" }, password: { label: "Password", type: "password" }, }, async authorize() { // Add logic here to look up the user from the credentials supplied const user = { id: "019900bb-61b3-7333-b760-b27784dfe33b", name: "J Smith", email: "jsmith@example.com", }; // Any object returned will be saved in `user` property of the JWT // If you return null then an error will be displayed advising the user to check their details. // You can also Reject this callback with an Error thus the user will be sent to the error page with the error message as a query parameter return user ?? null; }, }), GoogleProvider({ clientId: "697711350664-t6237s5n3ttjd1npp1qif1aupptkr0va.apps.googleusercontent.com", clientSecret: "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", }), ], callbacks: { async signIn({ user, account, profile }) { if (typeof user?.email !== "string") return false; const db = await getDb(POSTGRES_CONNECTION_STRING); const userFromDb = await db.users.findByEmailAddress(user.email); if (!userFromDb) { return false; } console.log("signIn", user, account, profile); return true; }, jwt: async ({ token }) => { if (typeof token?.email !== "string") return token; const db = await getDb(POSTGRES_CONNECTION_STRING); let userFromDb = await db.users.findByEmailAddress(token.email || ""); if (!userFromDb) { userFromDb = await db.users.create({ // id: token.id, email: token.email, username: token.email, password: null, createdAt: null, lastLogin: null, }); } return { ...token, id: userFromDb?.id || "", }; }, session: ({ token, session }) => { return { ...session, user: { ...session.user, id: token.id as string, }, }; }, }, } satisfies Omit; /** * Retrieve Auth.js session from Request */ export async function getSession( req: Request, config: Omit ): Promise { setEnvDefaults(process.env, config); const requestURL = new URL(req.url); const url = createActionURL( "session", requestURL.protocol, req.headers, process.env, config ); const response = await Auth( new Request(url, { headers: { cookie: req.headers.get("cookie") ?? "" } }), config ); const { status = 200 } = response; const data = await response.json(); if (!data || !Object.keys(data).length) return null; if (status === 200) return data; throw new Error(data.message); } /** * Add Auth.js session to context * @link {@see https://authjs.dev/getting-started/session-management/get-session} **/ export const authjsSessionMiddleware: Get<[], UniversalMiddleware> = () => async (request, context) => { try { return { ...context, session: await getSession(request, authjsConfig), }; } catch (error) { console.debug("authjsSessionMiddleware:", error); return { ...context, session: null, }; } }; /** * Auth.js route * @link {@see https://authjs.dev/getting-started/installation} **/ export const authjsHandler = (() => async (request) => { return Auth(request, authjsConfig); }) satisfies Get<[], UniversalHandler>;