11import NextAuth from 'next-auth' ;
22import Credentials from 'next-auth/providers/credentials' ;
33import { z } from 'zod' ;
4+ import { JWT } from 'next-auth/jwt' ;
5+ import { TokenRefreshReqDto , TokenRefreshResDto } from '@/types/domain/auth' ;
6+ import { ApiResponse } from '@/types/common' ;
7+
8+ const TOKEN_REFRESH_BUFFER = 60 * 1000 ;
9+
10+ async function refreshAccessToken ( token : JWT ) : Promise < JWT > {
11+ try {
12+ const response = await fetch (
13+ `${ process . env . BACKEND_API_URL } /auth/token/refresh` ,
14+ {
15+ method : 'POST' ,
16+ headers : { 'Content-Type' : 'application/json' } ,
17+ body : JSON . stringify ( {
18+ refreshToken : token . refreshToken ,
19+ } satisfies TokenRefreshReqDto ) ,
20+ } ,
21+ ) ;
22+
23+ if ( ! response . ok ) {
24+ throw new Error ( 'Failed to refresh access token' ) ;
25+ }
26+
27+ const { data } : ApiResponse < TokenRefreshResDto > = await response . json ( ) ;
28+
29+ return {
30+ ...token ,
31+ accessToken : data . accessToken ,
32+ refreshToken : data . refreshToken ,
33+ accessTokenExpires : Date . now ( ) + 60 * 60 * 1000 ,
34+ } ;
35+ } catch ( error ) {
36+ console . error ( 'Refresh token error:' , error ) ;
37+ return {
38+ ...token ,
39+ error : 'RefreshAccessTokenError' ,
40+ } ;
41+ }
42+ }
443
544export const { handlers, signIn, signOut, auth } = NextAuth ( {
645 providers : [
@@ -41,6 +80,7 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
4180 accessToken : data . accessToken ,
4281 refreshToken : data . refreshToken ,
4382 profileUrl : data . profileUrl || null ,
83+ expiresIn : data . expires_in ,
4484 } ;
4585 } catch ( error ) {
4686 console . error ( 'Social Auth Error:' , error ) ;
@@ -83,6 +123,7 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
83123 accessToken : data . accessToken ,
84124 refreshToken : data . refreshToken ,
85125 profileUrl : data . profileUrl ,
126+ expiresIn : data . expires_in ,
86127 } ;
87128 } catch ( error ) {
88129 console . error ( 'Login Error:' , error ) ;
@@ -95,21 +136,31 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
95136 callbacks : {
96137 async jwt ( { token, user } ) {
97138 if ( user ) {
98- const u = user ;
99- token . accessToken = u . accessToken ;
100- token . refreshToken = u . refreshToken ;
101- token . userId = u . userId ;
102- token . nickName = u . nickName ;
103- token . profileImageUrl = u . profileUrl ;
139+ const expiresIn = user . expiresIn ?? 60 * 60 ;
140+ return {
141+ ...token ,
142+ accessToken : user . accessToken ,
143+ refreshToken : user . refreshToken ,
144+ userId : user . userId ,
145+ nickName : user . nickName ,
146+ profileUrl : user . profileUrl ,
147+ accessTokenExpires : Date . now ( ) + expiresIn * 1000 ,
148+ } ;
149+ }
150+
151+ if ( Date . now ( ) < ( token . accessTokenExpires ?? 0 ) - TOKEN_REFRESH_BUFFER ) {
152+ return token ;
104153 }
105- return token ;
154+
155+ return refreshAccessToken ( token ) ;
106156 } ,
107157 async session ( { session, token } ) {
108158 session . user . userId = token . userId as string ;
109159 session . user . nickName = token . nickName as string ;
110- session . user . profileUrl = token . profileImageUrl ;
111- session . accessToken = token . accessToken ;
112- session . refreshToken = token . refreshToken ;
160+ session . user . profileUrl = token . profileUrl ;
161+ session . accessToken = token . accessToken as string ;
162+ session . refreshToken = token . refreshToken as string ;
163+ session . error = token . error ;
113164 return session ;
114165 } ,
115166 } ,
0 commit comments