From 0658d110ba61ddb8a7df4e9b5fe1b91f4612a134 Mon Sep 17 00:00:00 2001 From: Sianida26 Date: Sat, 6 Jan 2024 16:14:35 +0700 Subject: [PATCH] Added base auth logic --- .env | 1 + package.json | 3 + pnpm-lock.yaml | 151 +++++++++++++++++++++++- prisma/schema.prisma | 18 +++ src/app/(auth)/login/page.tsx | 67 +++++++++++ src/app/api/auth/[...nextauth]/route.ts | 3 + src/config/auth.ts | 0 src/features/auth/authUtils.ts | 10 ++ src/features/auth/index.ts | 31 +++++ 9 files changed, 281 insertions(+), 3 deletions(-) create mode 100644 .env create mode 100644 prisma/schema.prisma create mode 100644 src/app/(auth)/login/page.tsx create mode 100644 src/app/api/auth/[...nextauth]/route.ts create mode 100644 src/config/auth.ts create mode 100644 src/features/auth/authUtils.ts create mode 100644 src/features/auth/index.ts diff --git a/.env b/.env new file mode 100644 index 0000000..3210ba3 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +DATABASE_URL="mysql://root:root@localhost:3306/dashboard?schema=public" \ No newline at end of file diff --git a/package.json b/package.json index 2939e24..3e7f160 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,10 @@ }, "dependencies": { "@mantine/core": "^7.3.2", + "@mantine/form": "^7.3.2", "@mantine/hooks": "^7.3.2", "next": "14.0.4", + "next-auth": "^4.24.5", "react": "^18", "react-dom": "^18" }, @@ -25,6 +27,7 @@ "postcss": "^8.4.32", "postcss-preset-mantine": "^1.12.1", "postcss-simple-vars": "^7.0.1", + "prisma": "^5.7.1", "tailwindcss": "^3.3.0", "typescript": "^5" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 77c632b..3152d75 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,12 +8,18 @@ dependencies: '@mantine/core': specifier: ^7.3.2 version: 7.3.2(@mantine/hooks@7.3.2)(@types/react@18.2.45)(react-dom@18.2.0)(react@18.2.0) + '@mantine/form': + specifier: ^7.3.2 + version: 7.3.2(react@18.2.0) '@mantine/hooks': specifier: ^7.3.2 version: 7.3.2(react@18.2.0) next: specifier: 14.0.4 version: 14.0.4(react-dom@18.2.0)(react@18.2.0) + next-auth: + specifier: ^4.24.5 + version: 4.24.5(next@14.0.4)(react-dom@18.2.0)(react@18.2.0) react: specifier: ^18 version: 18.2.0 @@ -49,6 +55,9 @@ devDependencies: postcss-simple-vars: specifier: ^7.0.1 version: 7.0.1(postcss@8.4.32) + prisma: + specifier: ^5.7.1 + version: 5.7.1 tailwindcss: specifier: ^3.3.0 version: 3.4.0 @@ -234,6 +243,16 @@ packages: - '@types/react' dev: false + /@mantine/form@7.3.2(react@18.2.0): + resolution: {integrity: sha512-/qa1KQKVC46XWgIU190r3XM3Xld8Lsvz4L/an//TO67RnAGEdC5OCvr2JCb+fprZZi3YdxaKOkVNvP20W23qkg==} + peerDependencies: + react: ^18.2.0 + dependencies: + fast-deep-equal: 3.1.3 + klona: 2.0.6 + react: 18.2.0 + dev: false + /@mantine/hooks@7.3.2(react@18.2.0): resolution: {integrity: sha512-xgumuuI3PBWXff5N02HCI7PEy25mDEdyXDQklUYK93J6FKwpcosyZnGVitoUrV1gLtYYa9ZudeAWdhHuh/CpOg==} peerDependencies: @@ -354,6 +373,10 @@ packages: fastq: 1.16.0 dev: true + /@panva/hkdf@1.1.1: + resolution: {integrity: sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==} + dev: false + /@pkgjs/parseargs@0.11.0: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -361,6 +384,38 @@ packages: dev: true optional: true + /@prisma/debug@5.7.1: + resolution: {integrity: sha512-yrVSO/YZOxdeIxcBtZ5BaNqUfPrZkNsAKQIQg36cJKMxj/VYK3Vk5jMKkI+gQLl0KReo1YvX8GWKfV788SELjw==} + dev: true + + /@prisma/engines-version@5.7.1-1.0ca5ccbcfa6bdc81c003cf549abe4269f59c41e5: + resolution: {integrity: sha512-dIR5IQK/ZxEoWRBDOHF87r1Jy+m2ih3Joi4vzJRP+FOj5yxCwS2pS5SBR3TWoVnEK1zxtLI/3N7BjHyGF84fgw==} + dev: true + + /@prisma/engines@5.7.1: + resolution: {integrity: sha512-R+Pqbra8tpLP2cvyiUpx+SIKglav3nTCpA+rn6826CThviQ8yvbNG0s8jNpo51vS9FuZO3pOkARqG062vKX7uA==} + requiresBuild: true + dependencies: + '@prisma/debug': 5.7.1 + '@prisma/engines-version': 5.7.1-1.0ca5ccbcfa6bdc81c003cf549abe4269f59c41e5 + '@prisma/fetch-engine': 5.7.1 + '@prisma/get-platform': 5.7.1 + dev: true + + /@prisma/fetch-engine@5.7.1: + resolution: {integrity: sha512-9ELauIEBkIaEUpMIYPRlh5QELfoC6pyHolHVQgbNxglaINikZ9w9X7r1TIePAcm05pCNp2XPY1ObQIJW5nYfBQ==} + dependencies: + '@prisma/debug': 5.7.1 + '@prisma/engines-version': 5.7.1-1.0ca5ccbcfa6bdc81c003cf549abe4269f59c41e5 + '@prisma/get-platform': 5.7.1 + dev: true + + /@prisma/get-platform@5.7.1: + resolution: {integrity: sha512-eDlswr3a1m5z9D/55Iyt/nZqS5UpD+DZ9MooBB3hvrcPhDQrcf9m4Tl7buy4mvAtrubQ626ECtb8c6L/f7rGSQ==} + dependencies: + '@prisma/debug': 5.7.1 + dev: true + /@rushstack/eslint-patch@1.6.1: resolution: {integrity: sha512-UY+FGM/2jjMkzQLn8pxcHGMaVLh9aEitG3zY2CiY7XHdLiz3bZOwa6oDxNqEMv7zZkV+cj5DOdz0cQ1BP5Hjgw==} dev: true @@ -784,6 +839,11 @@ packages: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true + /cookie@0.5.0: + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + engines: {node: '>= 0.6'} + dev: false + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -1288,7 +1348,6 @@ packages: /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - dev: true /fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} @@ -1798,6 +1857,10 @@ packages: hasBin: true dev: true + /jose@4.15.4: + resolution: {integrity: sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ==} + dev: false + /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -1843,6 +1906,11 @@ packages: json-buffer: 3.0.1 dev: true + /klona@2.0.6: + resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} + engines: {node: '>= 8'} + dev: false + /language-subtag-registry@0.3.22: resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==} dev: true @@ -1903,7 +1971,6 @@ packages: engines: {node: '>=10'} dependencies: yallist: 4.0.0 - dev: true /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} @@ -1965,6 +2032,31 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true + /next-auth@4.24.5(next@14.0.4)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-3RafV3XbfIKk6rF6GlLE4/KxjTcuMCifqrmD+98ejFq73SRoj2rmzoca8u764977lH/Q7jo6Xu6yM+Re1Mz/Og==} + peerDependencies: + next: ^12.2.5 || ^13 || ^14 + nodemailer: ^6.6.5 + react: ^17.0.2 || ^18 + react-dom: ^17.0.2 || ^18 + peerDependenciesMeta: + nodemailer: + optional: true + dependencies: + '@babel/runtime': 7.23.6 + '@panva/hkdf': 1.1.1 + cookie: 0.5.0 + jose: 4.15.4 + next: 14.0.4(react-dom@18.2.0)(react@18.2.0) + oauth: 0.9.15 + openid-client: 5.6.2 + preact: 10.19.3 + preact-render-to-string: 5.2.6(preact@10.19.3) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + uuid: 8.3.2 + dev: false + /next@14.0.4(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-qbwypnM7327SadwFtxXnQdGiKpkuhaRLE2uq62/nRul9cj9KhQ5LhHmlziTNqUidZotw/Q1I9OjirBROdUJNgA==} engines: {node: '>=18.17.0'} @@ -2019,10 +2111,19 @@ packages: engines: {node: '>=0.10.0'} dev: true + /oauth@0.9.15: + resolution: {integrity: sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==} + dev: false + /object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + /object-hash@2.2.0: + resolution: {integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==} + engines: {node: '>= 6'} + dev: false + /object-hash@3.0.0: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} @@ -2090,12 +2191,26 @@ packages: es-abstract: 1.22.3 dev: true + /oidc-token-hash@5.0.3: + resolution: {integrity: sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==} + engines: {node: ^10.13.0 || >=12.0.0} + dev: false + /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 dev: true + /openid-client@5.6.2: + resolution: {integrity: sha512-TIVimoK/fAvpiISLcoGZyNJx2TOfd5AE6TXn58FFj6Y8qbU/jqky54Aws7sYKuCph1bLPWSRUa1r/Rd6K21bhg==} + dependencies: + jose: 4.15.4 + lru-cache: 6.0.0 + object-hash: 2.2.0 + oidc-token-hash: 5.0.3 + dev: false + /optionator@0.9.3: resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} engines: {node: '>= 0.8.0'} @@ -2290,11 +2405,37 @@ packages: source-map-js: 1.0.2 dev: true + /preact-render-to-string@5.2.6(preact@10.19.3): + resolution: {integrity: sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==} + peerDependencies: + preact: '>=10' + dependencies: + preact: 10.19.3 + pretty-format: 3.8.0 + dev: false + + /preact@10.19.3: + resolution: {integrity: sha512-nHHTeFVBTHRGxJXKkKu5hT8C/YWBkPso4/Gad6xuj5dbptt9iF9NZr9pHbPhBrnT2klheu7mHTxTZ/LjwJiEiQ==} + dev: false + /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} dev: true + /pretty-format@3.8.0: + resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} + dev: false + + /prisma@5.7.1: + resolution: {integrity: sha512-ekho7ziH0WEJvC4AxuJz+ewRTMskrebPcrKuBwcNzVDniYxx+dXOGcorNeIb9VEMO5vrKzwNYvhD271Ui2jnNw==} + engines: {node: '>=16.13'} + hasBin: true + requiresBuild: true + dependencies: + '@prisma/engines': 5.7.1 + dev: true + /prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} dependencies: @@ -2969,6 +3110,11 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true + /uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + dev: false + /watchpack@2.4.0: resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} engines: {node: '>=10.13.0'} @@ -3057,7 +3203,6 @@ packages: /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - dev: true /yaml@2.3.4: resolution: {integrity: sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==} diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..d4e6115 --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,18 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "mysql" + url = env("DATABASE_URL") +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? + isActive Boolean @default(true) +} \ No newline at end of file diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx new file mode 100644 index 0000000..786138e --- /dev/null +++ b/src/app/(auth)/login/page.tsx @@ -0,0 +1,67 @@ +"use client"; + +import { + Paper, + PasswordInput, + Stack, + Text, + TextInput, + Group, + Anchor, + Button, +} from "@mantine/core"; +import { useForm } from "@mantine/form"; +import React from "react"; + +export default function LoginPage() { + const form = useForm({ + initialValues: { + email: "", + password: "", + }, + }); + + return ( +
+ + + Welcome + +
+ + + + + + + toggle()} + size="xs" + > + Don't have an account? Register + + + + +
+
+
+ ); +} diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 0000000..c25b7d4 --- /dev/null +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,3 @@ +import authHandler from "@/features/auth" + +export {authHandler as GET, authHandler as POST} diff --git a/src/config/auth.ts b/src/config/auth.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/features/auth/authUtils.ts b/src/features/auth/authUtils.ts new file mode 100644 index 0000000..53abf4b --- /dev/null +++ b/src/features/auth/authUtils.ts @@ -0,0 +1,10 @@ +export async function validateUser(email: string, password: string) { + // Your user validation logic here... + // If valid, return user object; otherwise, return null. + // This is a placeholder function, implement your own validation logic. + if (email === "user@example.com" && password === "password") { + return { id: 1, name: "John Doe", email: "user@example.com" }; + } else { + return null; + } +} diff --git a/src/features/auth/index.ts b/src/features/auth/index.ts new file mode 100644 index 0000000..b96356b --- /dev/null +++ b/src/features/auth/index.ts @@ -0,0 +1,31 @@ +import NextAuth from "next-auth/next"; +import CredentialsProvider from "next-auth/providers/credentials" + +const auth = NextAuth({ + providers:[ + CredentialsProvider({ + name: "Email/Password", + credentials: { + email: { + label: "Email", + type: "text", + }, + password: { + label: "password", + type: "password" + } + }, + authorize: async (credentials, req) => { + + const user = { id: "1", name: "John Doe", email: "john.doe@example.com" }; + + if (user){ + return user; + } + return null; + }, + }) + ] +}) + +export default auth;