├── .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 |
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 |
98 |
99 |
100 | );
101 | }
102 |
--------------------------------------------------------------------------------