Next-auth refresh token

이슈

  • Refresh token으로 Access token을 재발급 받은 후 새로고침하면 로그인이 풀리는 현상

원인

  1. 리프레시 토큰으로 토큰 재발급 후 만료시간을 설정 오류
  2. 토큰을 설정하는 jwt 콜백이 비정상적으로 한번 더 실행
    1. 리프레시 토큰으로 토큰을 재발급 받은 후 jwt 콜백이 호출되고 session 콜백이 호출돼서 값이 넘어가야하는데 마지막에 만료된 토큰데이터로 jwt 콜백이 한번더 호출되면서 문제가 발생

해결

  1. 리프레시 토큰으로 토큰 재발급 후 만료시간을 설정 오류
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에서 만료시간 설정에 버그가 있어서 수정

  1. 토큰을 설정하는 jwt 콜백이 비정상적으로 한번 더 실행
    1. 로컬환경에서만 발생하는 문제로 next.confg.js에 있는 reactStrictMode 설정을 false로 변경하면 비정상적으로 jwt 콜백이 한번 더 호출하는 현상이 사라지는 것을 확인
    2. 로컬환경에서 리프레시 토큰이 정상적으로 동작하는 것을 확인 후 개발의 안정성을 위해 reactStrictMode을 다시 true로 설정

참고

Read more

Lumen - AI Agent를 위한 지속 가능한 두뇌

Lumen - AI Agent를 위한 지속 가능한 두뇌

왜 만들었는가 AI 에이전트는 모든 대화를 기억상실증 상태에서 시작합니다. Claude Code, Cursor, Codex, Mastra 하네스, LangChain 파이프라인 — 이 도구들은 세상을 알지만 당신의 세상은 전혀 모릅니다. 당신이 읽은 200편의 논문, 당신이 출시하는 코드베이스, 지난 분기에 내린 아키텍처 결정, 새벽 2시에 그 버그를 잡아냈을 때 마침내 통했던 트래젝토리. 모든 세션이 같은 컨텍스트를

By Sardor Madaminov

200 OK, 텅 빈 body — Starlette Race Condition 장애 분석기

발생일: 2026-04-23 / 해결일: 2026-04-27 영향 범위: report-dev.machine365.ai 전체 API 들어가며 API가 200 OK를 반환하는데 body가 비어있다. 프론트엔드에는 아무것도 안 뜨고, Swagger UI(/docs)도 빈 화면. 그런데 로컬에서 돌리면 멀쩡하다. 배경은 이랬다. 미터링(Metering) 기능을 만들면서 API 호출 로그를 수집할 미들웨어를 작성했다. Spring Boot 백엔드에 먼저 적용하고, Python

By Jeonggil