Skip to content

Commit 7159460

Browse files
authored
Merge pull request #52 from IT-Cotato/refactor/refresh-token
feat: accesstoken 자동 갱신 로직 구현
2 parents 91ad709 + 629cda2 commit 7159460

2 files changed

Lines changed: 68 additions & 13 deletions

File tree

auth.ts

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,45 @@
11
import NextAuth from 'next-auth';
22
import Credentials from 'next-auth/providers/credentials';
33
import { 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

544
export 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
},

next-auth.d.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,29 @@ declare module 'next-auth' {
1010
profileUrl: string | null;
1111
accessToken: string;
1212
refreshToken: string;
13+
expiresIn?: number;
1314
}
1415

1516
interface Session extends DefaultSession {
1617
user: {
1718
userId: string;
1819
nickName: string;
19-
profileImageUrl: string | null;
20+
profileUrl: string | null;
2021
} & DefaultSession['user'];
2122
accessToken: string;
2223
refreshToken: string;
24+
error?: 'RefreshAccessTokenError';
2325
}
2426
}
2527

2628
declare module 'next-auth/jwt' {
2729
interface JWT extends DefaultJWT {
2830
userId: string;
29-
nickname: string;
30-
profileImageUrl: string | null;
31+
nickName: string;
32+
profileUrl: string | null;
3133
accessToken: string;
3234
refreshToken: string;
35+
accessTokenExpires: number;
36+
error?: 'RefreshAccessTokenError';
3337
}
3438
}

0 commit comments

Comments
 (0)