aboutsummaryrefslogtreecommitdiffstats
path: root/apps/web/server
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/server')
-rw-r--r--apps/web/server/api/client.ts16
-rw-r--r--apps/web/server/auth.ts96
2 files changed, 112 insertions, 0 deletions
diff --git a/apps/web/server/api/client.ts b/apps/web/server/api/client.ts
new file mode 100644
index 00000000..88ea7a0e
--- /dev/null
+++ b/apps/web/server/api/client.ts
@@ -0,0 +1,16 @@
+import { appRouter } from "@hoarder/trpc/routers/_app";
+import { getServerAuthSession } from "@/server/auth";
+import { Context, createCallerFactory } from "@hoarder/trpc";
+import { db } from "@hoarder/db";
+
+export const createContext = async (database?: typeof db): Promise<Context> => {
+ const session = await getServerAuthSession();
+ return {
+ user: session?.user ?? null,
+ db: database ?? db,
+ };
+};
+
+const createCaller = createCallerFactory(appRouter);
+
+export const api = createCaller(createContext);
diff --git a/apps/web/server/auth.ts b/apps/web/server/auth.ts
new file mode 100644
index 00000000..950443b9
--- /dev/null
+++ b/apps/web/server/auth.ts
@@ -0,0 +1,96 @@
+import NextAuth, { NextAuthOptions, getServerSession } from "next-auth";
+import type { Adapter } from "next-auth/adapters";
+import AuthentikProvider from "next-auth/providers/authentik";
+import serverConfig from "@hoarder/shared/config";
+import { validatePassword } from "@hoarder/trpc/auth";
+import { db } from "@hoarder/db";
+import { DefaultSession } from "next-auth";
+import CredentialsProvider from "next-auth/providers/credentials";
+import { DrizzleAdapter } from "@auth/drizzle-adapter";
+
+import { Provider } from "next-auth/providers/index";
+
+declare module "next-auth/jwt" {
+ export interface JWT {
+ user: {
+ id: string;
+ role: "admin" | "user";
+ } & DefaultSession["user"];
+ }
+}
+
+declare module "next-auth" {
+ /**
+ * Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
+ */
+ export interface Session {
+ user: {
+ id: string;
+ role: "admin" | "user";
+ } & DefaultSession["user"];
+ }
+
+ export interface DefaultUser {
+ role: "admin" | "user" | null;
+ }
+}
+
+const providers: Provider[] = [
+ CredentialsProvider({
+ // The name to display on the sign in form (e.g. "Sign in with...")
+ name: "Credentials",
+ credentials: {
+ email: { label: "Email", type: "email", placeholder: "Email" },
+ password: { label: "Password", type: "password" },
+ },
+ async authorize(credentials) {
+ if (!credentials) {
+ return null;
+ }
+
+ try {
+ return await validatePassword(
+ credentials?.email,
+ credentials?.password,
+ );
+ } catch (e) {
+ return null;
+ }
+ },
+ }),
+];
+
+if (serverConfig.auth.authentik) {
+ providers.push(AuthentikProvider(serverConfig.auth.authentik));
+}
+
+export const authOptions: NextAuthOptions = {
+ // https://github.com/nextauthjs/next-auth/issues/9493
+ adapter: DrizzleAdapter(db) as Adapter,
+ providers: providers,
+ session: {
+ strategy: "jwt",
+ },
+ callbacks: {
+ async jwt({ token, user }) {
+ if (user) {
+ token.user = {
+ id: user.id,
+ name: user.name,
+ email: user.email,
+ image: user.image,
+ role: user.role || "user",
+ };
+ }
+ return token;
+ },
+ async session({ session, token }) {
+ session.user = { ...token.user };
+ return session;
+ },
+ },
+};
+
+export const authHandler = NextAuth(authOptions);
+
+export const getServerAuthSession = () => getServerSession(authOptions);