diff --git a/auth.ts b/auth.ts index 1c9ac1b..13fec4b 100644 --- a/auth.ts +++ b/auth.ts @@ -1,8 +1,32 @@ -import NextAuth from "next-auth" +import NextAuth, { DefaultSession } from "next-auth" +import { JWT } from "@auth/core/jwt" import { PrismaAdapter } from "@auth/prisma-adapter" import { db } from "@/lib/db" import authConfig from "@/auth.config" +import { getUserById } from "./data/user" +import { UserRole } from "@prisma/client" + + +declare module "@auth/core/types" { + /** + * Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context + */ + interface Session { + user: { + role: UserRole + // By default, TypeScript merges new interface properties and overwrite existing ones. In this case, the default session user properties will be overwritten, with the new one defined above. To keep the default session user properties, you need to add them back into the newly declared interface + } & DefaultSession["user"] // To keep the default types + } +} + +declare module "@auth/core/jwt" { + /** Returned by the `jwt` callback and `auth`, when using JWT sessions */ + interface JWT { + role: UserRole + } +} + export const { handlers: { GET, POST }, @@ -23,9 +47,22 @@ export const { if (token.sub && session.user) { session.user.id = token.sub } + + if (token.role && session.user) { + session.user.role = token.role + } + return session }, async jwt({ token, user, account, profile, isNewUser }) { + if (!token.sub) return token + + const existingUser = await getUserById(token.sub) + + if (!existingUser) return token + + token.role = existingUser.role + return token } }, diff --git a/components/auth/role-gate.tsx b/components/auth/role-gate.tsx new file mode 100644 index 0000000..5e73798 --- /dev/null +++ b/components/auth/role-gate.tsx @@ -0,0 +1,30 @@ +"use client" + +import { UserRole } from "@prisma/client"; +import { useSession } from "next-auth/react"; +import React from "react"; + + +interface RoleGateProps { + children: React.ReactNode + roles: UserRole[] +} + +export const RoleGate = ({ + children, + roles, +}: RoleGateProps) => { + const session = useSession() + const role = session?.data?.user.role as UserRole + + if (roles.includes(role)) { + return ( +
+ { children } +
+ ); + } + return
+} + +export default RoleGate \ No newline at end of file diff --git a/components/navigation/adminprofile.tsx b/components/navigation/adminprofile.tsx new file mode 100644 index 0000000..6305120 --- /dev/null +++ b/components/navigation/adminprofile.tsx @@ -0,0 +1,22 @@ +"use client" + +import axios from "axios"; +import * as React from 'react'; +import { useEffect, useState } from "react"; +import { useSession } from "next-auth/react"; +import { cn } from "@/lib/utils"; + +const AdminProfile = () => { + const session = useSession(); + const [user, setUser] = useState<{ id: string, username: string }>() + + + return ( +
+

Role:

+

{session?.data?.user?.role}

+
+ ); +} + +export default AdminProfile; \ No newline at end of file diff --git a/components/navigation/settings.tsx b/components/navigation/settings.tsx index 1cfe3e3..16a0578 100644 --- a/components/navigation/settings.tsx +++ b/components/navigation/settings.tsx @@ -1,6 +1,8 @@ import Link from "next/link"; import { Button } from "../ui/button"; import UserProfile from "./userprofile"; +import AdminProfile from "./adminprofile"; +import RoleGate from "../auth/role-gate"; const SettingsNavigation = async () => { return ( @@ -8,7 +10,12 @@ const SettingsNavigation = async () => {
Hermes
- +
+ + + + +
diff --git a/components/navigation/userprofile.tsx b/components/navigation/userprofile.tsx index 66fa4ce..92a347b 100644 --- a/components/navigation/userprofile.tsx +++ b/components/navigation/userprofile.tsx @@ -20,14 +20,13 @@ const UserProfile = () => { previousUsername = session.user?.name || "" if (session.user) { const fetchData = async () => { + if (user) return + var userData = (await axios.get("/api/account")).data setUser(userData) - console.log(userData) } fetchData().catch(console.error) - - // TODO: check session if impersonation is in use. } }, [session]) diff --git a/lib/current-user.ts b/lib/current-user.ts index bb8e231..c04e604 100644 --- a/lib/current-user.ts +++ b/lib/current-user.ts @@ -1,16 +1,9 @@ import { db } from "@/lib/db" -import { useSession } from "next-auth/react" - -export const currentUser = async() => { - const { data: session, status } = useSession() - - if (status !== "authenticated") { - return null; - } +export const currentUser = async(id: string) => { const user = await db.user.findUnique({ where: { - id: session?.user?.name as string + id } }); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 281516c..d622a25 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -8,11 +8,17 @@ datasource db { relationMode = "prisma" } +enum UserRole { + USER + ADMIN +} + model User { id String @id @default(cuid()) name String? email String? @unique emailVerified DateTime? + role UserRole @default(USER) image String? ttsDefaultVoice Int @default(1) ttsEnabledVoice Int @default(1048575)