├── .eslintrc.json
├── .gitignore
├── LICENSE
├── README.md
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
├── next.svg
└── vercel.svg
├── src
├── app
│ ├── api
│ │ ├── auth
│ │ │ └── [...nextauth]
│ │ │ │ └── route.ts
│ │ └── register
│ │ │ └── route.ts
│ ├── dashboard
│ │ └── page.tsx
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.tsx
│ ├── login
│ │ └── page.tsx
│ ├── page.tsx
│ └── register
│ │ └── page.tsx
├── components
│ └── Navbar.tsx
├── models
│ └── User.js
└── utils
│ ├── SessionProvider.tsx
│ └── db.js
├── tailwind.config.ts
└── tsconfig.json
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Umair
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 | 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 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | 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.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
37 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {}
3 |
4 | module.exports = nextConfig
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "next-auth-register",
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 | "bcryptjs": "^2.4.3",
13 | "mongoose": "^7.5.3",
14 | "next": "latest",
15 | "next-auth": "^4.23.1",
16 | "react": "latest",
17 | "react-dom": "latest"
18 | },
19 | "devDependencies": {
20 | "@types/node": "latest",
21 | "@types/react": "latest",
22 | "@types/react-dom": "latest",
23 | "autoprefixer": "latest",
24 | "eslint": "latest",
25 | "eslint-config-next": "latest",
26 | "postcss": "latest",
27 | "tailwindcss": "latest",
28 | "typescript": "latest"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/api/auth/[...nextauth]/route.ts:
--------------------------------------------------------------------------------
1 | import NextAuth from "next-auth";
2 | import { Account, User as AuthUser } from "next-auth";
3 | import GithubProvider from "next-auth/providers/github";
4 | import CredentialsProvider from "next-auth/providers/credentials";
5 | import bcrypt from "bcryptjs";
6 | import User from "@/models/User";
7 | import connect from "@/utils/db";
8 |
9 | export const authOptions: any = {
10 | // Configure one or more authentication providers
11 | providers: [
12 | CredentialsProvider({
13 | id: "credentials",
14 | name: "Credentials",
15 | credentials: {
16 | email: { label: "Email", type: "text" },
17 | password: { label: "Password", type: "password" },
18 | },
19 | async authorize(credentials: any) {
20 | await connect();
21 | try {
22 | const user = await User.findOne({ email: credentials.email });
23 | if (user) {
24 | const isPasswordCorrect = await bcrypt.compare(
25 | credentials.password,
26 | user.password
27 | );
28 | if (isPasswordCorrect) {
29 | return user;
30 | }
31 | }
32 | } catch (err: any) {
33 | throw new Error(err);
34 | }
35 | },
36 | }),
37 | GithubProvider({
38 | clientId: process.env.GITHUB_ID ?? "",
39 | clientSecret: process.env.GITHUB_SECRET ?? "",
40 | }),
41 | // ...add more providers here
42 | ],
43 | callbacks: {
44 | async signIn({ user, account }: { user: AuthUser; account: Account }) {
45 | if (account?.provider == "credentials") {
46 | return true;
47 | }
48 | if (account?.provider == "github") {
49 | await connect();
50 | try {
51 | const existingUser = await User.findOne({ email: user.email });
52 | if (!existingUser) {
53 | const newUser = new User({
54 | email: user.email,
55 | });
56 |
57 | await newUser.save();
58 | return true;
59 | }
60 | return true;
61 | } catch (err) {
62 | console.log("Error saving user", err);
63 | return false;
64 | }
65 | }
66 | },
67 | },
68 | };
69 |
70 | export const handler = NextAuth(authOptions);
71 | export { handler as GET, handler as POST };
72 |
--------------------------------------------------------------------------------
/src/app/api/register/route.ts:
--------------------------------------------------------------------------------
1 | import User from "@/models/User";
2 | import connect from "@/utils/db";
3 | import bcrypt from "bcryptjs";
4 | import { NextResponse } from "next/server";
5 |
6 | export const POST = async (request: any) => {
7 | const { email, password } = await request.json();
8 |
9 | await connect();
10 |
11 | const existingUser = await User.findOne({ email });
12 |
13 | if (existingUser) {
14 | return new NextResponse("Email is already in use", { status: 400 });
15 | }
16 |
17 | const hashedPassword = await bcrypt.hash(password, 5);
18 | const newUser = new User({
19 | email,
20 | password: hashedPassword,
21 | });
22 |
23 | try {
24 | await newUser.save();
25 | return new NextResponse("user is registered", { status: 200 });
26 | } catch (err: any) {
27 | return new NextResponse(err, {
28 | status: 500,
29 | });
30 | }
31 | };
32 |
--------------------------------------------------------------------------------
/src/app/dashboard/page.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { getServerSession } from "next-auth";
3 | import { redirect } from "next/navigation";
4 |
5 | const Dashboard = async () => {
6 | const session = await getServerSession();
7 | if (!session) {
8 | redirect("/");
9 | }
10 | return (
11 |
12 | Dashboard
13 |
14 | );
15 | };
16 |
17 | export default Dashboard;
18 |
--------------------------------------------------------------------------------
/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umairjameel321/next-auth-mongodb/f2f9ae569a978113cd32be1811b6156d0b6bb14a/src/app/favicon.ico
--------------------------------------------------------------------------------
/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | :root {
6 | --foreground-rgb: 0, 0, 0;
7 | --background-start-rgb: 214, 219, 220;
8 | --background-end-rgb: 255, 255, 255;
9 | }
10 |
11 | @media (prefers-color-scheme: dark) {
12 | :root {
13 | --foreground-rgb: 255, 255, 255;
14 | --background-start-rgb: 0, 0, 0;
15 | --background-end-rgb: 0, 0, 0;
16 | }
17 | }
18 |
19 | body {
20 | color: rgb(var(--foreground-rgb));
21 | background: linear-gradient(
22 | to bottom,
23 | transparent,
24 | rgb(var(--background-end-rgb))
25 | )
26 | rgb(var(--background-start-rgb));
27 | }
28 |
--------------------------------------------------------------------------------
/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import Navbar from "@/components/Navbar";
2 | import "./globals.css";
3 | import type { Metadata } from "next";
4 | import { Inter } from "next/font/google";
5 |
6 | import { getServerSession } from "next-auth";
7 | import SessionProvider from "@/utils/SessionProvider";
8 |
9 | const inter = Inter({ subsets: ["latin"] });
10 |
11 | export const metadata: Metadata = {
12 | title: "Create Next App",
13 | description: "Generated by create next app",
14 | };
15 |
16 | export default async function RootLayout({
17 | children,
18 | }: {
19 | children: React.ReactNode;
20 | }) {
21 | const session = await getServerSession();
22 | return (
23 |
24 |
25 |
26 |
27 |
28 | {children}
29 |
30 |
31 |
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/src/app/login/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useEffect, useState } from "react";
3 | import Link from "next/link";
4 | import { signIn, useSession } from "next-auth/react";
5 | import { useRouter } from "next/navigation";
6 |
7 | const Login = () => {
8 | const router = useRouter();
9 | const [error, setError] = useState("");
10 | // const session = useSession();
11 | const { data: session, status: sessionStatus } = useSession();
12 |
13 | useEffect(() => {
14 | if (sessionStatus === "authenticated") {
15 | router.replace("/dashboard");
16 | }
17 | }, [sessionStatus, router]);
18 |
19 | const isValidEmail = (email: string) => {
20 | const emailRegex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;
21 | return emailRegex.test(email);
22 | };
23 |
24 | const handleSubmit = async (e: any) => {
25 | e.preventDefault();
26 | const email = e.target[0].value;
27 | const password = e.target[1].value;
28 |
29 | if (!isValidEmail(email)) {
30 | setError("Email is invalid");
31 | return;
32 | }
33 |
34 | if (!password || password.length < 8) {
35 | setError("Password is invalid");
36 | return;
37 | }
38 |
39 | const res = await signIn("credentials", {
40 | redirect: false,
41 | email,
42 | password,
43 | });
44 |
45 | if (res?.error) {
46 | setError("Invalid email or password");
47 | if (res?.url) router.replace("/dashboard");
48 | } else {
49 | setError("");
50 | }
51 | };
52 |
53 | if (sessionStatus === "loading") {
54 | return Loading...
;
55 | }
56 |
57 | return (
58 | sessionStatus !== "authenticated" && (
59 |
60 |
61 |
Login
62 |
84 |
92 |
- OR -
93 |
97 | Register Here
98 |
99 |
100 |
101 | )
102 | );
103 | };
104 |
105 | export default Login;
106 |
--------------------------------------------------------------------------------
/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 |
3 | export default function Home() {
4 | return (
5 |
6 | Home Page
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/src/app/register/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useEffect, useState } from "react";
3 | import Link from "next/link";
4 | import { useRouter } from "next/navigation";
5 | import { useSession } from "next-auth/react";
6 |
7 | const Register = () => {
8 | const [error, setError] = useState("");
9 | const router = useRouter();
10 | const { data: session, status: sessionStatus } = useSession();
11 |
12 | useEffect(() => {
13 | if (sessionStatus === "authenticated") {
14 | router.replace("/dashboard");
15 | }
16 | }, [sessionStatus, router]);
17 |
18 | const isValidEmail = (email: string) => {
19 | const emailRegex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;
20 | return emailRegex.test(email);
21 | };
22 | const handleSubmit = async (e: any) => {
23 | e.preventDefault();
24 | const email = e.target[0].value;
25 | const password = e.target[1].value;
26 |
27 | if (!isValidEmail(email)) {
28 | setError("Email is invalid");
29 | return;
30 | }
31 |
32 | if (!password || password.length < 8) {
33 | setError("Password is invalid");
34 | return;
35 | }
36 |
37 | try {
38 | const res = await fetch("/api/register", {
39 | method: "POST",
40 | headers: {
41 | "Content-Type": "application/json",
42 | },
43 | body: JSON.stringify({
44 | email,
45 | password,
46 | }),
47 | });
48 | if (res.status === 400) {
49 | setError("This email is already registered");
50 | }
51 | if (res.status === 200) {
52 | setError("");
53 | router.push("/login");
54 | }
55 | } catch (error) {
56 | setError("Error, try again");
57 | console.log(error);
58 | }
59 | };
60 |
61 | if (sessionStatus === "loading") {
62 | return Loading...
;
63 | }
64 |
65 | return (
66 | sessionStatus !== "authenticated" && (
67 |
68 |
69 |
Register
70 |
92 |
- OR -
93 |
97 | Login with an existing account
98 |
99 |
100 |
101 | )
102 | );
103 | };
104 |
105 | export default Register;
106 |
--------------------------------------------------------------------------------
/src/components/Navbar.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React from "react";
3 | import Link from "next/link";
4 | import { signOut, useSession } from "next-auth/react";
5 |
6 | const Navbar = () => {
7 | const { data: session }: any = useSession();
8 | return (
9 |
10 |
11 |
12 |
13 |
- Home
14 |
15 |
16 |
17 |
18 |
- Dashboard
19 |
20 | {!session ? (
21 | <>
22 |
23 | - Login
24 |
25 |
26 | - Register
27 |
28 | >
29 | ) : (
30 | <>
31 | {session.user?.email}
32 | -
33 |
41 |
42 | >
43 | )}
44 |
45 |
46 |
47 | );
48 | };
49 |
50 | export default Navbar;
51 |
--------------------------------------------------------------------------------
/src/models/User.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 |
3 | const { Schema } = mongoose;
4 |
5 | const userSchema = new Schema(
6 | {
7 | email: {
8 | type: String,
9 | unique: true,
10 | required: true,
11 | },
12 | password: {
13 | type: String,
14 | required: false,
15 | },
16 | },
17 | { timestamps: true }
18 | );
19 |
20 | export default mongoose.models.User || mongoose.model("User", userSchema);
21 |
--------------------------------------------------------------------------------
/src/utils/SessionProvider.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React from "react";
3 | import { SessionProvider } from "next-auth/react";
4 |
5 | const AuthProvider = ({ children }: any) => {
6 | return {children};
7 | };
8 |
9 | export default AuthProvider;
10 |
--------------------------------------------------------------------------------
/src/utils/db.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 |
3 | const connect = async () => {
4 | if (mongoose.connections[0].readyState) return;
5 |
6 | try {
7 | await mongoose.connect(process.env.MONGO_URL, {
8 | useNewUrlParser: true,
9 | useUnifiedTopology: true,
10 | });
11 | console.log("Mongo Connection successfully established.");
12 | } catch (error) {
13 | throw new Error("Error connecting to Mongoose");
14 | }
15 | };
16 |
17 | export default connect;
18 |
--------------------------------------------------------------------------------
/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from 'tailwindcss'
2 |
3 | const config: Config = {
4 | content: [
5 | './src/pages/**/*.{js,ts,jsx,tsx,mdx}',
6 | './src/components/**/*.{js,ts,jsx,tsx,mdx}',
7 | './src/app/**/*.{js,ts,jsx,tsx,mdx}',
8 | ],
9 | theme: {
10 | extend: {
11 | backgroundImage: {
12 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
13 | 'gradient-conic':
14 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
15 | },
16 | },
17 | },
18 | plugins: [],
19 | }
20 | export default config
21 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./src/*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------