├── .eslintrc.json ├── app ├── favicon.ico ├── dashboard │ └── page.jsx ├── globals.css ├── Providers.js ├── register │ └── page.jsx ├── page.jsx ├── api │ ├── userExists │ │ └── route.js │ ├── register │ │ └── route.js │ └── auth │ │ └── [...nextauth] │ │ └── route.js └── layout.js ├── jsconfig.json ├── next.config.js ├── postcss.config.js ├── middleware.js ├── lib └── mongodb.js ├── models └── user.js ├── .gitignore ├── tailwind.config.js ├── package.json ├── public ├── vercel.svg └── next.svg ├── components ├── UserInfo.jsx ├── LoginForm.jsx └── RegisterForm.jsx └── README.md /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Godsont/auth-with-credentials/HEAD/app/favicon.ico -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "@/*": ["./*"] 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /middleware.js: -------------------------------------------------------------------------------- 1 | export { default } from "next-auth/middleware"; 2 | 3 | export const config = { matcher: ["/dashboard"] }; 4 | -------------------------------------------------------------------------------- /app/dashboard/page.jsx: -------------------------------------------------------------------------------- 1 | import UserInfo from "@/components/UserInfo"; 2 | 3 | export default function Dashboard() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | input { 6 | @apply w-[400px] border border-gray-200 py-2 px-6 bg-zinc-100/40; 7 | } 8 | -------------------------------------------------------------------------------- /app/Providers.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { SessionProvider } from "next-auth/react"; 4 | 5 | export const AuthProvider = ({ children }) => { 6 | return {children}; 7 | }; 8 | -------------------------------------------------------------------------------- /lib/mongodb.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | export const connectMongoDB = async () => { 4 | try { 5 | await mongoose.connect(process.env.MONGODB_URI); 6 | console.log("Connected to MongoDB"); 7 | } catch (error) { 8 | console.log("Error connecting to MongoDB: ", error); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /app/register/page.jsx: -------------------------------------------------------------------------------- 1 | import RegisterForm from "@/components/RegisterForm"; 2 | import { getServerSession } from "next-auth"; 3 | import { redirect } from "next/navigation"; 4 | import { authOptions } from "../api/auth/[...nextauth]/route"; 5 | 6 | export default async function Register() { 7 | const session = await getServerSession(authOptions); 8 | 9 | if (session) redirect("/dashboard"); 10 | 11 | return ; 12 | } 13 | -------------------------------------------------------------------------------- /app/page.jsx: -------------------------------------------------------------------------------- 1 | import LoginForm from "@/components/LoginForm"; 2 | import { getServerSession } from "next-auth"; 3 | import { redirect } from "next/navigation"; 4 | import { authOptions } from "./api/auth/[...nextauth]/route"; 5 | 6 | export default async function Home() { 7 | const session = await getServerSession(authOptions); 8 | 9 | if (session) redirect("/dashboard"); 10 | 11 | return ( 12 |
13 | 14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /models/user.js: -------------------------------------------------------------------------------- 1 | import mongoose, { Schema, models } from "mongoose"; 2 | 3 | const userSchema = new Schema( 4 | { 5 | name: { 6 | type: String, 7 | required: true, 8 | }, 9 | email: { 10 | type: String, 11 | required: true, 12 | }, 13 | password: { 14 | type: String, 15 | required: true, 16 | }, 17 | }, 18 | { timestamps: true } 19 | ); 20 | 21 | const User = models.User || mongoose.model("User", userSchema); 22 | export default User; 23 | -------------------------------------------------------------------------------- /app/api/userExists/route.js: -------------------------------------------------------------------------------- 1 | import { connectMongoDB } from "@/lib/mongodb"; 2 | import User from "@/models/user"; 3 | import { NextResponse } from "next/server"; 4 | 5 | export async function POST(req) { 6 | try { 7 | await connectMongoDB(); 8 | const { email } = await req.json(); 9 | const user = await User.findOne({ email }).select("_id"); 10 | console.log("user: ", user); 11 | return NextResponse.json({ user }); 12 | } catch (error) { 13 | console.log(error); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.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 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | .env 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | './pages/**/*.{js,ts,jsx,tsx,mdx}', 5 | './components/**/*.{js,ts,jsx,tsx,mdx}', 6 | './app/**/*.{js,ts,jsx,tsx,mdx}', 7 | ], 8 | theme: { 9 | extend: { 10 | backgroundImage: { 11 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 12 | 'gradient-conic': 13 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', 14 | }, 15 | }, 16 | }, 17 | plugins: [], 18 | } 19 | -------------------------------------------------------------------------------- /app/layout.js: -------------------------------------------------------------------------------- 1 | import { AuthProvider } from "./Providers"; 2 | import "./globals.css"; 3 | import { Inter } from "next/font/google"; 4 | 5 | const inter = Inter({ subsets: ["latin"] }); 6 | 7 | export const metadata = { 8 | title: "Create Next App", 9 | description: "Generated by create next app", 10 | }; 11 | 12 | export default function RootLayout({ children }) { 13 | return ( 14 | 15 | 16 | {children} 17 | 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "authapp", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "autoprefixer": "10.4.14", 13 | "bcryptjs": "^2.4.3", 14 | "eslint": "8.46.0", 15 | "eslint-config-next": "13.4.12", 16 | "mongoose": "^7.4.1", 17 | "next": "13.4.12", 18 | "next-auth": "^4.22.3", 19 | "postcss": "8.4.27", 20 | "react": "18.2.0", 21 | "react-dom": "18.2.0", 22 | "tailwindcss": "3.3.3" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/api/register/route.js: -------------------------------------------------------------------------------- 1 | import { connectMongoDB } from "@/lib/mongodb"; 2 | import User from "@/models/user"; 3 | import { NextResponse } from "next/server"; 4 | import bcrypt from "bcryptjs"; 5 | 6 | export async function POST(req) { 7 | try { 8 | const { name, email, password } = await req.json(); 9 | const hashedPassword = await bcrypt.hash(password, 10); 10 | await connectMongoDB(); 11 | await User.create({ name, email, password: hashedPassword }); 12 | 13 | return NextResponse.json({ message: "User registered." }, { status: 201 }); 14 | } catch (error) { 15 | return NextResponse.json( 16 | { message: "An error occurred while registering the user." }, 17 | { status: 500 } 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /components/UserInfo.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { signOut } from "next-auth/react"; 4 | import { useSession } from "next-auth/react"; 5 | 6 | export default function UserInfo() { 7 | const { data: session } = useSession(); 8 | 9 | return ( 10 |
11 |
12 |
13 | Name: {session?.user?.name} 14 |
15 |
16 | Email: {session?.user?.email} 17 |
18 | 24 |
25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /app/api/auth/[...nextauth]/route.js: -------------------------------------------------------------------------------- 1 | import { connectMongoDB } from "@/lib/mongodb"; 2 | import User from "@/models/user"; 3 | import NextAuth from "next-auth/next"; 4 | import CredentialsProvider from "next-auth/providers/credentials"; 5 | import bcrypt from "bcryptjs"; 6 | 7 | export const authOptions = { 8 | providers: [ 9 | CredentialsProvider({ 10 | name: "credentials", 11 | credentials: {}, 12 | 13 | async authorize(credentials) { 14 | const { email, password } = credentials; 15 | 16 | try { 17 | await connectMongoDB(); 18 | const user = await User.findOne({ email }); 19 | 20 | if (!user) { 21 | return null; 22 | } 23 | 24 | const passwordsMatch = await bcrypt.compare(password, user.password); 25 | 26 | if (!passwordsMatch) { 27 | return null; 28 | } 29 | 30 | return user; 31 | } catch (error) { 32 | console.log("Error: ", error); 33 | } 34 | }, 35 | }), 36 | ], 37 | session: { 38 | strategy: "jwt", 39 | }, 40 | secret: process.env.NEXTAUTH_SECRET, 41 | pages: { 42 | signIn: "/", 43 | }, 44 | }; 45 | 46 | const handler = NextAuth(authOptions); 47 | 48 | export { handler as GET, handler as POST }; 49 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | ``` 14 | 15 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 16 | 17 | You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file. 18 | 19 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 20 | 21 | ## Learn More 22 | 23 | To learn more about Next.js, take a look at the following resources: 24 | 25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 27 | 28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 29 | 30 | ## Deploy on Vercel 31 | 32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 33 | 34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 35 | -------------------------------------------------------------------------------- /components/LoginForm.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import Link from "next/link"; 4 | import { useState } from "react"; 5 | import { signIn } from "next-auth/react"; 6 | import { useRouter } from "next/navigation"; 7 | 8 | export default function LoginForm() { 9 | const [email, setEmail] = useState(""); 10 | const [password, setPassword] = useState(""); 11 | const [error, setError] = useState(""); 12 | 13 | const router = useRouter(); 14 | 15 | const handleSubmit = async (e) => { 16 | e.preventDefault(); 17 | 18 | try { 19 | const res = await signIn("credentials", { 20 | email, 21 | password, 22 | redirect: false, 23 | }); 24 | 25 | if (res.error) { 26 | setError("Invalid Credentials"); 27 | return; 28 | } 29 | 30 | router.replace("dashboard"); 31 | } catch (error) { 32 | console.log(error); 33 | } 34 | }; 35 | 36 | return ( 37 |
38 |
39 |

Login

40 | 41 |
42 | setEmail(e.target.value)} 44 | type="text" 45 | placeholder="Email" 46 | /> 47 | setPassword(e.target.value)} 49 | type="password" 50 | placeholder="Password" 51 | /> 52 | 55 | {error && ( 56 |
57 | {error} 58 |
59 | )} 60 | 61 | 62 | Don't have an account? Register 63 | 64 |
65 |
66 |
67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /components/RegisterForm.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import Link from "next/link"; 4 | import { useState } from "react"; 5 | import { useRouter } from "next/navigation"; 6 | 7 | export default function RegisterForm() { 8 | const [name, setName] = useState(""); 9 | const [email, setEmail] = useState(""); 10 | const [password, setPassword] = useState(""); 11 | const [error, setError] = useState(""); 12 | 13 | const router = useRouter(); 14 | 15 | const handleSubmit = async (e) => { 16 | e.preventDefault(); 17 | 18 | if (!name || !email || !password) { 19 | setError("All fields are necessary."); 20 | return; 21 | } 22 | 23 | try { 24 | const resUserExists = await fetch("api/userExists", { 25 | method: "POST", 26 | headers: { 27 | "Content-Type": "application/json", 28 | }, 29 | body: JSON.stringify({ email }), 30 | }); 31 | 32 | const { user } = await resUserExists.json(); 33 | 34 | if (user) { 35 | setError("User already exists."); 36 | return; 37 | } 38 | 39 | const res = await fetch("api/register", { 40 | method: "POST", 41 | headers: { 42 | "Content-Type": "application/json", 43 | }, 44 | body: JSON.stringify({ 45 | name, 46 | email, 47 | password, 48 | }), 49 | }); 50 | 51 | if (res.ok) { 52 | const form = e.target; 53 | form.reset(); 54 | router.push("/"); 55 | } else { 56 | console.log("User registration failed."); 57 | } 58 | } catch (error) { 59 | console.log("Error during registration: ", error); 60 | } 61 | }; 62 | 63 | return ( 64 |
65 |
66 |

Register

67 | 68 |
69 | setName(e.target.value)} 71 | type="text" 72 | placeholder="Full Name" 73 | /> 74 | setEmail(e.target.value)} 76 | type="text" 77 | placeholder="Email" 78 | /> 79 | setPassword(e.target.value)} 81 | type="password" 82 | placeholder="Password" 83 | /> 84 | 87 | 88 | {error && ( 89 |
90 | {error} 91 |
92 | )} 93 | 94 | 95 | Already have an account? Login 96 | 97 |
98 |
99 |
100 | ); 101 | } 102 | --------------------------------------------------------------------------------