+6

Sử dụng Axios interceptor để refresh token cho Next-Auth

Khá nhiều developers thích sủ dụng Next-Auth để authenticate cho dự án NextJs, và cũng không ít developers thích sự dụng Axios (một mình hoặc đi kèm thư viện như React-Query). Một ngày đẹp trời bạn nhận ra Next-Auth lưu data ở cookie vậy làm sao để hoạt động với thằng Axios nhỉ? Bài viết này mình sẽ hướng dẫn các bạn sử dụng cobo Next-Auth Axios (Tanstack React Query) để refresh token khi access token hết hạn.

Khi sử dụng Axios đi kèm với Next-Auth chúng ta cần xử lý được 2 phần: Đưa access token vào Header với request interceptor, refresh được token với response interceptor khi access token hết hạn. Next-Auth không hỗ trợ việc update session từ phía client mà ngoài componen nên đẹp nhất chúng ta sẽ tạo ra axios auth dưới dạng 1 hook.

Thiết lập ApiAuth Hook

Khởi tạo axios instance

Tạo file tại lib/api.ts

// lib/api.ts
import { API_URL } from "@/config/const";
import axios from "axios";

export const ApiAuth = axios.create({
  baseURL: API_URL,
});

Giải thích: File này không có gì đáng nói các bạn nhớ linh hoạt API_URL nhé, đây là enpoint của backend

Api Auth Hook

Tạo file tại hooks/auth/useApiAuth.ts

// hooks/auth/useApiAuth.ts

import { useSession } from "next-auth/react";
import { useEffect } from "react";
import { useRefreshToken } from "./useRefreshToken";
import { ApiAuth } from "@/lib/Api";

const useApiAuth = () => {
  const { data: session } = useSession();
  const refreshToken = useRefreshToken();

  useEffect(() => {
    const requestIntercept = ApiAuth.interceptors.request.use(
      (config) => {
        if (!config.headers["Authorization"]) {
          config.headers[
            "Authorization"
          ] = `Bearer ${session?.tokens?.accessToken}`;
        }
        return config;
      },
      (error) => Promise.reject(error)
    );

    const responseIntercept = ApiAuth.interceptors.response.use(
      (response) => response,
      async (error) => {
        const prevRequest = error?.config;
        if (error?.response?.status === 401 && !prevRequest?.sent) {
          prevRequest.sent = true;
          await refreshToken();
          prevRequest.headers[
            "Authorization"
          ] = `Bearer ${session?.tokens.accessToken}`;
          return ApiAuth(prevRequest);
        }
        return Promise.reject(error);
      }
    );

    return () => {
      ApiAuth.interceptors.request.eject(requestIntercept);
      ApiAuth.interceptors.response.eject(responseIntercept);
    };
  }, [session, refreshToken]);

  return ApiAuth;
};

export default useApiAuth;

Giải thích: Hook này thiết lập request interceptor cho axios với access token lấy từ Next-Auth session. Đối với response interceptor nếu nhận về response status 401 thì tiến hành refresh token và gọi lại request

Refresh Token Hook

Tạo file tại hooks/auth/useRefreshToken.ts

// hooks/auth/useRefreshToken.ts
import { Api } from "@/lib/Api";
import { signIn, useSession } from "next-auth/react";

export const useRefreshToken = () => {
  const { data: session } = useSession();

  const refreshToken = async () => {
      // Gọi tới backend để lấy access token mới và trả về
    const res = await Api.post("/auth/refresh-token", {
      refreshToken: session?.tokens.refreshToken,
    });

    if (session) session.tokens.accessToken = res.data.tokens.accessToken;
    else signIn();
  };
  return refreshToken;
};

Giải thích: useRefreshToken lấy thông tin refresh token trong Next-Auth session, gọi lên backend để lấy access token mới, cập nhật vào Next-Auth session, và trả về giá trị access token

Sử dụng

Thuần Axios

Lúc này useApiAuth() đã trở thành 1 hook các bạn có thể gọi trong component như bình thường. Ví dụ:

// component posts
import useAxiosAuth from "@lib/hooks/useAxiosAuth";

import { useSession } from "next-auth/react";
import { useState } from "react";

const Posts = () => {
  const [posts, setPosts] = useState();
  const fetchPost = async () => {
    const res = await axiosAuth.get("/posts");
    setPosts(res.data);
  };

  useEffect(() => {
      fetchPost()
  }, []}
  return (
    ...
  );
};

export default HomePage;

Với React Query

Tạo 1 hook kết hợp Api Auth và React Query

// hooks/post/useGetPost.ts
import { useMutation } from "@tanstack/react-query";
import useApiAuth from "@hooks/api/useApiAuth";

export const useGetPosts = () => {
  const apiAuth = useApiAuth();
  return useQuery({
   queryKey: ["getPosts"],
    queryFn: () => Api.get("/posts").then((res) => res.data),
  });
};

Sử dụng trong component

// component posts
import useGetPosts from "@hooks/post/useGetPost";

const Posts = () => {
  const { isLoading, data } = useGetPosts();
  return (
    ...
  );
};

All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.