Skip to content

Commit

Permalink
Merge pull request #74 from turbinecrew/feat/auth-api
Browse files Browse the repository at this point in the history
feat :  auth api - login기능 추가
  • Loading branch information
keeprok authored Jan 21, 2025
2 parents b00f9f2 + 44bd35c commit 9b71fe5
Show file tree
Hide file tree
Showing 21 changed files with 1,626 additions and 749 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,21 @@

---

### 🗓️ **Total Duration**

- **📅 프로젝트 수정 및 재설계 기간**: **2024.01.20 ~ 2024. **

### 📌 **Weekly Breakdown**

| **Week** | **Period** | **Todo Title** | **Check** |
| ----------- | ---------- | -------------- | --------- |
| **Week-01** | 01.20 - | |
| | |
| **Week-02** | | | |
| | | |

---

## 🛠️ **Tech Stacks**

### **Languages**
Expand Down
1,323 changes: 631 additions & 692 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
"animate": "^1.0.0",
"autoprefixer": "^10.4.20",
"axios": "^1.7.9",
"bcrypt": "^5.1.0",
"class-variance-authority": "^0.7.1",
"dlv": "^1.1.3",
"glob": "^11.0.0",
"jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21",
"lru-cache": "^11.0.2",
"lucide-react": "^0.468.0",
Expand All @@ -45,6 +47,8 @@
"@storybook/nextjs": "^8.6.0-alpha.0",
"@storybook/react": "^8.6.0-alpha.0",
"@storybook/test": "^8.6.0-alpha.0",
"@types/bcrypt": "^5.0.2",
"@types/jsonwebtoken": "^9.0.7",
"@types/mongodb": "^4.0.7",
"@types/node": "^20",
"@types/react": "^18",
Expand Down
77 changes: 77 additions & 0 deletions src/app/api/Auth/admin/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import jwt from "jsonwebtoken"
import { NextResponse } from "next/server"

// JWT_SECRET 설정 (환경 변수로 관리하는 것이 좋습니다)
const JWT_SECRET = "pixelcrew12345" // 실제로는 환경변수로 관리하는 것이 좋습니다

// JWT에서 반환되는 객체 타입 정의
interface CustomJwtPayload {
email: string
role: string
}

export async function GET(request: Request) {
try {
// 쿠키에서 JWT 가져오기
const cookieHeader = request.headers.get("cookie")
if (!cookieHeader) {
return NextResponse.json(
{ authenticated: false, message: "쿠키가 제공되지 않았습니다." },
{ status: 401 },
)
}

const cookies = Object.fromEntries(
cookieHeader.split("; ").map((c) => c.split("=")),
)
const token = cookies["accessToken"]
if (!token) {
return NextResponse.json(
{ authenticated: false, message: "토큰이 없습니다." },
{ status: 401 },
)
}

// JWT 검증
try {
const decoded = jwt.verify(token, JWT_SECRET) as CustomJwtPayload

// 이메일 확인
if (!decoded.email) {
return NextResponse.json(
{ authenticated: false, message: "유효하지 않은 이메일 정보입니다." },
{ status: 403 },
)
}

// 관리자인지 확인
if (decoded.role !== "admin") {
return NextResponse.json(
{ authenticated: false, message: "관리자 권한이 필요합니다." },
{ status: 403 },
)
}

// 관리자인 경우 대시보드 정보 제공
return NextResponse.json(
{
authenticated: true,
email: decoded.email,
message: "관리자 대시보드에 오신 것을 환영합니다.",
},
{ status: 200 },
)
} catch (error) {
return NextResponse.json(
{ authenticated: false, message: "유효하지 않은 토큰입니다." },
{ status: 401 },
)
}
} catch (error) {
console.error("Error during auth status check:", error)
return NextResponse.json(
{ authenticated: false, message: "서버 오류가 발생했습니다." },
{ status: 500 },
)
}
}
69 changes: 69 additions & 0 deletions src/app/api/Auth/getUsers/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import jwt from "jsonwebtoken"
import { MongoClient } from "mongodb"
import { NextResponse } from "next/server"
// 환경 변수 설정
const JWT_SECRET = process.env.JWT_SECRET // 환경 변수에서 JWT_SECRET 가져오기
const MONGODB_URI = process.env.MONGODB_URI // 환경 변수에서 MONGODB_URI 가져오기

// MongoDB 클라이언트 초기화
const client = new MongoClient(MONGODB_URI!)

// CustomJwtPayload 타입 정의
interface CustomJwtPayload {
email: string
id: string
}

