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

배타적 서브타입(Exlusive Subtype) 테이블 설계와 백엔드 구현

배타적 서브타입(Exlusive Subtype) 테이블 설계와 백엔드 구현

배타적 서브타입이란? 데이터베이스 테이블 설계에서 슈퍼타입(Super Type)과 서브타입(Sub Type)은 공통 속성을 기준으로 엔티티(테이블)를 상위/하위로 나누는 데이터 모델링 기법입니다. * Super Type (상위 엔티티) * 여러 엔티티가 공통으로 가지는 속성을 모아 놓은 엔티티 * 식별자(PK, ID)를 포함한 공통 정보를 가지고 있음 * Sub Type (하위 엔티티)

By Plato Jung
A2A 메모리 레이어: AI 에이전트를 위한 공유 두뇌 구축하기

A2A 메모리 레이어: AI 에이전트를 위한 공유 두뇌 구축하기

모든 것의 시작이 된 문제 우리를 계속 괴롭히던 것이 있었습니다: 왜 AI 에이전트는 대화가 끝나는 순간 모든 것을 잊어버릴까요? 한번 생각해보세요. 챗봇에게 전화보다 이메일을 선호한다고 말합니다. 다음 세션에서는요? 마치 처음 만난 것처럼 행동합니다. 한 상담원에게 주문 문제를 설명하고, 다른 상담원에게 연결되면, 갑자기 처음부터 다시 시작해야 합니다. 우리 모두 겪어본 일입니다.

By Sardor Madaminov
[시리즈 2편] 실무로 배우는 메시지 큐 - RabbitMQ

[시리즈 2편] 실무로 배우는 메시지 큐 - RabbitMQ

들어가며 [시리즈1]에서는 프로세스 내부 메시지 큐를 다뤘습니다. 이번엔 네트워크 메시지 큐인 RabbitMQ를 다룹니다. RabbitMQ 공식 문서나 기술 블로그는 많지만, 실무에서 어떻게 사용하는지에 대한 글은 의외로 적습니다. "Producer가 뭐고 Consumer가 뭔지는 알겠는데, 그래서 실제로는 어떻게 쓰는데?" 이번 글에서는 우리 MES 시스템에서 RabbitMQ를 어떻게 활용하고 있는지 실제 코드와 함께 공유합니다. 우리

By Jeonggil
[시리즈 1편] 실무로 배우는 메시지 큐 - Windows Message Loop

[시리즈 1편] 실무로 배우는 메시지 큐 - Windows Message Loop

들어가며 이 글은 "실무로 배우는 메시지 큐" 시리즈의 첫 번째 글입니다. 실무에서 발견한 문제를 해결하는 과정에서, IME 입력 문제와 해결 과정을 공유합니다. 메시지 큐는 RabbitMQ, Kafka 같은 네트워크 레벨만 있는 게 아닙니다. 우리가 매일 쓰는 Windows 애플리케이션도 메시지 큐 기반으로 동작합니다. * 시리즈1 (이 글): 프로세스 내부의 메시지 큐 - Windows

By Jeonggil