diff --git a/package.json b/package.json
index 8718a0d..4340126 100644
--- a/package.json
+++ b/package.json
@@ -9,15 +9,16 @@
"lint": "next lint"
},
"dependencies": {
+ "@auth/prisma-adapter": "^1.0.11",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@fontsource/roboto": "^5.0.8",
"@mui/icons-material": "^5.14.18",
"@mui/lab": "^5.0.0-alpha.155",
"@mui/material": "^5.14.18",
- "bcryptjs": "^2.4.3",
"@mui/x-date-pickers": "^6.18.3",
"@prisma/client": "^5.6.0",
+ "bcryptjs": "^2.4.3",
"dayjs": "^1.11.10",
"fabric": "^5.3.0",
"material-ui-popup-state": "^5.0.10",
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 49157e7..3856349 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -10,12 +10,42 @@ datasource db {
url = env("DATABASE_URL")
}
+model Account {
+ id String @id @map("_id") @db.ObjectId @default(auto())
+ userId String @db.ObjectId
+ providerType String
+ providerId String
+ providerAccountId String
+ refreshToken String?
+ accessToken String?
+ accessTokenExpires DateTime?
+ createdAt DateTime? @default(now())
+ updatedAt DateTime? @updatedAt
+ user User @relation(fields: [userId], references: [id])
+ @@unique([providerId, providerAccountId])
+}
+
+model Session {
+ id String @id @map("_id") @db.ObjectId @default(auto())
+ userId String @db.ObjectId
+ expires DateTime
+ sessionToken String @unique
+ accessToken String @unique
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ user User @relation(fields: [userId], references: [id])
+}
+
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
email String @unique
password String
nickname String
todos Todo[]
+ createdAt DateTime? @default(now())
+ updatedAt DateTime? @updatedAt
+ accounts Account[]
+ sessions Session[]
}
model Todo {
diff --git a/src/AuthSession.jsx b/src/AuthSession.jsx
new file mode 100644
index 0000000..0b24df4
--- /dev/null
+++ b/src/AuthSession.jsx
@@ -0,0 +1,10 @@
+/* eslint-disable react/prop-types */
+
+'use client';
+
+import React from 'react';
+import { SessionProvider } from 'next-auth/react';
+
+export default function AuthSession({ children }) {
+ return {children};
+}
diff --git a/src/app/api/auth/[...nextauth].js b/src/app/api/auth/[...nextauth].js
deleted file mode 100644
index 244d81e..0000000
--- a/src/app/api/auth/[...nextauth].js
+++ /dev/null
@@ -1,39 +0,0 @@
-import NextAuth from 'next-auth';
-import CredentialsProvider from 'next-auth/providers/credentials';
-
-export default NextAuth({
- providers: [
- CredentialsProvider({
- // The name to display on the sign in form (e.g. "Sign in with...")
- name: 'Credentials',
- // The credentials is used to generate a suitable form on the sign in page.
- // You can specify whatever fields you are expecting to be submitted.
- // e.g. domain, username, password, 2FA token, etc.
- // You can pass any HTML attribute to the tag through the object.
- credentials: {
- username: {
- label: 'Email',
- type: 'email',
- placeholder: 'fitapat@example.com',
- },
- password: { label: 'Password', type: 'password' },
- },
-
- async authorize(credentials, req) {
- // Add logic here to look up the user from the credentials supplied
- const user = { id: 1, name: 'J Smith', email: 'jsmith@example.com' };
-
- if (user) {
- // Any object returned will be saved in `user` property of the JWT
- return user;
- } else {
- // If you return null or false then the credentials will be rejected
- return null;
- // You can also Reject this callback with an Error or with a URL:
- // throw new Error("error message") // Redirect to error page
- // throw "/path/to/redirect" // Redirect to a URL
- }
- },
- }),
- ],
-});
diff --git a/src/app/api/auth/[...nextauth]/route.js b/src/app/api/auth/[...nextauth]/route.js
new file mode 100644
index 0000000..8de1b5d
--- /dev/null
+++ b/src/app/api/auth/[...nextauth]/route.js
@@ -0,0 +1,82 @@
+/* eslint-disable no-console */
+
+import bcrypt from 'bcryptjs';
+import NextAuth from 'next-auth';
+import CredentialsProvider from 'next-auth/providers/credentials';
+import { PrismaAdapter } from '@auth/prisma-adapter';
+import { PrismaClient } from '@prisma/client';
+
+const prisma = new PrismaClient();
+
+const handler = NextAuth({
+ adapter: PrismaAdapter(prisma),
+ providers: [
+ CredentialsProvider({
+ name: 'Credentials',
+ credentials: {
+ email: {
+ label: 'Email',
+ type: 'text',
+ placeholder: '이메일 주소 입력',
+ },
+ password: {
+ label: 'Password',
+ type: 'password',
+ placeholder: '비밀번호 입력',
+ },
+ },
+ // 승인
+ async authorize(credentials) {
+ if (!credentials?.email || !credentials?.password) {
+ return null;
+ }
+ // Prisma를 사용하여 유저 정보를 가져옴
+ const user = await prisma.user.findUnique({
+ where: { email: credentials.email },
+ });
+
+ console.log(user);
+
+ if (!user) {
+ return null;
+ }
+ const isCorrectPassword = await bcrypt.compare(
+ credentials.password,
+ user.password,
+ );
+
+ if (!isCorrectPassword) {
+ return null;
+ }
+ console.log('로그인 완료');
+ return user;
+ },
+ }),
+ ],
+ session: {
+ strategy: 'jwt',
+ },
+ callbacks: {
+ async jwt({ token, user, session }) {
+ if (user) {
+ return {
+ ...token,
+ id: user.id,
+ };
+ }
+ return token;
+ },
+ async session({ session, token, user }) {
+ return {
+ ...session,
+ user: {
+ ...session.user,
+ id: token.id,
+ },
+ };
+ },
+ },
+ secret: process.env.NEXTAUTH_SECRET,
+});
+
+export { handler as GET, handler as POST };
diff --git a/src/app/layout.jsx b/src/app/layout.jsx
index d589001..214f27a 100644
--- a/src/app/layout.jsx
+++ b/src/app/layout.jsx
@@ -5,6 +5,7 @@ import './globals.css';
import { Container } from '@mui/material';
import Header from '../components/header';
import Footer from '../components/footer';
+import AuthSession from '../AuthSession';
const inter = Inter({ subsets: ['latin'] });
@@ -18,8 +19,8 @@ export default function RootLayout({ children }) {
-
- {children}
+
+ {children}
diff --git a/src/app/login/page.jsx b/src/app/login/page.jsx
index feea6bb..16eda9b 100644
--- a/src/app/login/page.jsx
+++ b/src/app/login/page.jsx
@@ -2,6 +2,7 @@
/* eslint-disable no-console, no-alert */
+import { signIn } from 'next-auth/react';
import React, { useState } from 'react';
import {
Box,
@@ -20,14 +21,25 @@ export default function Login() {
const [isChecked, setChecked] = useState(false);
- const handleLoginSubmit = (e) => {
+ const handleLoginSubmit = async (e) => {
e.preventDefault();
- alert(`로그인 완료: ${email}`);
- window.location = '/';
+ console.log(`이메일: ${email}`);
+ const result = await signIn('credentials', {
+ email,
+ password,
+ redirect: false,
+ });
+ if (!result.error) {
+ alert(`로그인 완료: ${email}`);
+ window.location = '/';
+ } else {
+ console.error(result.error);
+ alert('로그인 실패');
+ }
};
const handleChecked = (e) => {
- // console.log(`${e.target.checked}`);
+ console.log(`${e.target.checked}`);
setChecked(e.target.checked);
};
diff --git a/src/app/user/page.jsx b/src/app/user/page.jsx
index e9e4227..7dac3b1 100644
--- a/src/app/user/page.jsx
+++ b/src/app/user/page.jsx
@@ -4,21 +4,45 @@
import React from 'react';
import { Stack, Button, Link } from '@mui/material';
+import { useSession, signOut } from 'next-auth/react';
const handleLogoutSubmit = (e) => {
e.preventDefault();
- window.location = '/login';
+ signOut({ callbackUrl: '/', redirect: false });
alert('로그아웃되었습니다.');
};
export default function User() {
+ const { session, status } = useSession();
+
return (
-
-
-
+ {status === 'authenticated' ? (
+
+
+ {/* 현재 로그인중인 사용자 이메일 표시 */}
+
+ 현재 로그인:
+ {status === 'authenticated' ? session?.user?.email : 'X'}
+
+
+ ) : (
+
+
+ 회원가입
+
+
+ 로그인
+
+
+ )}
+
설정