├── .eslintrc.json ├── .gitignore ├── .vscode └── settings.json ├── README.md ├── change.env ├── middleware.ts ├── next-env.d.ts ├── next.config.js ├── package-lock.json ├── package.json ├── pages ├── _app.tsx ├── admin.tsx ├── api │ ├── auth │ │ └── [...nextauth].ts │ └── hello.ts ├── auth │ ├── signin.tsx │ └── signup.tsx ├── index.tsx └── protected.tsx ├── public ├── favicon.ico └── vercel.svg ├── styles ├── Home.module.css └── globals.css └── 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 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": ["yourapp"] 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a Next.js Auth project made for educational purpose 2 | Part one: [See Video](https://youtu.be/EFucgPdjeNg). 3 | Part two: [See Video](https://youtu.be/ollnut-J47s). 4 | 5 | Just do `npm install` 6 | -------------------------------------------------------------------------------- /change.env: -------------------------------------------------------------------------------- 1 | NEXTAUTH_SECRET=my_little_secret -------------------------------------------------------------------------------- /middleware.ts: -------------------------------------------------------------------------------- 1 | import { withAuth } from "next-auth/middleware"; 2 | import { NextRequest, NextResponse } from "next/server"; 3 | 4 | export default withAuth( 5 | function middleware(req: NextRequest) { 6 | // return NextResponse 7 | return NextResponse.rewrite(new URL("/admin", req.url)); 8 | }, 9 | { 10 | callbacks: { 11 | authorized({ token }) { 12 | return token?.role === "admin"; 13 | }, 14 | }, 15 | } 16 | ); 17 | 18 | export const config = { matcher: ["/", "/admin"] }; 19 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | } 6 | 7 | module.exports = nextConfig 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "auth-app", 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 | "next": "12.2.2", 13 | "next-auth": "^4.10.1", 14 | "react": "18.2.0", 15 | "react-dom": "18.2.0" 16 | }, 17 | "devDependencies": { 18 | "@types/node": "18.0.6", 19 | "@types/react": "18.0.15", 20 | "@types/react-dom": "18.0.6", 21 | "eslint": "8.20.0", 22 | "eslint-config-next": "12.2.2", 23 | "typescript": "4.7.4" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import "../styles/globals.css"; 2 | import type { AppProps } from "next/app"; 3 | import { SessionProvider } from "next-auth/react"; 4 | 5 | function MyApp({ Component, pageProps }: AppProps) { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | } 12 | 13 | export default MyApp; 14 | -------------------------------------------------------------------------------- /pages/admin.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from "react"; 2 | 3 | interface Props {} 4 | 5 | const Admin: FC = (props): JSX.Element => { 6 | return
admin
; 7 | }; 8 | 9 | export default Admin; 10 | -------------------------------------------------------------------------------- /pages/api/auth/[...nextauth].ts: -------------------------------------------------------------------------------- 1 | import NextAuth, { NextAuthOptions } from "next-auth"; 2 | import CredentialsProvider from "next-auth/providers/credentials"; 3 | 4 | const authOptions: NextAuthOptions = { 5 | session: { 6 | strategy: "jwt", 7 | }, 8 | providers: [ 9 | CredentialsProvider({ 10 | type: "credentials", 11 | credentials: {}, 12 | authorize(credentials, req) { 13 | const { email, password } = credentials as { 14 | email: string; 15 | password: string; 16 | }; 17 | // perform you login logic 18 | // find out user from db 19 | if (email !== "john@gmail.com" || password !== "1234") { 20 | throw new Error("invalid credentials"); 21 | } 22 | 23 | // if everything is fine 24 | return { 25 | id: "1234", 26 | name: "John Doe", 27 | email: "john@gmail.com", 28 | role: "admin", 29 | }; 30 | }, 31 | }), 32 | ], 33 | pages: { 34 | signIn: "/auth/signin", 35 | // error: '/auth/error', 36 | // signOut: '/auth/signout' 37 | }, 38 | callbacks: { 39 | jwt(params) { 40 | // update token 41 | if (params.user?.role) { 42 | params.token.role = params.user.role; 43 | } 44 | // return final_token 45 | return params.token; 46 | }, 47 | }, 48 | }; 49 | 50 | export default NextAuth(authOptions); 51 | -------------------------------------------------------------------------------- /pages/api/hello.ts: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | import type { NextApiRequest, NextApiResponse } from 'next' 3 | 4 | type Data = { 5 | name: string 6 | } 7 | 8 | export default function handler( 9 | req: NextApiRequest, 10 | res: NextApiResponse 11 | ) { 12 | res.status(200).json({ name: 'John Doe' }) 13 | } 14 | -------------------------------------------------------------------------------- /pages/auth/signin.tsx: -------------------------------------------------------------------------------- 1 | import { NextPage } from "next"; 2 | import { signIn } from "next-auth/react"; 3 | import { FormEventHandler, useState } from "react"; 4 | 5 | interface Props {} 6 | 7 | const SignIn: NextPage = (props): JSX.Element => { 8 | const [userInfo, setUserInfo] = useState({ email: "", password: "" }); 9 | const handleSubmit: FormEventHandler = async (e) => { 10 | // validate your userinfo 11 | e.preventDefault(); 12 | 13 | const res = await signIn("credentials", { 14 | email: userInfo.email, 15 | password: userInfo.password, 16 | redirect: false, 17 | }); 18 | 19 | console.log(res); 20 | }; 21 | return ( 22 |
23 |
24 |

Login

25 | 28 | setUserInfo({ ...userInfo, email: target.value }) 29 | } 30 | type="email" 31 | placeholder="john@email.com" 32 | /> 33 | 36 | setUserInfo({ ...userInfo, password: target.value }) 37 | } 38 | type="password" 39 | placeholder="********" 40 | /> 41 | 42 |
43 |
44 | ); 45 | }; 46 | 47 | export default SignIn; 48 | -------------------------------------------------------------------------------- /pages/auth/signup.tsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndpniraj/next-auth-demo-yt/763317ea1ec82f3007bd7a5db5f0d2deb8559f06/pages/auth/signup.tsx -------------------------------------------------------------------------------- /pages/index.tsx: -------------------------------------------------------------------------------- 1 | import type { NextPage } from "next"; 2 | import Head from "next/head"; 3 | import Image from "next/image"; 4 | import { signIn } from "next-auth/react"; 5 | import styles from "../styles/Home.module.css"; 6 | 7 | const Home: NextPage = () => { 8 | return ( 9 |
10 | 11 | Create Next App 12 | 13 | 14 | 15 | 16 |
17 | 24 |
25 | 26 |
27 |
28 | ); 29 | }; 30 | 31 | export default Home; 32 | -------------------------------------------------------------------------------- /pages/protected.tsx: -------------------------------------------------------------------------------- 1 | import { NextPage } from "next"; 2 | import { useSession } from "next-auth/react"; 3 | import Router from "next/router"; 4 | import { useEffect } from "react"; 5 | 6 | const Protected: NextPage = (): JSX.Element => { 7 | const { status, data } = useSession(); 8 | 9 | useEffect(() => { 10 | if (status === "unauthenticated") Router.replace("/auth/signin"); 11 | }, [status]); 12 | 13 | if (status === "authenticated") 14 | return ( 15 |
16 | This page is Protected for special people. like{"\n"} 17 | {JSON.stringify(data.user, null, 2)} 18 |
19 | ); 20 | 21 | return
loading
; 22 | }; 23 | 24 | export default Protected; 25 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndpniraj/next-auth-demo-yt/763317ea1ec82f3007bd7a5db5f0d2deb8559f06/public/favicon.ico -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /styles/Home.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | padding: 0 2rem; 3 | } 4 | 5 | .main { 6 | min-height: 100vh; 7 | padding: 4rem 0; 8 | flex: 1; 9 | display: flex; 10 | flex-direction: column; 11 | justify-content: center; 12 | align-items: center; 13 | } 14 | 15 | .footer { 16 | display: flex; 17 | flex: 1; 18 | padding: 2rem 0; 19 | border-top: 1px solid #eaeaea; 20 | justify-content: center; 21 | align-items: center; 22 | } 23 | 24 | .footer a { 25 | display: flex; 26 | justify-content: center; 27 | align-items: center; 28 | flex-grow: 1; 29 | } 30 | 31 | .title a { 32 | color: #0070f3; 33 | text-decoration: none; 34 | } 35 | 36 | .title a:hover, 37 | .title a:focus, 38 | .title a:active { 39 | text-decoration: underline; 40 | } 41 | 42 | .title { 43 | margin: 0; 44 | line-height: 1.15; 45 | font-size: 4rem; 46 | } 47 | 48 | .title, 49 | .description { 50 | text-align: center; 51 | } 52 | 53 | .description { 54 | margin: 4rem 0; 55 | line-height: 1.5; 56 | font-size: 1.5rem; 57 | } 58 | 59 | .code { 60 | background: #fafafa; 61 | border-radius: 5px; 62 | padding: 0.75rem; 63 | font-size: 1.1rem; 64 | font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, 65 | Bitstream Vera Sans Mono, Courier New, monospace; 66 | } 67 | 68 | .grid { 69 | display: flex; 70 | align-items: center; 71 | justify-content: center; 72 | flex-wrap: wrap; 73 | max-width: 800px; 74 | } 75 | 76 | .card { 77 | margin: 1rem; 78 | padding: 1.5rem; 79 | text-align: left; 80 | color: inherit; 81 | text-decoration: none; 82 | border: 1px solid #eaeaea; 83 | border-radius: 10px; 84 | transition: color 0.15s ease, border-color 0.15s ease; 85 | max-width: 300px; 86 | } 87 | 88 | .card:hover, 89 | .card:focus, 90 | .card:active { 91 | color: #0070f3; 92 | border-color: #0070f3; 93 | } 94 | 95 | .card h2 { 96 | margin: 0 0 1rem 0; 97 | font-size: 1.5rem; 98 | } 99 | 100 | .card p { 101 | margin: 0; 102 | font-size: 1.25rem; 103 | line-height: 1.5; 104 | } 105 | 106 | .logo { 107 | height: 1em; 108 | margin-left: 0.5rem; 109 | } 110 | 111 | @media (max-width: 600px) { 112 | .grid { 113 | width: 100%; 114 | flex-direction: column; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 6 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 7 | } 8 | 9 | a { 10 | color: inherit; 11 | text-decoration: none; 12 | } 13 | 14 | * { 15 | box-sizing: border-box; 16 | } 17 | 18 | .sign-in-form{ 19 | width: 100%; 20 | height: 100vh; 21 | display: flex; 22 | justify-content: center; 23 | align-items: center; 24 | } 25 | .sign-in-form input{ 26 | display: block; 27 | padding: 5px; 28 | border: 1px gray solid; 29 | border-radius: 3px; 30 | margin-top: 15px; 31 | } 32 | .sign-in-form h1{ 33 | font-size: 18px; 34 | text-align: center; 35 | } 36 | .sign-in-form form{ 37 | padding: 50px 15px; 38 | border: 2px gray solid; 39 | border-radius: 3px; 40 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true 17 | }, 18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 19 | "exclude": ["node_modules"] 20 | } 21 | --------------------------------------------------------------------------------