最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Handling Authentication Tokens in Next.js Server Actions with External APIs - Stack Overflow

programmeradmin2浏览0评论

The Problem

I recently encountered a challenging authentication issue when working with Next.js Server Actions connecting to a NestJS backend API.

When making authenticated requests from client components everything works fine, but when using Server Actions (with the "use server" directive), the authentication token wasn't being passed to my external API, resulting in 401 Unauthorized errors.

Context

My architecture:

  • Frontend: Next.js app with next-auth for authentication
  • Backend: NestJS API requiring JWT token authentication
  • Authentication: JWT tokens stored in next-auth session

Client-side Code

// axios.ts - My API handler for HTTP requests
import axios, { AxiosInstance, AxiosRequestConfig } from "axios";
import { getSession } from "next-auth/react";

const axiosInstance: AxiosInstance = axios.create({
  baseURL: "http://localhost:3334/api",
  headers: {
    "Content-Type": "application/json",
    Accept: "application/json",
  },
});

// This works fine for client-side requests
axiosInstance.interceptors.request.use(async (request) => {
  const session = await getSession();
  if (session?.accessToken) {
    request.headers.Authorization = `Bearer ${session.accessToken}`;
  }
  return request;
});

export const apiHandler = async (
  method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
  url: string,
  body?: any,
  params?: any,
) => {
  const config: AxiosRequestConfig = {
    method,
    url,
    data: body,
    params: {
      ...params,
      where: params?.where ? JSON.stringify(params.where) : undefined,
      orderBy: method === "GET" ? params?.orderBy || "createdAt:desc" : undefined,
    },
  };
  
  try {
    const res = await axiosInstance(config);
    return {
      status: res.status,
      data: res.data,
    };
  } catch (error: any) {
    // Error handling
    console.log(`error`, error);
    throw {
      status: error.response?.status || 500,
      data: { message: error?.response?.data?.message || "An error occurred" },
    };
  }
};

export const setAuthToken = (token?: string) => {
  if (token) {
    axiosInstance.defaults.headersmon["Authorization"] = `Bearer ${token}`;
  } else {
    delete axiosInstance.defaults.headersmon["Authorization"];
  }
};

Server Action

"use server";
import { apiHandler } from "@/lib/axios";

export const countMissions = async (params) => {
  const res = await apiHandler(
    "GET",
    `/mission/count/specific`,
    undefined,
    params,
  );
  return res?.data || 0;
};

Usage in Components

// Client component
'use client';
import { countMissions } from "@/actions/mission";
import { useSession } from "next-auth/react";

export function MissionsComponent() {
  const { data: session } = useSession();
  
  // This works fine until we call countMissions
  useEffect(() => {
    if (session?.accessToken) {
      // Set token for client-side requests
      setAuthToken(session.accessToken);
      
      // But this fails with 401 because the server action can't access the token
      countMissions({ where: { /* params */ } }).then(data => {
        // Process data
      });
    }
  }, [session]);
  
  return <div>Missions content</div>;
}

Server Logs

[Nest] 105854 - 03/21/2025, 6:29:28 PM    WARN [HTTP] GET http://localhost:3334/api/mission/count/specific?where=%7B%22talents%22:%7B%22some%22:%7B%22talentId%22:%22cm7z762p70002svpppibalqr5%22%7D%7D%7D&orderBy=createdAt:desc 401, data object : {"date":"2025-03-21T18:29:28.991Z","method":"GET","protocol":"http","path":"/api/mission/count/specific","authorization":"","duration":3,"status":401} 3ms

The key issue is visible in the logs: "authorization":"" - the token is missing when the request is made from the server action.

Solutions I'm Considering

  1. Pass Token as Parameter: Update all server actions to accept a token parameter

    export const countMissions = async (params, token) => {
      // Pass token in headers
    };
    
  2. Token Store: Create a shared token store accessible to server actions

    // Client-side: set token
    setGlobalToken(session.accessToken);
    
    // Server-side: use token
    const token = getGlobalToken();
    
  3. Remove "use server": Convert server actions to client-side functions (works but loses server benefits)

  4. Use getServerSession: Retrieve session directly in server actions (if next-auth is properly configured)

    import { getServerSession } from "next-auth/next";
    
    export async function serverAction() {
      const session = await getServerSession();
      // Use session.accessToken
    }
    

Questions

  1. What's the recommended way to handle this authentication pattern in Next.js apps with external APIs?
  2. Are there built-in solutions in Next.js for passing auth tokens to server actions?
  3. Has anyone implemented a clean solution that doesn't require refactoring every server action?
  4. What are the security implications of the different approaches?

Looking forward to hearing how others are solving this common challenge!

发布评论

评论列表(0)

  1. 暂无评论