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

[Terraform 튜토리얼 1-6] 중복 없애다 망한 썰 – DRY 원칙, 정말 항상 맞을까?

[Terraform 튜토리얼 1-6] 중복 없애다 망한 썰 – DRY 원칙, 정말 항상 맞을까?

개발자라면 한 번쯤 들어봤을 말, "Don't Repeat Yourself", 줄여서 DRY 원칙. 이건 소프트웨어 개발에서 아주 중요한 원칙이에요. 중복을 줄이면 버그도 줄고, 유지 보수도 쉬워지고, 코드도 깔끔해지죠. 그런데… Terraform 같은 IaC 세계에서도 DRY가 무조건 좋을까요? 🤔 DRY가 뭔데? DRY 원칙의 핵심은 딱 하나: "같은 걸 반복해서 쓰지 마." * 상수 값, 로직, 설정

By Chansong
[Terraform 튜토리얼 1-5] 선언형 vs 명령형: Terraform은 왜 선언형인가?

[Terraform 튜토리얼 1-5] 선언형 vs 명령형: Terraform은 왜 선언형인가?

Terraform을 쓰다 보면 종종 듣게 되는 말이 하나 있어요: Terraform은 "선언형(Declarative)" 도구다. 그런데 이게 도대체 무슨 뜻일까요? "명령형(Imperative)"이랑은 뭐가 다르고, 왜 Terraform은 선언형일까요? 🧠 선언형과 명령형, 개념부터 정리 구분 선언형 (Declarative) 명령형 (Imperative) 핵심 개념 무엇이 되어야 하는지만 선언 어떻게 할지를 하나하나 명령 예시 “난 스테이크 먹을래” “고기

By Chansong
[Terraform 튜토리얼 1-4] 모듈이 정리가 안 될 땐, 응집도(Cohesion)를 의심하라

[Terraform 튜토리얼 1-4] 모듈이 정리가 안 될 땐, 응집도(Cohesion)를 의심하라

인프라를 코드로 관리할 때, 좋은 모듈을 만드는 핵심 개념 중 하나가 바로 응집도(Cohesion)입니다. 이 단어가 좀 낯설게 느껴질 수 있지만, 사실 우리는 매일 응집도가 높은 시스템 속에서 살고 있어요. ✅ 응집도란? 하나의 모듈이 하나의 책임 또는 기능에만 집중하는 정도 응집도가 높은 모듈은 말 그대로 한 가지 일에만 집중합니다. 그래서

By Chansong
[Terraform 튜토리얼 1-3] 내부는 감추고, 필요한 것만 보여줘 – 캡슐화란?

[Terraform 튜토리얼 1-3] 내부는 감추고, 필요한 것만 보여줘 – 캡슐화란?

Encapsulation, 캡슐화라고 하면 뭔가 개발자스럽고 어려워 보이죠? 객체지향 프로그래밍 할 때나 듣던 단어 같고요. 그런데 이 개념은, Terraform 같은 Infrastructure as Code(IaC)에서도 아주 중요합니다. 이번 글에서는 "캡슐화가 무엇이고", "왜 필요한지", 그리고 "Terraform에서는 어떻게 쓰이는지"를 쉽게 풀어보겠습니다. ✅ 캡슐화, 한 마디로 뭐야? “필요한 것만 보이게 하고, 나머지는 숨기는 것”

By Chansong