diff --git a/next.config.mjs b/next.config.mjs index e18d6f79..117e5c75 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -18,6 +18,11 @@ const nextConfig = { pathname: '/**', search: '', }, + { + protocol: 'http', + hostname: 'k.kakaocdn.net', + pathname: '/**', + }, ], }, }; diff --git a/src/app/(auth)/login/_components/LoginForm/index.tsx b/src/app/(auth)/login/_components/LoginForm/index.tsx index 660e964f..68937f5c 100644 --- a/src/app/(auth)/login/_components/LoginForm/index.tsx +++ b/src/app/(auth)/login/_components/LoginForm/index.tsx @@ -161,8 +161,8 @@ export default function LoginForm() { Cookies.set('userId', user.id.toString(), { path: '/', - secure: true, - sameSite: 'Strict', + secure: true, // HTTPS 쿠키 전송 + sameSite: 'Strict', // 사용자가 직접 사이트를 방문한 경우 쿠키 포함 }); toast.success('로그인 되었습니다.'); diff --git a/src/app/(auth)/login/_components/SocialLogin/index.tsx b/src/app/(auth)/login/_components/SocialLogin/index.tsx index 7ff90f1c..8c9a92d4 100644 --- a/src/app/(auth)/login/_components/SocialLogin/index.tsx +++ b/src/app/(auth)/login/_components/SocialLogin/index.tsx @@ -19,7 +19,14 @@ export default function SocialLogin() { { - 'kakao Login'; + // client_id, redirect_uri, state + const REST_API_KEY = process.env.NEXT_PUBLIC_KAKAO_REST_API_KEY; + const REDIRECT_URI = process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI; + const STATE = crypto.randomUUID(); // CSRF 방지를 위한 난수 문자열 생성 + + const kakaoAuthUrl = `https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&state=${STATE}`; + // redirection to kakaoAuthUrl + window.location.href = kakaoAuthUrl; }} /> diff --git a/src/app/(auth)/oauth/kakao/CookieSetter.tsx b/src/app/(auth)/oauth/kakao/CookieSetter.tsx new file mode 100644 index 00000000..80252d4c --- /dev/null +++ b/src/app/(auth)/oauth/kakao/CookieSetter.tsx @@ -0,0 +1,19 @@ +'use client'; + +import loginWithOauth from '@/app/(auth)/oauth/kakao/loginWIthOauth'; +import { useEffect } from 'react'; + +export interface CookieSetterProps { + code: string; + state: string; +} + +export default function CookieSetter({ code, state }: CookieSetterProps) { + useEffect(() => { + (async () => { + await loginWithOauth({ code, state }); + })(); + }, []); + + return
; +} diff --git a/src/app/(auth)/oauth/kakao/loginWIthOauth.ts b/src/app/(auth)/oauth/kakao/loginWIthOauth.ts new file mode 100644 index 00000000..c3d4153e --- /dev/null +++ b/src/app/(auth)/oauth/kakao/loginWIthOauth.ts @@ -0,0 +1,62 @@ +// server action +'use server'; + +import { CookieSetterProps } from '@/app/(auth)/oauth/kakao/CookieSetter'; +import { cookies } from 'next/headers'; + +export default async function loginWithOauth({ + code, + state, +}: CookieSetterProps) { + const url = `${process.env.NEXT_PUBLIC_API_URL}/auth/signIn/KAKAO`; + + const responseBody = { + state, + redirectUri: process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI as string, + token: code, + }; + + try { + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + credentials: 'include', + body: JSON.stringify(responseBody), + }); + console.log('response 응답', response); + + if (!response.ok) { + throw new Error('간편 로그인 실패'); + } + + const data = await response.json(); + const { accessToken, refreshToken, user } = data; + + // 쿠키에 토큰과 userId 설정 + const cookieStore = cookies(); + + cookieStore.set('accessToken', accessToken, { + path: '/', + secure: false, + //httpOnly: true, + sameSite: 'strict', + }); + cookieStore.set('refreshToken', refreshToken, { + path: '/', + secure: false, + // httpOnly: true, + sameSite: 'strict', + }); + cookieStore.set('userId', user.id.toString(), { + path: '/', + secure: false, + // httpOnly: true, + sameSite: 'strict', + }); + console.log('로그인 성공:', data); + } catch (error) { + console.error('카카오 로그인 실패', error); + } +} diff --git a/src/app/(auth)/oauth/kakao/page.tsx b/src/app/(auth)/oauth/kakao/page.tsx new file mode 100644 index 00000000..c607d198 --- /dev/null +++ b/src/app/(auth)/oauth/kakao/page.tsx @@ -0,0 +1,10 @@ +import CookieSetter from '@/app/(auth)/oauth/kakao/CookieSetter'; + +export default async function KakaoLoginPage({ + searchParams, +}: { + searchParams: Promise<{ [key: string]: string | string[] | undefined }>; +}) { + const { code, state } = await searchParams; + return ; +} diff --git a/src/app/(auth)/oauth/login/[kakao]/page.tsx b/src/app/(auth)/oauth/login/[kakao]/page.tsx deleted file mode 100644 index 19a90020..00000000 --- a/src/app/(auth)/oauth/login/[kakao]/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function LoginPage() { - return
LoginPage
; -} diff --git a/src/app/(auth)/oauth/signup/[kakao]/page.tsx b/src/app/(auth)/oauth/signup/[kakao]/page.tsx deleted file mode 100644 index 3f595079..00000000 --- a/src/app/(auth)/oauth/signup/[kakao]/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function SignupPage() { - return
SignupPage
; -} diff --git a/src/lib/apis/auth/index.ts b/src/lib/apis/auth/index.ts index 2db3ec3c..75c9bfa7 100644 --- a/src/lib/apis/auth/index.ts +++ b/src/lib/apis/auth/index.ts @@ -6,6 +6,7 @@ import { AuthBody, AuthResponse, } from '@/lib/apis/auth/type'; +import serverFetcher from '@/lib/server/fetcher.server'; // 회원가입 export async function signUp({ @@ -39,7 +40,7 @@ export async function postOAuth({ }: { body: OAuthBody; }): Promise { - return clientFetcher({ + return serverFetcher({ url: '/auth/signIn/KAKAO', method: 'POST', body,