├── .env.example
├── .gitignore
├── LICENSE.MD
├── README.md
├── app
├── (protected)
│ └── dashboard
│ │ └── page.tsx
├── api
│ └── auth
│ │ └── [...nextauth]
│ │ └── route.ts
├── favicon.ico
├── globals.css
├── layout.tsx
├── lib
│ └── actions.ts
├── page.tsx
├── sign-in
│ └── page.tsx
└── sign-up
│ └── page.tsx
├── auth.config.ts
├── auth.ts
├── components
└── SubmitButton.tsx
├── config.ts
├── lib
├── actions.ts
├── form-schemas.ts
└── prisma.ts
├── middleware.ts
├── next.config.mjs
├── package-lock.json
├── package.json
├── prisma
└── schema.prisma
├── public
├── next.svg
└── vercel.svg
├── remove_mee.png
├── routes.ts
└── tsconfig.json
/.env.example:
--------------------------------------------------------------------------------
1 | DATABASE_URL="mongodb+srv://test:test@cluster0.ns1yp.mongodb.net/myFirstDatabase"
2 | AUTH_SECRET=MeowMeowMeow
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 | .env
31 |
32 | # vercel
33 | .vercel
34 |
35 | # typescript
36 | *.tsbuildinfo
37 | next-env.d.ts
38 |
--------------------------------------------------------------------------------
/LICENSE.MD:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 MARK MATTHEW M. VERGARA
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Next.js MongoDB Prisma Auth Template
2 |
3 | [**`🌐 App Demo`**](https://nextjs-mongodb-prisma-auth-template.vercel.app/)
4 |
5 |
6 |
7 |
8 |
9 | This is a template repository for building a Next.js application with MongoDB, Prisma, and Next Auth **V5**.
10 |
11 | ## Features
12 |
13 | - 🚀 Next Auth **V5** with user registration, login, and logout functionality
14 | - 🚀 Protected Routes
15 | - 🚀 Next.js framework for server-side rendering and client-side rendering
16 | - 🚀 MongoDB for database storage
17 | - 🚀 Prisma for database ORM
18 |
19 | ## Getting Started
20 |
21 | 1. Clone the repository
22 | 2. Install dependencies: `npm install`
23 | 3. Set up your environment variables by creating a `.env` `or` `.env.local` file based on the `.env.example` file.
24 | 4. Generate and DB Push Prisma Client
25 | ```bash
26 | npx prisma generate
27 | npx prisma db push
28 | ```
29 | 5. Start the development server: `npm run dev`
30 |
31 | ## What you need to know
32 |
33 | - `auth.config.ts` `&&` `app/lib/actions.ts` handles auth logic
34 | - `/lib/form-schemas.ts` zod for form validation
35 | - `middleware.ts` handles protected routes
36 |
37 |
38 | ## More Starter Templates
39 |
40 | - [React Supabase Auth Template 🌟](https://github.com/mmvergara/react-supabase-auth-template)
41 | - [React Supabase ShadCN Auth Template](https://github.com/mmvergara/react-supabase-shadcn-auth-template)
42 | - [NextJs MongoDB Prisma Auth Template 🌟](https://github.com/mmvergara/nextjs-mongodb-prisma-auth-template)
43 | - [NextJs Discord Bot Template 🌟](https://github.com/mmvergara/nextjs-discord-bot-boilerplate)
44 | - [React Firebase🔥 Auth Template 🌟](https://github.com/mmvergara/react-firebase-auth-template)
45 | - [Golang Postgres Auth Template](https://github.com/mmvergara/golang-postgresql-auth-template)
46 | - [Vue Supabase Auth Template](https://github.com/mmvergara/vue-supabase-auth-starter-template)
47 | - [Remix Drizzle Auth Template](https://github.com/mmvergara/remix-drizzle-auth-template)
48 |
--------------------------------------------------------------------------------
/app/(protected)/dashboard/page.tsx:
--------------------------------------------------------------------------------
1 | import { auth } from "@/auth";
2 |
3 | import Link from "next/link";
4 |
5 | const DashboardPage = async () => {
6 | const session = await auth();
7 | return (
8 |
9 |
10 | ◄ Home
11 |
12 |
13 | This is a Protected Page
14 | Current User username : {session?.user?.email || "None"}
15 |
16 |
17 | );
18 | };
19 |
20 | export default DashboardPage;
21 |
--------------------------------------------------------------------------------
/app/api/auth/[...nextauth]/route.ts:
--------------------------------------------------------------------------------
1 | import { handlers } from "@/auth"; // Referring to the auth.ts we just created
2 | export const { GET, POST } = handlers;
3 |
--------------------------------------------------------------------------------
/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mmvergara/nextjs-mongodb-prisma-auth-template/4bda47ee44a59929a42c441841da96e8551a59f7/app/favicon.ico
--------------------------------------------------------------------------------
/app/globals.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | box-sizing: border-box;
5 | font-family: "Inter", sans-serif !important;
6 | }
7 |
8 | body {
9 | background-color: hsl(240, 4%, 9%);
10 | color: white;
11 | }
12 |
13 | main {
14 | display: flex;
15 | flex-direction: column;
16 | align-items: center;
17 | margin-top: 10vh;
18 | }
19 |
20 | a {
21 | text-decoration: none;
22 | }
23 |
24 | .header-text {
25 | display: flex;
26 | gap: 0.5em;
27 | align-items: center;
28 | padding: 0.5em 1em;
29 | font-weight: bold;
30 | background: none;
31 | text-align: center;
32 | }
33 |
34 | .main-container {
35 | display: flex;
36 | flex-direction: column;
37 | align-items: center;
38 | background-color: hsl(240, 4%, 12%);
39 | padding: 2em 2em;
40 | width: 100%;
41 | max-width: 500px;
42 | border-radius: 2px;
43 | box-shadow: 0 0 60px rgba(0, 0, 0, 0.2);
44 | border-top: 5px;
45 | border-width: 10px 0px 0px 0px;
46 | border-style: solid;
47 | border-color: #9faab8;
48 | }
49 |
50 | #github-repo-link {
51 | display: flex;
52 | gap: 0.5em;
53 | margin-top: 0;
54 | align-items: center;
55 | padding: 0.5em 1em;
56 | font-weight: bold;
57 | background: none;
58 | cursor: pointer;
59 | color: #9faab8;
60 | }
61 |
62 | #github-repo-link:hover {
63 | background-color: rgba(159, 170, 184, 0.1);
64 | }
65 |
66 | input {
67 | padding: 1rem;
68 | border-radius: 0.125rem;
69 | outline: none;
70 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
71 | background-color: hsl(240, 3%, 9%);
72 | border: 0;
73 | font-size: 1rem;
74 | color: white;
75 | margin-top: 7px;
76 | width: 300px;
77 | }
78 |
79 | button,
80 | a {
81 | font-size: 16px;
82 | padding: 1em;
83 | background: none;
84 | cursor: pointer;
85 | color: white;
86 | background: #9faab8;
87 | border: none;
88 | border-radius: 0.125em;
89 | transition: background-color 0.3s;
90 | width: 300px;
91 | text-align: center;
92 | margin-top: 1em;
93 | display: flex;
94 | justify-content: center;
95 | align-items: center;
96 | height: 40px;
97 | font-weight: bold;
98 | }
99 |
100 | button:hover,
101 | a:hover {
102 | background: #8c99a9;
103 | }
104 |
105 | .home-link {
106 | margin-bottom: 1em;
107 | }
108 |
109 | .auth-link {
110 | background: transparent;
111 | color: #9faab8;
112 | }
113 |
114 | .auth-link:hover {
115 | background: transparent;
116 | text-decoration: underline;
117 | }
118 |
119 | #divider {
120 | width: 70%;
121 | height: 2px;
122 | background-color: #9faab8;
123 | margin: 1em 0;
124 | border-radius: 100%;
125 | }
126 |
--------------------------------------------------------------------------------
/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import { Inter } from "next/font/google";
3 | import "./globals.css";
4 |
5 | const inter = Inter({ subsets: ["latin"] });
6 |
7 | export const metadata: Metadata = {
8 | title: "NextJS MongoDB Prisma Starter",
9 | description: "NextJS MongoDB Prisma Starter with TypeScript and TailwindCSS",
10 | };
11 |
12 | export default function RootLayout({
13 | children,
14 | }: Readonly<{
15 | children: React.ReactNode;
16 | }>) {
17 | return (
18 |
19 | {children}
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/app/lib/actions.ts:
--------------------------------------------------------------------------------
1 | import { signIn } from "@/auth";
2 | import { AuthError } from "next-auth";
3 |
4 | // ...
5 |
6 | export async function authenticate(
7 | prevState: string | undefined,
8 | formData: FormData
9 | ) {
10 | try {
11 | await signIn("credentials", formData);
12 | } catch (error) {
13 | if (error instanceof AuthError) {
14 | switch (error.type) {
15 | case "CredentialsSignin":
16 | return "Invalid credentials.";
17 | default:
18 | return "Something went wrong.";
19 | }
20 | }
21 | throw error;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/page.tsx:
--------------------------------------------------------------------------------
1 | import { auth, signOut } from "@/auth";
2 | import Link from "next/link";
3 | import {
4 | isRedirectError,
5 | redirect,
6 | } from "next/dist/client/components/redirect";
7 | import { SubmitButton } from "@/components/SubmitButton";
8 |
9 | export default async function Home() {
10 | const session = await auth();
11 | return (
12 |
13 |
14 | NextJS MongoDB Prisma Auth
15 | Current User : {session?.user?.email || "None"}
16 | {session?.user ? (
17 |
39 | ) : (
40 | Sign In
41 | )}
42 | Protected Page 🛡️
43 |
44 |
50 |
58 |
62 |
63 | Github Repository
64 |
65 |
66 |
67 | );
68 | }
69 |
--------------------------------------------------------------------------------
/app/sign-in/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { SubmitButton } from "@/components/SubmitButton";
3 | import { signInAction } from "@/lib/actions";
4 | import Link from "next/link";
5 | import { redirect } from "next/navigation";
6 |
7 | export default function SignInPage() {
8 | const handleFormSubmit = async (formData: FormData) => {
9 | const email = formData.get("email") as string;
10 | const password = formData.get("password") as string;
11 | const res = await signInAction({ email, password });
12 | if (res.error) {
13 | alert(res.error);
14 | return;
15 | }
16 | redirect("/");
17 | };
18 |
19 | return (
20 |
21 |
22 | ◄ Home
23 |
24 |
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/app/sign-up/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { signUpSchema } from "@/lib/form-schemas";
3 | import { SubmitButton } from "@/components/SubmitButton";
4 | import { signUpAction } from "@/lib/actions";
5 | import Link from "next/link";
6 |
7 | export default function SignUpPage() {
8 | const handleFormSubmit = async (formData: FormData) => {
9 | const formValues = {
10 | username: formData.get("username") as string,
11 | email: formData.get("email") as string,
12 | password: formData.get("password") as string,
13 | };
14 |
15 | const { error } = await signUpSchema.safeParseAsync(formValues);
16 | if (error) {
17 | alert(error.issues[0].message);
18 | }
19 |
20 | const res = await signUpAction(formValues);
21 | if (res?.error) {
22 | alert(res.error);
23 | }
24 | };
25 |
26 | return (
27 |
28 |
29 | ◄ Home
30 |
31 |
52 |
53 | );
54 | }
55 |
--------------------------------------------------------------------------------
/auth.config.ts:
--------------------------------------------------------------------------------
1 | import type { NextAuthConfig } from "next-auth";
2 | import { signInSchema } from "./lib/form-schemas";
3 | import { compare } from "bcryptjs";
4 | import Credentials from "next-auth/providers/credentials";
5 | import prisma from "./lib/prisma";
6 |
7 | export const authConfig = {
8 | providers: [
9 | Credentials({
10 | async authorize(credentials) {
11 | // Validate the fields
12 | const validatedFields = signInSchema.safeParse(credentials);
13 | if (!validatedFields.success) {
14 | return null;
15 | }
16 |
17 | // Validate that the user exists
18 | const { email, password } = validatedFields.data;
19 | const user = await prisma.user.findUnique({
20 | where: { email },
21 | });
22 | if (!user) {
23 | return null;
24 | }
25 |
26 | // Check the password
27 | const isPasswordMatch = await compare(password, user.password);
28 | if (!isPasswordMatch) {
29 | return null;
30 | }
31 |
32 | return user;
33 | },
34 | }),
35 | ],
36 | } satisfies NextAuthConfig;
37 |
--------------------------------------------------------------------------------
/auth.ts:
--------------------------------------------------------------------------------
1 | import { PrismaAdapter } from "@auth/prisma-adapter";
2 | import prisma from "./lib/prisma";
3 | import { Adapter } from "next-auth/adapters";
4 | import NextAuth from "next-auth";
5 | import { authConfig } from "./auth.config";
6 |
7 | export const { handlers, auth, signIn, signOut } = NextAuth({
8 | adapter: PrismaAdapter(prisma) as Adapter,
9 | session: { strategy: "jwt" },
10 | ...authConfig,
11 | });
12 |
--------------------------------------------------------------------------------
/components/SubmitButton.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useFormStatus } from "react-dom";
4 | import { type ComponentProps } from "react";
5 |
6 | type Props = ComponentProps<"button"> & {
7 | pendingText: string;
8 | };
9 |
10 | export function SubmitButton({ children, pendingText, ...props }: Props) {
11 | const { pending } = useFormStatus();
12 |
13 | return (
14 |
15 | {pending ? pendingText : children}
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/config.ts:
--------------------------------------------------------------------------------
1 | if (!process.env.DATABASE_URL) throw new Error("DATABASE_URL must be set");
2 | export const DATABASE_URL = process.env.DATABASE_URL;
3 | console.log("DATABASE_URL", DATABASE_URL);
4 |
--------------------------------------------------------------------------------
/lib/actions.ts:
--------------------------------------------------------------------------------
1 | "use server";
2 | import { SignInValues, SignUpValues, signUpSchema } from "./form-schemas";
3 | import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library";
4 | import { AuthError } from "next-auth";
5 | import { hashSync } from "bcryptjs";
6 | import { signIn } from "@/auth";
7 | import prisma from "./prisma";
8 | import { redirect } from "next/navigation";
9 |
10 | export const signInAction = async (signInValues: SignInValues) => {
11 | try {
12 | await signIn("credentials", signInValues);
13 | } catch (error) {
14 | if (error instanceof AuthError) {
15 | switch (error.type) {
16 | case "CredentialsSignin":
17 | return { error: "Invalid Credentials" };
18 | default:
19 | return { error: "An error occurred" };
20 | }
21 | }
22 | throw error;
23 | }
24 | redirect("/dashboard");
25 | };
26 |
27 | export const signUpAction = async (signUpValues: SignUpValues) => {
28 | const { data } = await signUpSchema.safeParseAsync(signUpValues);
29 | if (!data) return { error: "Invalid data" };
30 | try {
31 | await prisma.user.create({
32 | data: {
33 | ...data,
34 | password: hashSync(data.password, 10),
35 | },
36 | });
37 | } catch (error) {
38 | console.log("ERROR OCCURED SIGNUP ACTION", error);
39 | if (error instanceof PrismaClientKnownRequestError) {
40 | console.log(error.code);
41 | switch (error.code) {
42 | case "P2002":
43 | return { error: "Email already exists" };
44 | default:
45 | return { error: "An error occurred" };
46 | }
47 | }
48 | return { error: "An error occurred" };
49 | }
50 | redirect("/sign-in");
51 | };
52 |
--------------------------------------------------------------------------------
/lib/form-schemas.ts:
--------------------------------------------------------------------------------
1 | import * as z from "zod";
2 | const { object, string } = z;
3 |
4 | export const signInSchema = object({
5 | email: string({ required_error: "Email is required" }).email("Invalid email"),
6 | password: string({ required_error: "Password is required" })
7 | .min(8, "Password must be more than 8 characters")
8 | .max(32, "Password must be less than 32 characters"),
9 | });
10 | export type SignInValues = z.infer;
11 |
12 | export const signUpSchema = object({
13 | email: string({ required_error: "Email is required" }).email("Invalid email"),
14 | password: string({ required_error: "Password is required" })
15 | .min(8, "Password must be more than 8 characters")
16 | .max(32, "Password must be less than 32 characters"),
17 | username: string({ required_error: "Username is required" })
18 | .min(4, "Username is required")
19 | .max(32, "Username must be less than 32 characters"),
20 | });
21 | export type SignUpValues = z.infer;
22 |
--------------------------------------------------------------------------------
/lib/prisma.ts:
--------------------------------------------------------------------------------
1 | import { PrismaClient } from "@prisma/client";
2 |
3 | const prismaClientSingleton = () => {
4 | return new PrismaClient();
5 | };
6 |
7 | type PrismaClientSingleton = ReturnType;
8 |
9 | // eslint-disable-next-line
10 | const globalForPrisma = globalThis as unknown as {
11 | prisma: PrismaClientSingleton | undefined;
12 | };
13 |
14 | const prisma = globalForPrisma.prisma ?? prismaClientSingleton();
15 |
16 | export default prisma;
17 |
--------------------------------------------------------------------------------
/middleware.ts:
--------------------------------------------------------------------------------
1 | import { NextResponse } from "next/server";
2 | import { auth } from "./auth";
3 | import { apiAuthPrefix, authRoutes, publicRoutes } from "./routes";
4 |
5 | export default auth((req) => {
6 | const { nextUrl } = req;
7 | const isLoggedIn = !!req.auth;
8 |
9 | const isApiAuthRoute = nextUrl.pathname.startsWith(apiAuthPrefix);
10 | const isPublicRotue = publicRoutes.includes(nextUrl.pathname);
11 | const isAuthRoute = authRoutes.includes(nextUrl.pathname);
12 |
13 | // if it is an API Next Auth route, we don't want to redirect
14 | if (isApiAuthRoute) return;
15 |
16 | if (isAuthRoute) {
17 | if (isLoggedIn) {
18 | // if the user is already logged in and is in sign-in or sign-up page
19 | // redirect to the default logged in page (which is dashboard in this case)
20 | console.log("redirecting to dashboard");
21 | return NextResponse.redirect(new URL("/dashboard", nextUrl));
22 | }
23 | // if the user is not logged in and is in sign-in or sign-up page, let them be
24 | return;
25 | }
26 |
27 | if (!isLoggedIn && !isPublicRotue) {
28 | // if the user is not logged in and is not in a public route, redirect to sign-in page
29 | return NextResponse.redirect(new URL("/sign-in", nextUrl));
30 | }
31 |
32 | return;
33 | });
34 |
35 | export const config = {
36 | matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"],
37 | };
38 |
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {};
3 |
4 | export default nextConfig;
5 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nextjs-mongodb-prisma-auth-template",
3 | "version": "0.1.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "nextjs-mongodb-prisma-auth-template",
9 | "version": "0.1.0",
10 | "dependencies": {
11 | "@auth/prisma-adapter": "^2.0.0",
12 | "@prisma/client": "^5.13.0",
13 | "bcryptjs": "^2.4.3",
14 | "next": "^14.2.18",
15 | "next-auth": "^5.0.0-beta.17",
16 | "react": "^18",
17 | "react-dom": "^18",
18 | "zod": "^3.23.5"
19 | },
20 | "devDependencies": {
21 | "@types/bcryptjs": "^2.4.6",
22 | "@types/node": "^20",
23 | "@types/react": "^18",
24 | "@types/react-dom": "^18",
25 | "prisma": "^5.13.0",
26 | "typescript": "^5"
27 | }
28 | },
29 | "node_modules/@auth/core": {
30 | "version": "0.37.4",
31 | "resolved": "https://registry.npmjs.org/@auth/core/-/core-0.37.4.tgz",
32 | "integrity": "sha512-HOXJwXWXQRhbBDHlMU0K/6FT1v+wjtzdKhsNg0ZN7/gne6XPsIrjZ4daMcFnbq0Z/vsAbYBinQhhua0d77v7qw==",
33 | "license": "ISC",
34 | "dependencies": {
35 | "@panva/hkdf": "^1.2.1",
36 | "jose": "^5.9.6",
37 | "oauth4webapi": "^3.1.1",
38 | "preact": "10.24.3",
39 | "preact-render-to-string": "6.5.11"
40 | },
41 | "peerDependencies": {
42 | "@simplewebauthn/browser": "^9.0.1",
43 | "@simplewebauthn/server": "^9.0.2",
44 | "nodemailer": "^6.8.0"
45 | },
46 | "peerDependenciesMeta": {
47 | "@simplewebauthn/browser": {
48 | "optional": true
49 | },
50 | "@simplewebauthn/server": {
51 | "optional": true
52 | },
53 | "nodemailer": {
54 | "optional": true
55 | }
56 | }
57 | },
58 | "node_modules/@auth/prisma-adapter": {
59 | "version": "2.7.4",
60 | "resolved": "https://registry.npmjs.org/@auth/prisma-adapter/-/prisma-adapter-2.7.4.tgz",
61 | "integrity": "sha512-3T/X94R9J1sxOLQtsD3ijIZ0JGHPXlZQxRr/8NpnZBJ3KGxun/mNsZ1MwMRhTxy0mmn9JWXk7u9+xCcVn0pu3A==",
62 | "license": "ISC",
63 | "dependencies": {
64 | "@auth/core": "0.37.4"
65 | },
66 | "peerDependencies": {
67 | "@prisma/client": ">=2.26.0 || >=3 || >=4 || >=5"
68 | }
69 | },
70 | "node_modules/@next/env": {
71 | "version": "14.2.18",
72 | "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.18.tgz",
73 | "integrity": "sha512-2vWLOUwIPgoqMJKG6dt35fVXVhgM09tw4tK3/Q34GFXDrfiHlG7iS33VA4ggnjWxjiz9KV5xzfsQzJX6vGAekA==",
74 | "license": "MIT"
75 | },
76 | "node_modules/@next/swc-darwin-arm64": {
77 | "version": "14.2.18",
78 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.18.tgz",
79 | "integrity": "sha512-tOBlDHCjGdyLf0ube/rDUs6VtwNOajaWV+5FV/ajPgrvHeisllEdymY/oDgv2cx561+gJksfMUtqf8crug7sbA==",
80 | "cpu": [
81 | "arm64"
82 | ],
83 | "license": "MIT",
84 | "optional": true,
85 | "os": [
86 | "darwin"
87 | ],
88 | "engines": {
89 | "node": ">= 10"
90 | }
91 | },
92 | "node_modules/@next/swc-darwin-x64": {
93 | "version": "14.2.18",
94 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.18.tgz",
95 | "integrity": "sha512-uJCEjutt5VeJ30jjrHV1VIHCsbMYnEqytQgvREx+DjURd/fmKy15NaVK4aR/u98S1LGTnjq35lRTnRyygglxoA==",
96 | "cpu": [
97 | "x64"
98 | ],
99 | "license": "MIT",
100 | "optional": true,
101 | "os": [
102 | "darwin"
103 | ],
104 | "engines": {
105 | "node": ">= 10"
106 | }
107 | },
108 | "node_modules/@next/swc-linux-arm64-gnu": {
109 | "version": "14.2.18",
110 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.18.tgz",
111 | "integrity": "sha512-IL6rU8vnBB+BAm6YSWZewc+qvdL1EaA+VhLQ6tlUc0xp+kkdxQrVqAnh8Zek1ccKHlTDFRyAft0e60gteYmQ4A==",
112 | "cpu": [
113 | "arm64"
114 | ],
115 | "license": "MIT",
116 | "optional": true,
117 | "os": [
118 | "linux"
119 | ],
120 | "engines": {
121 | "node": ">= 10"
122 | }
123 | },
124 | "node_modules/@next/swc-linux-arm64-musl": {
125 | "version": "14.2.18",
126 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.18.tgz",
127 | "integrity": "sha512-RCaENbIZqKKqTlL8KNd+AZV/yAdCsovblOpYFp0OJ7ZxgLNbV5w23CUU1G5On+0fgafrsGcW+GdMKdFjaRwyYA==",
128 | "cpu": [
129 | "arm64"
130 | ],
131 | "license": "MIT",
132 | "optional": true,
133 | "os": [
134 | "linux"
135 | ],
136 | "engines": {
137 | "node": ">= 10"
138 | }
139 | },
140 | "node_modules/@next/swc-linux-x64-gnu": {
141 | "version": "14.2.18",
142 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.18.tgz",
143 | "integrity": "sha512-3kmv8DlyhPRCEBM1Vavn8NjyXtMeQ49ID0Olr/Sut7pgzaQTo4h01S7Z8YNE0VtbowyuAL26ibcz0ka6xCTH5g==",
144 | "cpu": [
145 | "x64"
146 | ],
147 | "license": "MIT",
148 | "optional": true,
149 | "os": [
150 | "linux"
151 | ],
152 | "engines": {
153 | "node": ">= 10"
154 | }
155 | },
156 | "node_modules/@next/swc-linux-x64-musl": {
157 | "version": "14.2.18",
158 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.18.tgz",
159 | "integrity": "sha512-mliTfa8seVSpTbVEcKEXGjC18+TDII8ykW4a36au97spm9XMPqQTpdGPNBJ9RySSFw9/hLuaCMByluQIAnkzlw==",
160 | "cpu": [
161 | "x64"
162 | ],
163 | "license": "MIT",
164 | "optional": true,
165 | "os": [
166 | "linux"
167 | ],
168 | "engines": {
169 | "node": ">= 10"
170 | }
171 | },
172 | "node_modules/@next/swc-win32-arm64-msvc": {
173 | "version": "14.2.18",
174 | "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.18.tgz",
175 | "integrity": "sha512-J5g0UFPbAjKYmqS3Cy7l2fetFmWMY9Oao32eUsBPYohts26BdrMUyfCJnZFQkX9npYaHNDOWqZ6uV9hSDPw9NA==",
176 | "cpu": [
177 | "arm64"
178 | ],
179 | "license": "MIT",
180 | "optional": true,
181 | "os": [
182 | "win32"
183 | ],
184 | "engines": {
185 | "node": ">= 10"
186 | }
187 | },
188 | "node_modules/@next/swc-win32-ia32-msvc": {
189 | "version": "14.2.18",
190 | "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.18.tgz",
191 | "integrity": "sha512-Ynxuk4ZgIpdcN7d16ivJdjsDG1+3hTvK24Pp8DiDmIa2+A4CfhJSEHHVndCHok6rnLUzAZD+/UOKESQgTsAZGg==",
192 | "cpu": [
193 | "ia32"
194 | ],
195 | "license": "MIT",
196 | "optional": true,
197 | "os": [
198 | "win32"
199 | ],
200 | "engines": {
201 | "node": ">= 10"
202 | }
203 | },
204 | "node_modules/@next/swc-win32-x64-msvc": {
205 | "version": "14.2.18",
206 | "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.18.tgz",
207 | "integrity": "sha512-dtRGMhiU9TN5nyhwzce+7c/4CCeykYS+ipY/4mIrGzJ71+7zNo55ZxCB7cAVuNqdwtYniFNR2c9OFQ6UdFIMcg==",
208 | "cpu": [
209 | "x64"
210 | ],
211 | "license": "MIT",
212 | "optional": true,
213 | "os": [
214 | "win32"
215 | ],
216 | "engines": {
217 | "node": ">= 10"
218 | }
219 | },
220 | "node_modules/@panva/hkdf": {
221 | "version": "1.2.1",
222 | "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz",
223 | "integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==",
224 | "license": "MIT",
225 | "funding": {
226 | "url": "https://github.com/sponsors/panva"
227 | }
228 | },
229 | "node_modules/@prisma/client": {
230 | "version": "5.22.0",
231 | "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.22.0.tgz",
232 | "integrity": "sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==",
233 | "hasInstallScript": true,
234 | "license": "Apache-2.0",
235 | "engines": {
236 | "node": ">=16.13"
237 | },
238 | "peerDependencies": {
239 | "prisma": "*"
240 | },
241 | "peerDependenciesMeta": {
242 | "prisma": {
243 | "optional": true
244 | }
245 | }
246 | },
247 | "node_modules/@prisma/debug": {
248 | "version": "5.22.0",
249 | "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.22.0.tgz",
250 | "integrity": "sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==",
251 | "devOptional": true,
252 | "license": "Apache-2.0"
253 | },
254 | "node_modules/@prisma/engines": {
255 | "version": "5.22.0",
256 | "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.22.0.tgz",
257 | "integrity": "sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==",
258 | "devOptional": true,
259 | "hasInstallScript": true,
260 | "license": "Apache-2.0",
261 | "dependencies": {
262 | "@prisma/debug": "5.22.0",
263 | "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2",
264 | "@prisma/fetch-engine": "5.22.0",
265 | "@prisma/get-platform": "5.22.0"
266 | }
267 | },
268 | "node_modules/@prisma/engines-version": {
269 | "version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2",
270 | "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2.tgz",
271 | "integrity": "sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==",
272 | "devOptional": true,
273 | "license": "Apache-2.0"
274 | },
275 | "node_modules/@prisma/fetch-engine": {
276 | "version": "5.22.0",
277 | "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.22.0.tgz",
278 | "integrity": "sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==",
279 | "devOptional": true,
280 | "license": "Apache-2.0",
281 | "dependencies": {
282 | "@prisma/debug": "5.22.0",
283 | "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2",
284 | "@prisma/get-platform": "5.22.0"
285 | }
286 | },
287 | "node_modules/@prisma/get-platform": {
288 | "version": "5.22.0",
289 | "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.22.0.tgz",
290 | "integrity": "sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==",
291 | "devOptional": true,
292 | "license": "Apache-2.0",
293 | "dependencies": {
294 | "@prisma/debug": "5.22.0"
295 | }
296 | },
297 | "node_modules/@swc/counter": {
298 | "version": "0.1.3",
299 | "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
300 | "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
301 | "license": "Apache-2.0"
302 | },
303 | "node_modules/@swc/helpers": {
304 | "version": "0.5.5",
305 | "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz",
306 | "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==",
307 | "license": "Apache-2.0",
308 | "dependencies": {
309 | "@swc/counter": "^0.1.3",
310 | "tslib": "^2.4.0"
311 | }
312 | },
313 | "node_modules/@types/bcryptjs": {
314 | "version": "2.4.6",
315 | "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz",
316 | "integrity": "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==",
317 | "dev": true,
318 | "license": "MIT"
319 | },
320 | "node_modules/@types/cookie": {
321 | "version": "0.6.0",
322 | "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
323 | "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==",
324 | "license": "MIT"
325 | },
326 | "node_modules/@types/node": {
327 | "version": "20.17.9",
328 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.9.tgz",
329 | "integrity": "sha512-0JOXkRyLanfGPE2QRCwgxhzlBAvaRdCNMcvbd7jFfpmD4eEXll7LRwy5ymJmyeZqk7Nh7eD2LeUyQ68BbndmXw==",
330 | "dev": true,
331 | "license": "MIT",
332 | "dependencies": {
333 | "undici-types": "~6.19.2"
334 | }
335 | },
336 | "node_modules/@types/prop-types": {
337 | "version": "15.7.13",
338 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz",
339 | "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==",
340 | "dev": true,
341 | "license": "MIT"
342 | },
343 | "node_modules/@types/react": {
344 | "version": "18.3.12",
345 | "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz",
346 | "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==",
347 | "dev": true,
348 | "license": "MIT",
349 | "dependencies": {
350 | "@types/prop-types": "*",
351 | "csstype": "^3.0.2"
352 | }
353 | },
354 | "node_modules/@types/react-dom": {
355 | "version": "18.3.1",
356 | "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz",
357 | "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==",
358 | "dev": true,
359 | "license": "MIT",
360 | "dependencies": {
361 | "@types/react": "*"
362 | }
363 | },
364 | "node_modules/bcryptjs": {
365 | "version": "2.4.3",
366 | "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
367 | "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==",
368 | "license": "MIT"
369 | },
370 | "node_modules/busboy": {
371 | "version": "1.6.0",
372 | "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
373 | "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
374 | "dependencies": {
375 | "streamsearch": "^1.1.0"
376 | },
377 | "engines": {
378 | "node": ">=10.16.0"
379 | }
380 | },
381 | "node_modules/caniuse-lite": {
382 | "version": "1.0.30001685",
383 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001685.tgz",
384 | "integrity": "sha512-e/kJN1EMyHQzgcMEEgoo+YTCO1NGCmIYHk5Qk8jT6AazWemS5QFKJ5ShCJlH3GZrNIdZofcNCEwZqbMjjKzmnA==",
385 | "funding": [
386 | {
387 | "type": "opencollective",
388 | "url": "https://opencollective.com/browserslist"
389 | },
390 | {
391 | "type": "tidelift",
392 | "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
393 | },
394 | {
395 | "type": "github",
396 | "url": "https://github.com/sponsors/ai"
397 | }
398 | ],
399 | "license": "CC-BY-4.0"
400 | },
401 | "node_modules/client-only": {
402 | "version": "0.0.1",
403 | "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
404 | "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
405 | "license": "MIT"
406 | },
407 | "node_modules/cookie": {
408 | "version": "0.7.1",
409 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
410 | "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
411 | "license": "MIT",
412 | "engines": {
413 | "node": ">= 0.6"
414 | }
415 | },
416 | "node_modules/csstype": {
417 | "version": "3.1.3",
418 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
419 | "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
420 | "dev": true,
421 | "license": "MIT"
422 | },
423 | "node_modules/fsevents": {
424 | "version": "2.3.3",
425 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
426 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
427 | "dev": true,
428 | "hasInstallScript": true,
429 | "license": "MIT",
430 | "optional": true,
431 | "os": [
432 | "darwin"
433 | ],
434 | "engines": {
435 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
436 | }
437 | },
438 | "node_modules/graceful-fs": {
439 | "version": "4.2.11",
440 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
441 | "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
442 | "license": "ISC"
443 | },
444 | "node_modules/jose": {
445 | "version": "5.9.6",
446 | "resolved": "https://registry.npmjs.org/jose/-/jose-5.9.6.tgz",
447 | "integrity": "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==",
448 | "license": "MIT",
449 | "funding": {
450 | "url": "https://github.com/sponsors/panva"
451 | }
452 | },
453 | "node_modules/js-tokens": {
454 | "version": "4.0.0",
455 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
456 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
457 | "license": "MIT"
458 | },
459 | "node_modules/loose-envify": {
460 | "version": "1.4.0",
461 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
462 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
463 | "license": "MIT",
464 | "dependencies": {
465 | "js-tokens": "^3.0.0 || ^4.0.0"
466 | },
467 | "bin": {
468 | "loose-envify": "cli.js"
469 | }
470 | },
471 | "node_modules/nanoid": {
472 | "version": "3.3.8",
473 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
474 | "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
475 | "funding": [
476 | {
477 | "type": "github",
478 | "url": "https://github.com/sponsors/ai"
479 | }
480 | ],
481 | "license": "MIT",
482 | "bin": {
483 | "nanoid": "bin/nanoid.cjs"
484 | },
485 | "engines": {
486 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
487 | }
488 | },
489 | "node_modules/next": {
490 | "version": "14.2.18",
491 | "resolved": "https://registry.npmjs.org/next/-/next-14.2.18.tgz",
492 | "integrity": "sha512-H9qbjDuGivUDEnK6wa+p2XKO+iMzgVgyr9Zp/4Iv29lKa+DYaxJGjOeEA+5VOvJh/M7HLiskehInSa0cWxVXUw==",
493 | "license": "MIT",
494 | "dependencies": {
495 | "@next/env": "14.2.18",
496 | "@swc/helpers": "0.5.5",
497 | "busboy": "1.6.0",
498 | "caniuse-lite": "^1.0.30001579",
499 | "graceful-fs": "^4.2.11",
500 | "postcss": "8.4.31",
501 | "styled-jsx": "5.1.1"
502 | },
503 | "bin": {
504 | "next": "dist/bin/next"
505 | },
506 | "engines": {
507 | "node": ">=18.17.0"
508 | },
509 | "optionalDependencies": {
510 | "@next/swc-darwin-arm64": "14.2.18",
511 | "@next/swc-darwin-x64": "14.2.18",
512 | "@next/swc-linux-arm64-gnu": "14.2.18",
513 | "@next/swc-linux-arm64-musl": "14.2.18",
514 | "@next/swc-linux-x64-gnu": "14.2.18",
515 | "@next/swc-linux-x64-musl": "14.2.18",
516 | "@next/swc-win32-arm64-msvc": "14.2.18",
517 | "@next/swc-win32-ia32-msvc": "14.2.18",
518 | "@next/swc-win32-x64-msvc": "14.2.18"
519 | },
520 | "peerDependencies": {
521 | "@opentelemetry/api": "^1.1.0",
522 | "@playwright/test": "^1.41.2",
523 | "react": "^18.2.0",
524 | "react-dom": "^18.2.0",
525 | "sass": "^1.3.0"
526 | },
527 | "peerDependenciesMeta": {
528 | "@opentelemetry/api": {
529 | "optional": true
530 | },
531 | "@playwright/test": {
532 | "optional": true
533 | },
534 | "sass": {
535 | "optional": true
536 | }
537 | }
538 | },
539 | "node_modules/next-auth": {
540 | "version": "5.0.0-beta.25",
541 | "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-5.0.0-beta.25.tgz",
542 | "integrity": "sha512-2dJJw1sHQl2qxCrRk+KTQbeH+izFbGFPuJj5eGgBZFYyiYYtvlrBeUw1E/OJJxTRjuxbSYGnCTkUIRsIIW0bog==",
543 | "license": "ISC",
544 | "dependencies": {
545 | "@auth/core": "0.37.2"
546 | },
547 | "peerDependencies": {
548 | "@simplewebauthn/browser": "^9.0.1",
549 | "@simplewebauthn/server": "^9.0.2",
550 | "next": "^14.0.0-0 || ^15.0.0-0",
551 | "nodemailer": "^6.6.5",
552 | "react": "^18.2.0 || ^19.0.0-0"
553 | },
554 | "peerDependenciesMeta": {
555 | "@simplewebauthn/browser": {
556 | "optional": true
557 | },
558 | "@simplewebauthn/server": {
559 | "optional": true
560 | },
561 | "nodemailer": {
562 | "optional": true
563 | }
564 | }
565 | },
566 | "node_modules/next-auth/node_modules/@auth/core": {
567 | "version": "0.37.2",
568 | "resolved": "https://registry.npmjs.org/@auth/core/-/core-0.37.2.tgz",
569 | "integrity": "sha512-kUvzyvkcd6h1vpeMAojK2y7+PAV5H+0Cc9+ZlKYDFhDY31AlvsB+GW5vNO4qE3Y07KeQgvNO9U0QUx/fN62kBw==",
570 | "license": "ISC",
571 | "dependencies": {
572 | "@panva/hkdf": "^1.2.1",
573 | "@types/cookie": "0.6.0",
574 | "cookie": "0.7.1",
575 | "jose": "^5.9.3",
576 | "oauth4webapi": "^3.0.0",
577 | "preact": "10.11.3",
578 | "preact-render-to-string": "5.2.3"
579 | },
580 | "peerDependencies": {
581 | "@simplewebauthn/browser": "^9.0.1",
582 | "@simplewebauthn/server": "^9.0.2",
583 | "nodemailer": "^6.8.0"
584 | },
585 | "peerDependenciesMeta": {
586 | "@simplewebauthn/browser": {
587 | "optional": true
588 | },
589 | "@simplewebauthn/server": {
590 | "optional": true
591 | },
592 | "nodemailer": {
593 | "optional": true
594 | }
595 | }
596 | },
597 | "node_modules/next-auth/node_modules/preact": {
598 | "version": "10.11.3",
599 | "resolved": "https://registry.npmjs.org/preact/-/preact-10.11.3.tgz",
600 | "integrity": "sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==",
601 | "license": "MIT",
602 | "funding": {
603 | "type": "opencollective",
604 | "url": "https://opencollective.com/preact"
605 | }
606 | },
607 | "node_modules/next-auth/node_modules/preact-render-to-string": {
608 | "version": "5.2.3",
609 | "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.3.tgz",
610 | "integrity": "sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==",
611 | "license": "MIT",
612 | "dependencies": {
613 | "pretty-format": "^3.8.0"
614 | },
615 | "peerDependencies": {
616 | "preact": ">=10"
617 | }
618 | },
619 | "node_modules/oauth4webapi": {
620 | "version": "3.1.3",
621 | "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.1.3.tgz",
622 | "integrity": "sha512-dik5wEMdFL5p3JlijYvM7wMNCgaPhblLIDCZtdXcaZp5wgu5Iwmsu7lMzgFhIDTi5d0BJo03LVoOoFQvXMeOeQ==",
623 | "license": "MIT",
624 | "funding": {
625 | "url": "https://github.com/sponsors/panva"
626 | }
627 | },
628 | "node_modules/picocolors": {
629 | "version": "1.1.1",
630 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
631 | "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
632 | "license": "ISC"
633 | },
634 | "node_modules/postcss": {
635 | "version": "8.4.31",
636 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
637 | "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
638 | "funding": [
639 | {
640 | "type": "opencollective",
641 | "url": "https://opencollective.com/postcss/"
642 | },
643 | {
644 | "type": "tidelift",
645 | "url": "https://tidelift.com/funding/github/npm/postcss"
646 | },
647 | {
648 | "type": "github",
649 | "url": "https://github.com/sponsors/ai"
650 | }
651 | ],
652 | "license": "MIT",
653 | "dependencies": {
654 | "nanoid": "^3.3.6",
655 | "picocolors": "^1.0.0",
656 | "source-map-js": "^1.0.2"
657 | },
658 | "engines": {
659 | "node": "^10 || ^12 || >=14"
660 | }
661 | },
662 | "node_modules/preact": {
663 | "version": "10.24.3",
664 | "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.3.tgz",
665 | "integrity": "sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==",
666 | "license": "MIT",
667 | "funding": {
668 | "type": "opencollective",
669 | "url": "https://opencollective.com/preact"
670 | }
671 | },
672 | "node_modules/preact-render-to-string": {
673 | "version": "6.5.11",
674 | "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-6.5.11.tgz",
675 | "integrity": "sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw==",
676 | "license": "MIT",
677 | "peerDependencies": {
678 | "preact": ">=10"
679 | }
680 | },
681 | "node_modules/pretty-format": {
682 | "version": "3.8.0",
683 | "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz",
684 | "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==",
685 | "license": "MIT"
686 | },
687 | "node_modules/prisma": {
688 | "version": "5.22.0",
689 | "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.22.0.tgz",
690 | "integrity": "sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==",
691 | "devOptional": true,
692 | "hasInstallScript": true,
693 | "license": "Apache-2.0",
694 | "dependencies": {
695 | "@prisma/engines": "5.22.0"
696 | },
697 | "bin": {
698 | "prisma": "build/index.js"
699 | },
700 | "engines": {
701 | "node": ">=16.13"
702 | },
703 | "optionalDependencies": {
704 | "fsevents": "2.3.3"
705 | }
706 | },
707 | "node_modules/react": {
708 | "version": "18.3.1",
709 | "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
710 | "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
711 | "license": "MIT",
712 | "dependencies": {
713 | "loose-envify": "^1.1.0"
714 | },
715 | "engines": {
716 | "node": ">=0.10.0"
717 | }
718 | },
719 | "node_modules/react-dom": {
720 | "version": "18.3.1",
721 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
722 | "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
723 | "license": "MIT",
724 | "dependencies": {
725 | "loose-envify": "^1.1.0",
726 | "scheduler": "^0.23.2"
727 | },
728 | "peerDependencies": {
729 | "react": "^18.3.1"
730 | }
731 | },
732 | "node_modules/scheduler": {
733 | "version": "0.23.2",
734 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
735 | "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
736 | "license": "MIT",
737 | "dependencies": {
738 | "loose-envify": "^1.1.0"
739 | }
740 | },
741 | "node_modules/source-map-js": {
742 | "version": "1.2.1",
743 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
744 | "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
745 | "license": "BSD-3-Clause",
746 | "engines": {
747 | "node": ">=0.10.0"
748 | }
749 | },
750 | "node_modules/streamsearch": {
751 | "version": "1.1.0",
752 | "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
753 | "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
754 | "engines": {
755 | "node": ">=10.0.0"
756 | }
757 | },
758 | "node_modules/styled-jsx": {
759 | "version": "5.1.1",
760 | "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz",
761 | "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==",
762 | "license": "MIT",
763 | "dependencies": {
764 | "client-only": "0.0.1"
765 | },
766 | "engines": {
767 | "node": ">= 12.0.0"
768 | },
769 | "peerDependencies": {
770 | "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0"
771 | },
772 | "peerDependenciesMeta": {
773 | "@babel/core": {
774 | "optional": true
775 | },
776 | "babel-plugin-macros": {
777 | "optional": true
778 | }
779 | }
780 | },
781 | "node_modules/tslib": {
782 | "version": "2.8.1",
783 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
784 | "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
785 | "license": "0BSD"
786 | },
787 | "node_modules/typescript": {
788 | "version": "5.7.2",
789 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz",
790 | "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==",
791 | "dev": true,
792 | "license": "Apache-2.0",
793 | "bin": {
794 | "tsc": "bin/tsc",
795 | "tsserver": "bin/tsserver"
796 | },
797 | "engines": {
798 | "node": ">=14.17"
799 | }
800 | },
801 | "node_modules/undici-types": {
802 | "version": "6.19.8",
803 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
804 | "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
805 | "dev": true,
806 | "license": "MIT"
807 | },
808 | "node_modules/zod": {
809 | "version": "3.23.8",
810 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
811 | "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
812 | "license": "MIT",
813 | "funding": {
814 | "url": "https://github.com/sponsors/colinhacks"
815 | }
816 | }
817 | }
818 | }
819 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nextjs-mongodb-prisma-auth-template",
3 | "version": "0.1.0",
4 | "private": true,
5 | "author": "mmvergara",
6 | "description": "Template repository for building a Next.js application with MongoDB, Prisma, and Next Auth authentication",
7 | "scripts": {
8 | "dev": "next dev",
9 | "build": "prisma generate && next build",
10 | "start": "next start",
11 | "lint": "next lint"
12 | },
13 | "dependencies": {
14 | "@auth/prisma-adapter": "^2.0.0",
15 | "@prisma/client": "^5.13.0",
16 | "bcryptjs": "^2.4.3",
17 | "next": "^14.2.18",
18 | "next-auth": "^5.0.0-beta.17",
19 | "react": "^18",
20 | "react-dom": "^18",
21 | "zod": "^3.23.5"
22 | },
23 | "devDependencies": {
24 | "@types/bcryptjs": "^2.4.6",
25 | "@types/node": "^20",
26 | "@types/react": "^18",
27 | "@types/react-dom": "^18",
28 | "prisma": "^5.13.0",
29 | "typescript": "^5"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/prisma/schema.prisma:
--------------------------------------------------------------------------------
1 |
2 | generator client {
3 | provider = "prisma-client-js"
4 | }
5 |
6 | datasource db {
7 | provider = "mongodb"
8 | url = env("DATABASE_URL")
9 | }
10 |
11 |
12 |
13 | model User {
14 | id String @id @default(auto()) @map("_id") @db.ObjectId
15 | username String
16 | email String @unique
17 | password String
18 | image String?
19 | accounts Account[]
20 |
21 | createdAt DateTime @default(now())
22 | updatedAt DateTime @updatedAt
23 | }
24 |
25 | model Account {
26 | id String @id @default(auto()) @map("_id") @db.ObjectId
27 | userId String @db.ObjectId
28 | type String
29 | provider String
30 | providerAccountId String
31 | refresh_token String? @db.String
32 | access_token String? @db.String
33 | expires_at Int?
34 | token_type String?
35 | scope String?
36 | id_token String? @db.String
37 | session_state String?
38 |
39 | createdAt DateTime @default(now())
40 | updatedAt DateTime @updatedAt
41 |
42 | user User @relation(fields: [userId], references: [id], onDelete: Cascade)
43 |
44 | @@unique([provider, providerAccountId])
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/remove_mee.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mmvergara/nextjs-mongodb-prisma-auth-template/4bda47ee44a59929a42c441841da96e8551a59f7/remove_mee.png
--------------------------------------------------------------------------------
/routes.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Public routes
3 | * @type {string[]}
4 | */
5 | export const publicRoutes = ["/", "/sign-in", "/sign-up"];
6 |
7 | export const authRoutes = ["/sign-in", "/sign-up"];
8 |
9 | export const apiAuthPrefix = "/api/auth";
10 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "plugins": [
16 | {
17 | "name": "next"
18 | }
19 | ],
20 | "paths": {
21 | "@/*": ["./*"]
22 | }
23 | },
24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25 | "exclude": ["node_modules"]
26 | }
27 |
--------------------------------------------------------------------------------