export async function GET(request: Request) {
try {
// 쿠키에서 JWT 가져오기
const cookieHeader = request.headers.get("cookie")
if (!cookieHeader) {
return NextResponse.json(
{ authenticated: false, message: "쿠키가 제공되지 않았습니다." },
{ status: 401 },
)
}

const cookies = Object.fromEntries(
cookieHeader.split("; ").map((c) => c.split("=")),
)
const token = cookies["accessToken"]
if (!token) {
return NextResponse.json(
{ authenticated: false, message: "토큰이 없습니다." },
{ status: 401 },
)
}

// JWT 검증 및 디코딩
const decoded = jwt.verify(token, JWT_SECRET!) as CustomJwtPayload

// MongoDB에서 로그인된 사용자 정보 가져오기
await client.connect()
const db = client.db("PixcelCrew")
const collection = db.collection("User")

// 로그인된 사용자 정보 가져오기
const user = await collection.findOne({ email: decoded.email })

if (!user) {
return NextResponse.json(
{ authenticated: false, message: "사용자를 찾을 수 없습니다." },
{ status: 404 },
)
}

// 로그인된 사용자 정보 반환
return NextResponse.json(
{ authenticated: true, message: "사용자 정보", user: user },
{ status: 200 },
)
} catch (error) {
console.error("Error during auth status check:", error)
return NextResponse.json(
{ authenticated: false, message: "유효하지 않은 토큰입니다." },
{ status: 401 },
)
}
}
121 changes: 121 additions & 0 deletions src/app/api/Auth/login/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import bcrypt from "bcrypt"
import jwt from "jsonwebtoken"
import { MongoClient } from "mongodb"
import { NextResponse } from "next/server"

const uri = process.env.MONGODB_URI
const JWT_SECRET = process.env.JWT_SECRET

if (!uri || !JWT_SECRET) {
throw new Error(
"환경 변수가 설정되지 않았습니다. MONGODB_URI와 JWT_SECRET를 확인하세요.",
)
}

let client: MongoClient

if (!global._mongoClientPromise) {
client = new MongoClient(uri)
global._mongoClientPromise = client.connect()
}
const clientPromise: Promise<MongoClient> = global._mongoClientPromise

const generateAccessToken = (user: any): string => {
return jwt.sign(
{ email: user.email, role: user.role || "member", id: user._id },
JWT_SECRET,
{ expiresIn: "15m" },
)
}

const generateRefreshToken = (user: any): string => {
return jwt.sign(
{ email: user.email, role: user.role || "member", id: user._id },
JWT_SECRET,
{ expiresIn: "7d" },
)
}

export async function POST(request: Request) {
try {
const requestData = await request.json()
console.log("서버로 수신된 데이터:", requestData)

const { email, password }: { email: string; password: string } = requestData

if (
!email ||
!password ||
typeof email !== "string" ||
typeof password !== "string"
) {
console.error("유효하지 않은 데이터 형식:", requestData)
return NextResponse.json(
{ message: "유효한 이메일과 비밀번호를 입력해주세요." },
{ status: 400 },
)
}

const client = await clientPromise
const db = client.db("PixcelCrew")
const collection = db.collection("User")

const user = await collection.findOne({ email })
if (!user) {
console.error("사용자가 존재하지 않습니다:", email)
return NextResponse.json(
{ message: "사용자가 존재하지 않습니다." },
{ status: 404 },
)
}

if (!user.password || typeof user.password !== "string") {
console.error("DB에 저장된 비밀번호가 유효하지 않습니다:", user.password)
return NextResponse.json(
{ message: "서버에 저장된 사용자 정보가 손상되었습니다." },
{ status: 500 },
)
}

const isPasswordValid = await bcrypt.compare(password, user.password)
if (!isPasswordValid) {
console.error("비밀번호가 일치하지 않습니다:", email)
return NextResponse.json(
{ message: "비밀번호가 올바르지 않습니다." },
{ status: 401 },
)
}

const accessToken = generateAccessToken(user)
const refreshToken = generateRefreshToken(user)

await collection.updateOne(
{ _id: user._id },
{ $set: { refreshToken, updatedAt: new Date() } },
)

const response = NextResponse.json({ message: "로그인 성공" })
response.cookies.set("accessToken", accessToken, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
path: "/",
maxAge: 15 * 60,
sameSite: "strict",
})
response.cookies.set("refreshToken", refreshToken, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
path: "/",
maxAge: 7 * 24 * 60 * 60,
sameSite: "strict",
})

return response
} catch (error: any) {
console.error("로그인 중 오류 발생:", error.message || error)
return NextResponse.json(
{ message: "서버 오류가 발생했습니다." },
{ status: 500 },
)
}
}
Loading

0 comments on commit 9b71fe5

Please sign in to comment.