auth.ts 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. 'use server';
  2. import { cookies } from 'next/headers';
  3. import { fetchJson, getAccessToken, getRefreshToken } from '@/lib/utils/server';
  4. import { MemberResponse } from '@/types/response/account/member';
  5. import { LoginResponse } from '@/types/response/auth';
  6. // RefreshToken으로 AccessToken 갱신
  7. // 백엔드가 Set-Cookie 를 보내지 않으므로 응답 body 의 새 토큰을 직접 쿠키에 set.
  8. // 이게 빠지면 cookie 의 구 refreshToken 이 갱신되지 않아 grace period(30s) 만료 시 재사용 탐지로 강제 로그아웃됨.
  9. async function refreshAccessToken(): Promise<boolean>
  10. {
  11. const refreshToken = await getRefreshToken();
  12. if (!refreshToken) {
  13. return false;
  14. }
  15. const res = await fetchJson<LoginResponse>('/api/auth/refresh-token', {
  16. method: 'POST',
  17. body: JSON.stringify({ RefreshToken: refreshToken })
  18. });
  19. if (!res.success || !res.data) {
  20. return false;
  21. }
  22. const cookieStore = await cookies();
  23. const sevenDaysSec = 7 * 24 * 60 * 60;
  24. const opts = {
  25. httpOnly: true,
  26. path: '/',
  27. sameSite: 'lax' as const,
  28. secure: process.env.NODE_ENV === 'production',
  29. maxAge: sevenDaysSec
  30. };
  31. cookieStore.set('accessToken', res.data.accessToken, opts);
  32. cookieStore.set('refreshToken', res.data.refreshToken, opts);
  33. return true;
  34. }
  35. // 로그인 확인
  36. export async function checkAuthServer(): Promise<boolean>
  37. {
  38. const accessToken = await getAccessToken();
  39. if (accessToken) {
  40. try {
  41. const payload = JSON.parse(
  42. Buffer.from(accessToken.split('.')[1], 'base64').toString()
  43. );
  44. if (payload.exp * 1000 > Date.now()) {
  45. return true;
  46. }
  47. } catch {
  48. }
  49. }
  50. return await refreshAccessToken();
  51. }
  52. // SSR 초기 회원 정보 조회 (헤더 깜빡임 방지용)
  53. export async function getInitialMember(): Promise<MemberResponse|null>
  54. {
  55. const accessToken = await getAccessToken();
  56. if (!accessToken) {
  57. return null;
  58. }
  59. try {
  60. const payload = JSON.parse(
  61. Buffer.from(accessToken.split('.')[1], 'base64').toString()
  62. );
  63. if (payload.exp * 1000 <= Date.now()) {
  64. return null;
  65. }
  66. } catch {
  67. return null;
  68. }
  69. const res = await fetchJson<MemberResponse>('/api/auth/profile', {
  70. method: 'GET'
  71. });
  72. if (res.success && res.data) {
  73. return res.data;
  74. }
  75. return null;
  76. }