diff options
Diffstat (limited to 'apps/web/server')
| -rw-r--r-- | apps/web/server/api/client.ts | 16 | ||||
| -rw-r--r-- | apps/web/server/auth.ts | 96 |
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); |
