diff --git a/app/api/auth/[...nextauth]/options.ts b/app/api/auth/[...nextauth]/options.ts new file mode 100644 index 0000000..beb5c76 --- /dev/null +++ b/app/api/auth/[...nextauth]/options.ts @@ -0,0 +1,24 @@ +import GitHubProvider from "next-auth/providers/github"; +import { NextAuthOptions } from "next-auth"; + +export const authOptions: NextAuthOptions = { + providers: [ + GitHubProvider({ + clientId: process.env.GITHUB_CLIENT_ID as string, + clientSecret: process.env.GITHUB_CLIENT_SECRET as string, + }), + ], + callbacks: { + async jwt({ token, account }) { + if (account) { + token.accessToken = account.access_token; + } + return token; + }, + async session({ session, token }) { + // Type assertion to let TypeScript know token will have accessToken + session.accessToken = (token as { accessToken?: string }).accessToken; + return session; + }, + }, +}; diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 0000000..9da800b --- /dev/null +++ b/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,6 @@ +import NextAuth from "next-auth"; +import { authOptions } from "./options"; + +const handler = NextAuth(authOptions); + +export { handler as GET, handler as POST }; diff --git a/app/layout.tsx b/app/layout.tsx index fbde2fb..e381c98 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -80,7 +80,6 @@ export default function RootLayout({ children: React.ReactNode; }) { const isProduction = process.env.VERCEL_ENV === "production"; - return ( - {children} + {children} diff --git a/components/auth/auth-menu.tsx b/components/auth/auth-menu.tsx new file mode 100644 index 0000000..81c8ac2 --- /dev/null +++ b/components/auth/auth-menu.tsx @@ -0,0 +1,65 @@ +"use client"; + +import { signIn, signOut, useSession } from "next-auth/react"; +import { Avatar } from "@nextui-org/avatar"; +import { Button } from "@nextui-org/button"; +import { + Dropdown, + DropdownTrigger, + DropdownMenu, + DropdownItem, +} from "@nextui-org/dropdown"; +import { Skeleton } from "@nextui-org/skeleton"; + +import { GithubIcon } from "@/assets/icons"; + +const AuthMenu: React.FC = () => { + const { data: session, status } = useSession(); + + if (status === "loading") { + return ; + } + + return ( +
+ {session ? ( + + + {/* TODO: Support no image placeholder */} + + + + { + +

Signed in as

+

{session.user?.name}

+
+ } + signOut()}> + Log Out + +
+
+ ) : ( + + )} +
+ ); +}; + +export default AuthMenu; diff --git a/components/cta-banner.tsx b/components/cta-banner.tsx index f26d32f..9c89de4 100644 --- a/components/cta-banner.tsx +++ b/components/cta-banner.tsx @@ -66,7 +66,7 @@ const CtaBanner = ({}: ICtaBannerProps) => { className="font-semibold border-large hover:underline" color="danger" variant="bordered" - onClick={handleClose} + onPress={handleClose} > Hide this diff --git a/components/nav/navbar.tsx b/components/nav/navbar.tsx index a4d096d..45aea6f 100644 --- a/components/nav/navbar.tsx +++ b/components/nav/navbar.tsx @@ -5,12 +5,13 @@ import { NavbarBrand, } from "@nextui-org/navbar"; import { Chip } from "@nextui-org/chip"; +import AuthMenu from "@/components/auth/auth-menu"; import { MyImage } from "@/components/ui/image"; +import { getAllProjects } from "@/lib/api/projects"; import { BugReport, CtaButton, FeedbackForms, ProjectDropDown } from "./items"; import Separator from "./separator"; import SocialLinks from "./social-links"; -import { getAllProjects } from "@/lib/api/projects"; export default async function Navbar() { const projects = await getAllProjects().catch((error) => { @@ -51,6 +52,9 @@ export default async function Navbar() { {projects && } +
+ +
); diff --git a/components/table/row.tsx b/components/table/row.tsx index 0df0699..4a788f2 100644 --- a/components/table/row.tsx +++ b/components/table/row.tsx @@ -347,8 +347,7 @@ interface IApplyButtonProps { onOpen: () => void; } export const ApplyButton = ({ onOpen }: IApplyButtonProps) => { - const handleClick = (e: React.MouseEvent) => { - e.stopPropagation(); + const handleClick = () => { onOpen(); }; @@ -356,7 +355,7 @@ export const ApplyButton = ({ onOpen }: IApplyButtonProps) => { diff --git a/middlewares/github.ts b/middlewares/github.ts new file mode 100644 index 0000000..b369f2b --- /dev/null +++ b/middlewares/github.ts @@ -0,0 +1,4 @@ +export { default } from "next-auth/middleware"; + +// applies next-auth only to matching routes +export const config = { matcher: ["/profiles"] }; diff --git a/next.config.js b/next.config.js index e74a631..8b09827 100644 --- a/next.config.js +++ b/next.config.js @@ -16,6 +16,7 @@ const cspHeader = ` const nextConfig = { images: { + remotePatterns: ["avatars.githubusercontent.com"], formats: ["image/avif", "image/webp"], remotePatterns: [ { diff --git a/package.json b/package.json index b1bc082..568d8b8 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ }, "dependencies": { "@next/third-parties": "15.1.3", + "@nextui-org/avatar": "^2.2.5", "@nextui-org/button": "2.2.8", "@nextui-org/card": "^2.2.8", "@nextui-org/checkbox": "^2.3.7", @@ -47,6 +48,7 @@ "isomorphic-dompurify": "^2.19.0", "marked": "^15.0.4", "next": "15.1.3", + "next-auth": "^4.24.10", "next-themes": "^0.4.4", "react": "19.0.0", "react-dom": "19.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index caf5ca3..f587aa9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@next/third-parties': specifier: 15.1.3 version: 15.1.3(next@15.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + '@nextui-org/avatar': + specifier: ^2.2.5 + version: 2.2.5(@nextui-org/system@2.4.5(@nextui-org/theme@2.4.4(tailwindcss@3.4.17))(framer-motion@11.15.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@nextui-org/theme@2.4.4(tailwindcss@3.4.17))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@nextui-org/button': specifier: 2.2.8 version: 2.2.8(@nextui-org/system@2.4.5(@nextui-org/theme@2.4.4(tailwindcss@3.4.17))(framer-motion@11.15.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@nextui-org/theme@2.4.4(tailwindcss@3.4.17))(framer-motion@11.15.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -119,6 +122,9 @@ importers: next: specifier: 15.1.3 version: 15.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + next-auth: + specifier: ^4.24.10 + version: 4.24.11(next@15.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) next-themes: specifier: ^0.4.4 version: 0.4.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -450,6 +456,14 @@ packages: react: '>=18 || >=19.0.0-rc.0' react-dom: '>=18 || >=19.0.0-rc.0' + '@nextui-org/avatar@2.2.5': + resolution: {integrity: sha512-ugpwuH1M+eyWY+6Fb4OgGev1HweRrE61bA3rHxRzR3CcjWoe9g7JoQ04NbNNLUDp+aZuil/RWmgSfGUXEexZVQ==} + peerDependencies: + '@nextui-org/system': '>=2.4.0' + '@nextui-org/theme': '>=2.4.0' + react: '>=18 || >=19.0.0-rc.0' + react-dom: '>=18 || >=19.0.0-rc.0' + '@nextui-org/button@2.2.8': resolution: {integrity: sha512-Km9ER+jpA3DdYmbh8k30w2DMXZIPtZjs7QVVaPAEW+rJYnGWNsomcltlOLIvHpBYeAbB8hfJCbRJ35r9WeL9Gw==} peerDependencies: @@ -789,6 +803,9 @@ packages: resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} + '@panva/hkdf@1.2.1': + resolution: {integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -1410,6 +1427,10 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -2027,6 +2048,9 @@ packages: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true + jose@4.15.9: + resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2095,6 +2119,10 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + marked@15.0.4: resolution: {integrity: sha512-TCHvDqmb3ZJ4PWG7VEGVgtefA5/euFmsIhxtD0XsBxI39gUSKL81mIRFdt0AiNQozUahd4ke98ZdirExd/vSEw==} engines: {node: '>= 18'} @@ -2157,6 +2185,20 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + next-auth@4.24.11: + resolution: {integrity: sha512-pCFXzIDQX7xmHFs4KVH4luCjaCbuPRtZ9oBUjUhOk84mZ9WVPf94n87TxYI4rSRf9HmfHEF8Yep3JrYDVOo3Cw==} + peerDependencies: + '@auth/core': 0.34.2 + next: ^12.2.5 || ^13 || ^14 || ^15 + nodemailer: ^6.6.5 + react: ^17.0.2 || ^18 || ^19 + react-dom: ^17.0.2 || ^18 || ^19 + peerDependenciesMeta: + '@auth/core': + optional: true + nodemailer: + optional: true + next-themes@0.4.4: resolution: {integrity: sha512-LDQ2qIOJF0VnuVrrMSMLrWGjRMkq+0mpgl6e0juCLqdJ+oo8Q84JRWT6Wh11VDQKkMMe+dVzDKLWs5n87T+PkQ==} peerDependencies: @@ -2198,10 +2240,17 @@ packages: nwsapi@2.2.16: resolution: {integrity: sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==} + oauth@0.9.15: + resolution: {integrity: sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + object-hash@2.2.0: + resolution: {integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==} + engines: {node: '>= 6'} + object-hash@3.0.0: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} @@ -2234,6 +2283,13 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} + oidc-token-hash@5.0.3: + resolution: {integrity: sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==} + engines: {node: ^10.13.0 || >=12.0.0} + + openid-client@5.7.1: + resolution: {integrity: sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -2339,6 +2395,14 @@ packages: resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} engines: {node: ^10 || ^12 || >=14} + preact-render-to-string@5.2.6: + resolution: {integrity: sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==} + peerDependencies: + preact: '>=10' + + preact@10.25.4: + resolution: {integrity: sha512-jLdZDb+Q+odkHJ+MpW/9U5cODzqnB+fy2EiHSZES7ldV5LK7yjlVzTp7R8Xy6W6y75kfK8iWYtFVH7lvjwrCMA==} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -2348,6 +2412,9 @@ packages: engines: {node: '>=14'} hasBin: true + pretty-format@3.8.0: + resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} + prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -2741,6 +2808,10 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + w3c-xmlserializer@5.0.0: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} @@ -2813,6 +2884,9 @@ packages: xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yaml@2.6.1: resolution: {integrity: sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==} engines: {node: '>= 14'} @@ -3085,6 +3159,19 @@ snapshots: - '@nextui-org/theme' - framer-motion + '@nextui-org/avatar@2.2.5(@nextui-org/system@2.4.5(@nextui-org/theme@2.4.4(tailwindcss@3.4.17))(framer-motion@11.15.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@nextui-org/theme@2.4.4(tailwindcss@3.4.17))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@nextui-org/react-utils': 2.1.2(react@19.0.0) + '@nextui-org/shared-utils': 2.1.2 + '@nextui-org/system': 2.4.5(@nextui-org/theme@2.4.4(tailwindcss@3.4.17))(framer-motion@11.15.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@nextui-org/theme': 2.4.4(tailwindcss@3.4.17) + '@nextui-org/use-image': 2.1.1(react@19.0.0) + '@react-aria/focus': 3.19.0(react@19.0.0) + '@react-aria/interactions': 3.22.5(react@19.0.0) + '@react-aria/utils': 3.26.0(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + '@nextui-org/button@2.2.8(@nextui-org/system@2.4.5(@nextui-org/theme@2.4.4(tailwindcss@3.4.17))(framer-motion@11.15.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@nextui-org/theme@2.4.4(tailwindcss@3.4.17))(framer-motion@11.15.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@nextui-org/react-utils': 2.1.2(react@19.0.0) @@ -3677,6 +3764,8 @@ snapshots: '@nolyfill/is-core-module@1.0.39': {} + '@panva/hkdf@1.2.1': {} + '@pkgjs/parseargs@0.11.0': optional: true @@ -4512,6 +4601,8 @@ snapshots: concat-map@0.0.1: {} + cookie@0.7.2: {} + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -5278,6 +5369,8 @@ snapshots: jiti@1.21.7: {} + jose@4.15.9: {} + js-tokens@4.0.0: {} js-yaml@4.1.0: @@ -5360,6 +5453,10 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + marked@15.0.4: {} matchmediaquery@0.4.2: @@ -5409,6 +5506,21 @@ snapshots: natural-compare@1.4.0: {} + next-auth@4.24.11(next@15.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + dependencies: + '@babel/runtime': 7.26.0 + '@panva/hkdf': 1.2.1 + cookie: 0.7.2 + jose: 4.15.9 + next: 15.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + oauth: 0.9.15 + openid-client: 5.7.1 + preact: 10.25.4 + preact-render-to-string: 5.2.6(preact@10.25.4) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + uuid: 8.3.2 + next-themes@0.4.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: react: 19.0.0 @@ -5447,8 +5559,12 @@ snapshots: nwsapi@2.2.16: {} + oauth@0.9.15: {} + object-assign@4.1.1: {} + object-hash@2.2.0: {} + object-hash@3.0.0: {} object-inspect@1.13.3: {} @@ -5490,6 +5606,15 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.0.0 + oidc-token-hash@5.0.3: {} + + openid-client@5.7.1: + dependencies: + jose: 4.15.9 + lru-cache: 6.0.0 + object-hash: 2.2.0 + oidc-token-hash: 5.0.3 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -5587,10 +5712,19 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + preact-render-to-string@5.2.6(preact@10.25.4): + dependencies: + preact: 10.25.4 + pretty-format: 3.8.0 + + preact@10.25.4: {} + prelude-ls@1.2.1: {} prettier@3.4.2: {} + pretty-format@3.8.0: {} + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 @@ -6070,6 +6204,8 @@ snapshots: util-deprecate@1.0.2: {} + uuid@8.3.2: {} + w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 @@ -6151,6 +6287,8 @@ snapshots: xmlchars@2.2.0: {} + yallist@4.0.0: {} + yaml@2.6.1: {} yocto-queue@0.1.0: {} diff --git a/tsconfig.json b/tsconfig.json index f67aea9..b2d0201 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "typeRoots": ["./types", "./node_modules/@types"], "target": "es5", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, diff --git a/types/next-auth.d.ts b/types/next-auth.d.ts new file mode 100644 index 0000000..bcc1fb0 --- /dev/null +++ b/types/next-auth.d.ts @@ -0,0 +1,17 @@ +import "next-auth"; + +declare module "next-auth" { + interface Session { + accessToken?: string; // Add custom properties to the session object + user?: { + name?: string; + email?: string; + image?: string; + id?: string; // Example: if you include the user's GitHub ID + }; + } + + interface JWT { + accessToken?: string; + } +}