diff --git a/server/authjs-handler.ts b/server/authjs-handler.ts index 47b8561..f6f99e2 100644 --- a/server/authjs-handler.ts +++ b/server/authjs-handler.ts @@ -8,14 +8,13 @@ import { 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, +import { + type Get, + type UniversalHandler, + type UniversalMiddleware, + env as getEnv, } from "@universal-middleware/core"; -import { env } from "./env.js"; import { getDbClient } from "../database/index.js"; -import { JWT } from "@auth/core/jwt"; if (!globalThis.crypto) { /** @@ -30,113 +29,114 @@ if (!globalThis.crypto) { }); } -const authjsConfig = { - basePath: "/api/auth", - // trustHost: Boolean( - // env.AUTH_TRUST_HOST ?? env.VERCEL ?? env.NODE_ENV !== "production" - // ), - trustHost: true, - // TODO: Replace secret {@see https://authjs.dev/reference/core#secret} - secret: env.AUTHJS_SECRET, - 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", - // }; +const authjsConfig = (env: Record) => + ({ + basePath: "/api/auth", + // trustHost: Boolean( + // env.AUTH_TRUST_HOST ?? env.VERCEL ?? env.NODE_ENV !== "production" + // ), + trustHost: true, + // TODO: Replace secret {@see https://authjs.dev/reference/core#secret} + secret: env.AUTHJS_SECRET, + 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: env.GOOGLE_CLIENT_ID, - clientSecret: env.GOOGLE_CLIENT_SECRET, - }), - ], - callbacks: { - async signIn({ user, account, profile }) { - if (typeof user?.email !== "string") return false; - //@ts-ignore - const dbClient = await getDbClient(env.POSTGRES_CONNECTION_STRING); - let userFromDb = await dbClient - .selectFrom("users") - .selectAll() - .where("email", "=", user.email) - .executeTakeFirst(); - if (!userFromDb) { - userFromDb = ( - await dbClient - .insertInto("users") - .values({ - email: user.email, - username: user.email, - password: null, - createdAt: null, - lastLogin: null, - }) - .returningAll() - .execute() - )[0]; - } - console.log("signIn", user, account, profile); - return true; - }, - jwt: async ({ token }) => { - if (typeof token?.email !== "string") return token; - //@ts-ignore - const dbClient = await getDbClient(env.POSTGRES_CONNECTION_STRING); - let userFromDb = await dbClient - .selectFrom("users") - .selectAll() - .where("email", "=", token.email || "") - .executeTakeFirst(); - /** TODO: the following should never happen, because the account in - * created in the `isgnIn` step; but I don't know what error to throw here - * if for some reason there is no such account.*/ - if (!userFromDb) { - userFromDb = ( - await dbClient - .insertInto("users") - .values({ - email: token.email, - username: token.email, - password: null, - createdAt: null, - lastLogin: null, - }) - .returningAll() - .execute() - )[0]; - } - return { - ...token, - id: userFromDb?.id || "", - }; - }, - session: ({ token, session }) => { - return { - ...session, - user: { - ...session.user, - id: token.id as string, - }, - jwt: token, - }; + // // 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: env.GOOGLE_CLIENT_ID, + clientSecret: env.GOOGLE_CLIENT_SECRET, + }), + ], + callbacks: { + async signIn({ user, account, profile }) { + if (typeof user?.email !== "string") return false; + //@ts-ignore + const dbClient = await getDbClient(env.POSTGRES_CONNECTION_STRING); + let userFromDb = await dbClient + .selectFrom("users") + .selectAll() + .where("email", "=", user.email) + .executeTakeFirst(); + if (!userFromDb) { + userFromDb = ( + await dbClient + .insertInto("users") + .values({ + email: user.email, + username: user.email, + password: null, + createdAt: null, + lastLogin: null, + }) + .returningAll() + .execute() + )[0]; + } + console.log("signIn", user, account, profile); + return true; + }, + jwt: async ({ token }) => { + if (typeof token?.email !== "string") return token; + //@ts-ignore + const dbClient = await getDbClient(env.POSTGRES_CONNECTION_STRING); + let userFromDb = await dbClient + .selectFrom("users") + .selectAll() + .where("email", "=", token.email || "") + .executeTakeFirst(); + /** TODO: the following should never happen, because the account in + * created in the `isgnIn` step; but I don't know what error to throw here + * if for some reason there is no such account.*/ + if (!userFromDb) { + userFromDb = ( + await dbClient + .insertInto("users") + .values({ + email: token.email, + username: token.email, + password: null, + createdAt: null, + lastLogin: null, + }) + .returningAll() + .execute() + )[0]; + } + return { + ...token, + id: userFromDb?.id || "", + }; + }, + session: ({ token, session }) => { + return { + ...session, + user: { + ...session.user, + id: token.id as string, + }, + jwt: token, + }; + }, }, - }, -} satisfies Omit; + } satisfies Omit); /** * Retrieve Auth.js session from Request @@ -174,11 +174,15 @@ export async function getSession( * @link {@see https://authjs.dev/getting-started/session-management/get-session} **/ export const authjsSessionMiddleware: Get<[], UniversalMiddleware> = - () => async (request, context) => { + () => async (request, context, runtime) => { + const env = getEnv(runtime); try { return { ...context, - session: await getSession(request, authjsConfig), + session: await getSession( + request, + authjsConfig(env as Record) + ), }; } catch (error) { console.debug("authjsSessionMiddleware:", error); @@ -193,6 +197,7 @@ export const authjsSessionMiddleware: Get<[], UniversalMiddleware> = * Auth.js route * @link {@see https://authjs.dev/getting-started/installation} **/ -export const authjsHandler = (() => async (request) => { - return Auth(request, authjsConfig); +export const authjsHandler = (() => async (request, context, runtime) => { + const env = getEnv(runtime); + return Auth(request, authjsConfig(env as Record)); }) satisfies Get<[], UniversalHandler>; diff --git a/server/trpc-handler.ts b/server/trpc-handler.ts index be90c96..f4e0623 100644 --- a/server/trpc-handler.ts +++ b/server/trpc-handler.ts @@ -27,7 +27,7 @@ export const trpcHandler = ((endpoint) => (request, context, runtime) => { ); const jwt = await getToken({ req, - secret: processEnv.AUTHJS_SECRET, + secret: (env.AUTHJS_SECRET || processEnv.AUTHJS_SECRET) as string, /** Needed to specify cookie name because for some reason in production * it wasn't reading the correct cookie but in development it was. It * was not straightforward to fix the name of the cookie in