이슈
- Refresh token으로 Access token을 재발급 받은 후 새로고침하면 로그인이 풀리는 현상
원인
- 리프레시 토큰으로 토큰 재발급 후 만료시간을 설정 오류
- 토큰을 설정하는 jwt 콜백이 비정상적으로 한번 더 실행
- 리프레시 토큰으로 토큰을 재발급 받은 후 jwt 콜백이 호출되고 session 콜백이 호출돼서 값이 넘어가야하는데 마지막에 만료된 토큰데이터로 jwt 콜백이 한번더 호출되면서 문제가 발생
해결
- 리프레시 토큰으로 토큰 재발급 후 만료시간을 설정 오류
import NextAuth from "next-auth"
import Providers from "next-auth/providers"
const GOOGLE_AUTHORIZATION_URL =
"https://accounts.google.com/o/oauth2/v2/auth?" +
new URLSearchParams({
prompt: "consent",
access_type: "offline",
response_type: "code",
})
/**
* Takes a token, and returns a new token with updated
* `accessToken` and `accessTokenExpires`. If an error occurs,
* returns the old token and an error property
*/
async function refreshAccessToken(token) {
try {
const url =
"https://oauth2.googleapis.com/token?" +
new URLSearchParams({
client_id: process.env.GOOGLE_CLIENT_ID,
client_secret: process.env.GOOGLE_CLIENT_SECRET,
grant_type: "refresh_token",
refresh_token: token.refreshToken,
})
const response = await fetch(url, {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
method: "POST",
})
const refreshedTokens = await response.json()
if (!response.ok) {
throw refreshedTokens
}
return {
...token,
accessToken: refreshedTokens.access_token,
// 여기(accessTokenExpires)에서 새로 발급받은 토큰의 만료시간을 설정
accessTokenExpires: Date.now() + refreshedTokens.expires_in * 1000,
refreshToken: refreshedTokens.refresh_token ?? token.refreshToken, // Fall back to old refresh token
}
} catch (error) {
console.log(error)
return {
...token,
error: "RefreshAccessTokenError",
}
}
}
export default NextAuth({
providers: [
Providers.Google({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
authorizationUrl: GOOGLE_AUTHORIZATION_URL,
}),
],
callbacks: {
async jwt(token, user, account) {
// Initial sign in
if (account && user) {
return {
accessToken: account.accessToken,
accessTokenExpires: Date.now() + account.expires_in * 1000,
refreshToken: account.refresh_token,
user,
}
}
// Return previous token if the access token has not expired yet
if (Date.now() < token.accessTokenExpires) {
return token
}
// Access token has expired, try to update it
return refreshAccessToken(token)
},
async session(session, token) {
if (token) {
session.user = token.user
session.accessToken = token.accessToken
session.error = token.error
}
return session
},
},
})
- refreshAccessToken함수의 리턴 값 중 accessTokenExpires에서 만료시간 설정에 버그가 있어서 수정
- 토큰을 설정하는 jwt 콜백이 비정상적으로 한번 더 실행
- 로컬환경에서만 발생하는 문제로 next.confg.js에 있는 reactStrictMode 설정을 false로 변경하면 비정상적으로 jwt 콜백이 한번 더 호출하는 현상이 사라지는 것을 확인
- 로컬환경에서 리프레시 토큰이 정상적으로 동작하는 것을 확인 후 개발의 안정성을 위해 reactStrictMode을 다시 true로 설정
참고