작성
·
136
0
안녕하세요 next-auth로 토큰 갱신 로직을 개발하는 중에 해결되지 않는 문제가 발생하여 질문 드립니다.
현재 클라이언트에서 api 요청을 하면 middleware에서 const token = await auth(); 이런식으로 세션 정보를 가져와 accessToken을 헤더에 삽입하는 방식을 구현하였습니다.
if (request.nextUrl.pathname.startsWith("/gateway")) {
const session = await auth();
const accessToken = session?.accessToken;
const { device } = userAgent(request);
const localeFromCookie = cookieStore.get("dp_lang")?.value as string;
const defaultLocale = getLanguageCodeFromLocale(localeFromCookie);
request.headers.set("Accept", "*/*");
request.headers.set("Authorization", `Bearer ${accessToken}`);
request.headers.set("Access-Control-Allow-Origin", "*");
request.headers.set("deviceType", "1");
request.headers.set("deviceId", getDeviceIdFromCookie(cookieStore));
request.headers.set("osType", getOsTypeFromCookie(cookieStore));
request.headers.set("User-Agent", device.model ?? "");
request.headers.set("locale", localeFromCookie);
request.headers.set("language", defaultLocale);
const response = NextResponse.next({
request: {
headers: request.headers,
},
});
return response;
}
따라서 미들웨어에서 세션에 접근하는 순간에 jwt콜백이 실행되어 jwt 토큰에 저장되어 있는 토큰 expire 시간을 비교하여 만료여부를 판단 한 뒤에 토큰 갱신이 되도록 구현을 하였습니다.
async jwt({ token, account, user }) {
// Initial sign in
if (account && user) {
return {
accessToken: user.accessToken,
expiresAt: new Date(
Date.now() + (user?.expiresIn ?? 0)
).toISOString(),
refreshToken: user.refreshToken,
serviceAvailable: true,
};
}
// Return previous token if the access token has not expired yet
if (
new Date() <
new Date(new Date(token.expiresAt as string).getTime() - 5000)
) {
console.log("@@@@@@valid");
return token;
} else {
if (token.error === "RefreshTokenInvalid") {
return token;
}
// Access token has expired, try tㄴo update it
try {
console.log("@@@@@@expired");
const refreshedTokens = await authApi.refreshAuthToken({
refreshToken: token.refreshToken as string,
accessToken: token.accessToken as string,
});
console.log("==========token refreshed========");
// reissue token
return {
...token,
accessToken: refreshedTokens.accessToken,
expiresAt: new Date(
Date.now() + (refreshedTokens.expiresIn ?? 18000)
).toISOString(),
refreshToken: refreshedTokens.refreshToken ?? token.refreshToken,
error: null,
};
} catch (error) {
console.error("Token refresh failed", error);
if (error.status === RestApiErrorType.invalidTokenException) {
return {
...token,
error: "RefreshTokenInvalid",
};
}
return { ...token, error: "RefreshAccessTokenError" };
}
}
},
기존에는 세션에 접근을 할때 next-auth의 jwt 콜백이 돌고 값이 리턴되면 쿠키의 session-token값에 암호화된 값들이 업데이트 되는 걸로 알고 있었는데 찾아보니 현재 세션에 접근하고 갱신하는 부분이 서버단에 있어서 쿠키가 업데이트 되지 않는다고 합니다.
따라서 현재 토큰이 만료된 후 한번에 3가지의 api 콜을 요청했을때 첫번째 api요청에서는 정상적으로 토큰 refresh가 요청이 되며 middleware에서 세션에 접근해 갱신된 토큰이 가져와져 요청이 성공되나 그 뒤 요청부터는 쿠키에 업데이트가 되지 않아서인지 세션에서 갱신 전 토큰이 가져와지는 현상이 발생하고 있습니다.
클라이언트에서 세션 업데이트 후 세션에 접근을 해야 쿠키값이 갱신이 된다고 하는데 다른 방법이 있는지 문의드립니다.
답변 2
0
한 번에 세 가지 api를 요청하시는데 저 셋이 연관이 있는 경우에는 하나의 api로 만드시는 게 어떤가 싶습니다. 백엔드를 수정하기보다는 next의 routes handlers 활용하시면 좋습니다.
0
안녕하세요, 인프런 AI 인턴입니다. 박지수 Jisu Park님.
next-auth를 활용하여 accessToken과 refreshToken을 관리하는 과정에서 마주친 문제에 대해 설명드리겠습니다. next-auth의 jwt 콜백을 통해 토큰 만료 시 갱신하여 미들웨어에서 이를 처리하는 방식은 상당히 일반적인 접근법입니다. 그러나 클라이언트에서 세션 토큰이 쿠키에 업데이트되지 않는 문제로 고생하고 계신 것 같습니다.
기본적으로 next-auth는 서버 사이드에서 세션을 업데이트하고, 클라이언트에서는 쿠키를 통해 세션 정보를 전달받습니다. 따라서 서버 사이드에서 토큰이 업데이트되더라도 클라이언트의 쿠키가 업데이트되지 않는 경우가 발생할 수 있습니다. 이를 해결하기 위한 몇 가지 방법을 제안드립니다:
클라이언트에서 세션 갱신 강제화: 각 API 호출 직전에 세션 정보를 강제로 갱신하는 방법을 사용할 수 있습니다. 이는 next-auth
의 getSession
이나 useSession
훅을 호출하여 세션 정보를 항상 최신 상태로 유지할 수 있게 합니다. 하지만 이는 클라이언트-서버 간 통신의 빈도를 높일 수 있으니 주의가 필요합니다.
단일 갱신 요청: 여러 API 요청이 동시에 두 번 이상의 토큰 갱신을 유발할 수 있습니다. 이를 방지하기 위해, 특정 시점에 한 번만 토큰 갱신 요청을 하도록 throttling이나 debouncing 기술을 활용할 수 있습니다. 그렇게 되면 첫 번째 요청에서만 갱신이 이루어지고 나머지 요청은 동일한 세션 데이터로 처리될 것입니다.
토큰 유지 전략 변경: 현재 서버에서만 토큰을 갱신해서 업데이트된 세션 정보를 보존하지 못하는 문제를 해결하기 위해, 클라이언트 초기화 시점을 기준으로 토큰을 신규 발급 및 저장 처리하도록 변경할 수도 있습니다.
관련된 질문으로 볼 때, 이 문제에 대해 좀 더 다양한 맥락을 이해하는 데에 도움을 줄 수 있는 게시물이 있습니다:
- Next.js middleware에서 쿠키 업데이트에 관한 질문입니다.에서는 미들웨어에서 쿠키의 값이 업데이트되지 않는 현상에 대해 다룹니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
현재 api call 하는 로직은 이미 기존에 사용하던 로직들이 있어 route handler로 변경하기가 어려운 상황입니다..! 기존에 사용하던 프로젝트에서 인증 부분만 next-auth로 변경하는 작업 진행중입니다.