Skip to content

Commit 116c3dd

Browse files
authored
[#129] Kakao Login (#186)
* πŸ›Fix: authorize ν•¨μˆ˜ μˆ˜μ • 및 μ—λŸ¬ 처리 κ°œμ„  - API 응닡값 λ³€κ²½ 사항 반영 - μ€‘λ³΅λœ `id`, `name`, `email` 제거 - API 응닡값(`id`, `username`, `email`) ν™œμš©ν•˜μ—¬ 데이터 리턴 - ν¬λ¦¬λ΄μ…œ 미제곡 μ‹œ `null` λ°˜ν™˜ν•˜λ„λ‘ μˆ˜μ • - `try-catch` 블둝 μΆ”κ°€λ‘œ μ—λŸ¬ 둜그 좜λ ₯ 및 ν•œ μ€„λ‘œ 처리 κ°„μ†Œν™” * ✨Feat: Kakao 둜그인 κΈ°λŠ₯ μΆ”κ°€ 및 인증 둜직 κ°œμ„  - 카카였 둜그인 API ν•¨μˆ˜ 및 νƒ€μž… μ •μ˜ - 카카였 둜그인 λ²„νŠΌμ— signIn('kakao') ν•Έλ“€λŸ¬ μΆ”κ°€ - KakaoProvider μΆ”κ°€λ‘œ 카카였 둜그인 지원 - `clientId`와 `clientSecret` ν™˜κ²½ λ³€μˆ˜ 적용 - 카카였 둜그인 μ‹œ μ„œλ²„ 검증 둜직 μΆ”κ°€ (`postKaKaoLogin`) - 성곡 μ‹œ μ‚¬μš©μž 정보 λ§€ν•‘ - next-auth νƒ€μž… μ •μ˜ μˆ˜μ • 및 ν™•μž₯ - `Session` νƒ€μž…μ—μ„œ `DefaultSession['user']`에 이미 ν¬ν•¨λœ `name`, `email` 제거 - `User` νƒ€μž…μ„ `DefaultUser`둜 ν™•μž₯ν•˜μ—¬ κΈ°μ‘΄ μ€‘λ³΅λœ ν•„λ“œ(`id`, `name`, `email`) 제거 - `User` νƒ€μž…μ— `expiredAt` ν•„λ“œ μΆ”κ°€
1 parent 163b472 commit 116c3dd

6 files changed

Lines changed: 107 additions & 44 deletions

File tree

β€Žsrc/app/(unauthorized)/signin/page.tsxβ€Ž

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
// import Image from 'next/image';
3+
import Image from 'next/image';
44
import Link from 'next/link';
55
import { SubmitHandler, useForm } from 'react-hook-form';
66
import { AUTH_VALIDATION_REGEX } from '@constant/auth';
@@ -9,6 +9,7 @@ import Button from '@ui/common/Button';
99
import { INPUT_MESSAGE } from '@constant/input';
1010
import { useLogin } from '@hooks/auth/useLogin';
1111
import { useRouter } from 'next/navigation';
12+
import { signIn } from 'next-auth/react';
1213

1314
type TLoginFormInputs = {
1415
email: string;
@@ -98,11 +99,11 @@ export default function SignInPage({
9899
>
99100
λ‘œκ·ΈμΈν•˜κΈ°
100101
</Button>
101-
{/* TODO: 2μ°¨ κ°œλ°œμ— μΆ”κ°€ */}
102-
{/* <Button
102+
<Button
103103
size="large"
104104
fullWidth={true}
105105
className="border-[#FEE500] bg-[#FEE500] text-slate-800 hover:border-[#FEE500] hover:bg-[#FEE500] active:border-[#FEE500] active:bg-[#FEE500]"
106+
onClick={() => signIn('kakao')}
106107
>
107108
<Image
108109
src="/asset/image/kakao.png"
@@ -111,7 +112,7 @@ export default function SignInPage({
111112
alt="카카였 μ•„μ΄μ½˜"
112113
/>
113114
μΉ΄μΉ΄μ˜€ν†‘μœΌλ‘œ λ‘œκ·ΈμΈν•˜κΈ°
114-
</Button> */}
115+
</Button>
115116
</div>
116117
<div className="flex h-5 gap-1">
117118
<div className="text-sm font-medium leading-tight">

β€Žsrc/lib/api/service/auth.api.tsβ€Ž

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
TGetEmailExistsRequest,
33
TGetEmailExistsResponse,
4+
TPostKaKaoLoginRequest,
45
TPostLoginRequest,
56
TPostRefreshTokenRequest,
67
TPostRegisterRequest,
@@ -50,3 +51,11 @@ export const getEmailExists = async (data: TGetEmailExistsRequest) => {
5051
console.error(err);
5152
}
5253
};
54+
55+
export const postKaKaoLogin = async (data: TPostKaKaoLoginRequest) => {
56+
const response = await post<TResponse<TTokenResponse | null>>(
57+
'/api/v1/torip/auth/kakao/token',
58+
data,
59+
);
60+
return response.data;
61+
};

β€Žsrc/lib/authOptions.tsβ€Ž

Lines changed: 82 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import CredentialsProvider from 'next-auth/providers/credentials';
2+
import KakaoProvider from 'next-auth/providers/kakao';
23
import { NextAuthOptions, Session, User } from 'next-auth';
34
import { JWT } from 'next-auth/jwt';
45
import dayjs from 'dayjs';
56
import {
7+
postKaKaoLogin,
68
postLogin,
79
postRefreshToken,
810
postRegister,
@@ -37,60 +39,108 @@ export const authOptions: NextAuthOptions = {
3739
placeholder: 'λΉ„λ°€λ²ˆν˜Έλ₯Ό μž…λ ₯ν•΄μ£Όμ„Έμš”.',
3840
},
3941
},
40-
async authorize(
41-
credentials: Record<'name' | 'email' | 'password', string> | undefined,
42-
) {
43-
// κΈ°μ‘΄ authorize ν•¨μˆ˜ λ‚΄μš© μœ μ§€
42+
async authorize(credentials) {
4443
if (!credentials) {
45-
throw new Error('No credentials provided.');
44+
return null;
4645
}
46+
4747
const { name, email, password } = credentials;
4848

49-
// νšŒμ›κ°€μž…
50-
if (name) {
49+
try {
50+
// νšŒμ›κ°€μž…
51+
if (name) {
52+
const {
53+
result: token,
54+
success,
55+
message,
56+
} = await postRegister({
57+
username: name,
58+
email: email,
59+
password: password,
60+
});
61+
62+
if (success && token) {
63+
return {
64+
id: token.id.toString(),
65+
name: token.username,
66+
email: token.email,
67+
accessToken: token.accessToken,
68+
refreshToken: token.refreshToken,
69+
expiredAt: token.expiredAt,
70+
};
71+
} else {
72+
throw new Error(message || 'Signup failed.');
73+
}
74+
}
75+
76+
// 둜그인
5177
const {
5278
result: token,
5379
success,
5480
message,
55-
} = await postRegister({
56-
username: name,
81+
} = await postLogin({
5782
email: email,
5883
password: password,
5984
});
6085

61-
if (success) {
62-
return { id: '1', name, email, ...token };
86+
if (success && token) {
87+
return {
88+
id: token.id.toString(),
89+
name: token.username,
90+
email: token.email,
91+
accessToken: token.accessToken,
92+
refreshToken: token.refreshToken,
93+
expiredAt: token.expiredAt,
94+
};
6395
} else {
64-
const error = new Error('Signup failed.');
65-
(error as Error).message = message;
66-
throw error;
96+
throw new Error(message || 'Login failed.');
6797
}
68-
}
69-
70-
// 둜그인
71-
const {
72-
result: token,
73-
success,
74-
message,
75-
} = await postLogin({
76-
email: email,
77-
password: password,
78-
});
79-
80-
if (success) {
81-
return { id: '1', name, email, ...token };
82-
} else {
83-
const error = new Error('Login failed.');
84-
(error as Error).message = message;
85-
throw error;
98+
} catch (error) {
99+
// eslint-disable-next-line no-console
100+
console.error('Authentication error:', error);
101+
return null;
86102
}
87103
},
88104
}),
105+
KakaoProvider({
106+
clientId: process.env.KAKAO_CLIENT_ID!,
107+
clientSecret: process.env.KAKAO_CLIENT_SECRET!,
108+
}),
89109
],
90110
pages: {
91111
signIn: '/signin',
92112
},
93113
callbacks: {
114+
async signIn({ user, account }) {
115+
// 카카였 둜그인 μ„œλ²„ 검증
116+
if (account?.provider === 'kakao' && account.access_token) {
117+
try {
118+
const {
119+
result: token,
120+
success,
121+
message,
122+
} = await postKaKaoLogin({
123+
accessToken: account.access_token,
124+
});
125+
if (success && token) {
126+
user.id = token.id.toString();
127+
user.name = token.username;
128+
user.email = token.email;
129+
user.accessToken = token.accessToken;
130+
user.refreshToken = token.refreshToken;
131+
user.expiredAt = token.expiredAt;
132+
return true;
133+
} else {
134+
throw new Error(message || 'KAKAO Login failed.');
135+
}
136+
} catch (error) {
137+
// eslint-disable-next-line no-console
138+
console.error('Error during Kakao login:', error);
139+
return false;
140+
}
141+
}
142+
return true;
143+
},
94144
async jwt({ token, user }: { token: JWT; user?: IMyUser }) {
95145
if (user) {
96146
token.id = Number(user.id);

β€Žsrc/lib/next-auth.d.tsβ€Ž

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,21 @@
11
// types/next-auth.d.ts
22

3-
import { DefaultSession } from 'next-auth';
3+
import { DefaultSession, DefaultUser } from 'next-auth';
44

55
declare module 'next-auth' {
66
// eslint-disable-next-line @typescript-eslint/naming-convention
77
interface Session {
88
user: {
99
id: number;
10-
name: string | null;
11-
email: string | null;
1210
} & DefaultSession['user'];
1311
accessToken?: string;
1412
refreshToken?: string;
1513
expiredAt?: string;
1614
}
17-
18-
interface IUser {
19-
id: number;
20-
name: string;
21-
email: string;
15+
// eslint-disable-next-line @typescript-eslint/naming-convention
16+
interface User extends DefaultUser {
2217
accessToken?: string;
2318
refreshToken?: string;
19+
expiredAt?: string;
2420
}
2521
}

β€Žsrc/model/auth.model.tsβ€Ž

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,7 @@ export type TGetEmailExistsRequest = string;
1313
export type TGetEmailExistsResponse = {
1414
exists: boolean;
1515
};
16+
17+
export type TPostKaKaoLoginRequest = {
18+
accessToken: string;
19+
};

β€Žsrc/model/model.tsβ€Ž

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ export type TResponse<T> = {
66
};
77

88
export type TTokenResponse = {
9+
id: number;
10+
username: string;
11+
email: string;
912
accessToken: string;
1013
refreshToken: string;
1114
expiredAt: string;

0 commit comments

Comments
Β (0)