You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NextJS 14 SSR(Auth0认证)如何实现客户端直接携带JWT调用外部.NET API并处理令牌刷新

NextJS 14 SSR(Auth0认证)如何实现客户端直接携带JWT调用外部.NET API并处理令牌刷新

兄弟,我太懂你这种为了调用个API还要绕一圈服务端的麻烦了——不仅重复写代码,还多了一次网络跳转,完全是浪费精力!咱们直接把流程改成客户端直连你的.NET API,同时把token过期刷新的问题也给解决掉,一步步来:

第一步:修改Auth0回调逻辑,把Token存到安全Cookie里

默认情况下,@auth0/nextjs-auth0会把token存在自己的session里,客户端拿不到也没法直接用。所以咱们要在Auth0的回调完成后,把access_token和refresh_token写到HttpOnly、Secure的Cookie里——这种Cookie只有浏览器能自动携带,JS读不到,安全性有保障。

打开你的app/api/auth/[auth0]/route.ts,修改handleAuth的回调逻辑:

import { handleAuth, handleCallback } from '@auth0/nextjs-auth0';

export const GET = handleAuth({
  async callback(req, res) {
    try {
      await handleCallback(req, res, {
        async afterCallback(req, res, session, state) {
          // 计算access_token的剩余有效期,转成秒
          const accessTokenMaxAge = Math.floor((session.accessTokenExpiresAt - Date.now()) / 1000);
          
          // 把token写到Cookie里,配置安全属性
          res.setHeader('Set-Cookie', [
            `access_token=${session.accessToken}; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=${accessTokenMaxAge}`,
            `refresh_token=${session.refreshToken}; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=2592000` // 30天,和Auth0配置保持一致
          ]);
          return session;
        }
      });
    } catch (error) {
      res.status((error as any).status || 500).end((error as Error).message);
    }
  }
});

第二步:给外部.NET API配置跨域(CORS)

因为你的NextJS客户端和.NET API大概率不是同一个域名,所以得让API允许来自NextJS域名的请求,并且允许携带Cookie凭证。打开.NET项目的Program.cs,添加CORS配置:

builder.Services.AddCors(options =>
{
  options.AddPolicy("AllowNextJSClients",
    policy =>
    {
      // 替换成你的NextJS域名,比如"https://your-nextjs-app.com"
      policy.WithOrigins("https://your-nextjs-domain.com")
            .AllowAnyHeader()
            .AllowAnyMethod()
            .AllowCredentials(); // 关键:允许携带Cookie凭证
    });
});

// 注意要放在UseRouting之后,UseAuthorization之前
app.UseCors("AllowNextJSClients");

第三步:让.NET API从Cookie里读取JWT验证

默认.NET的JWT认证是从Authorization头拿token,现在咱们要改成从Cookie里读access_token,调整认证中间件的配置:

using Microsoft.AspNetCore.Authentication.JwtBearer;

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  .AddJwtBearer(options =>
  {
    options.Authority = "https://your-auth0-domain.com"; // 替换成你的Auth0域名
    options.Audience = "your-api-audience"; // 替换成你的API在Auth0里配置的Audience
    
    // 从Cookie里获取JWT token
    options.Events = new JwtBearerEvents
    {
      OnMessageReceived = context =>
      {
        context.Token = context.Request.Cookies["access_token"];
        return Task.CompletedTask;
      }
    };
  });

第四步:实现NextJS的Token刷新接口

当access_token过期后,咱们要用refresh_token去Auth0换个新的。写个专门的API路由来干这个活,客户端不用直接和Auth0打交道,安全性更高。新建app/api/refresh-token/route.ts:

import { getSession, updateSession } from '@auth0/nextjs-auth0';
import { NextResponse } from 'next/server';

export const POST = async (req: Request) => {
  const session = await getSession();
  if (!session || !session.refreshToken) {
    return NextResponse.json({ error: '没有可用的刷新令牌' }, { status: 401 });
  }

  try {
    // 调用Auth0的令牌刷新接口
    const auth0Response = await fetch(`https://${process.env.AUTH0_DOMAIN}/oauth/token`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        grant_type: 'refresh_token',
        client_id: process.env.AUTH0_CLIENT_ID,
        client_secret: process.env.AUTH0_CLIENT_SECRET,
        refresh_token: session.refreshToken,
        audience: process.env.AUTH0_AUDIENCE // 你的API Audience
      })
    });

    const tokenData = await auth0Response.json();
    if (!auth0Response.ok) throw new Error(tokenData.error || '刷新令牌失败');

    // 更新session里的token信息
    session.accessToken = tokenData.access_token;
    session.accessTokenExpiresAt = Date.now() + (tokenData.expires_in * 1000);
    // 如果Auth0返回了新的refresh_token,也同步更新
    if (tokenData.refresh_token) session.refreshToken = tokenData.refresh_token;

    await updateSession(req, session);

    // 把新的access_token写到Cookie里
    const newAccessTokenCookie = `access_token=${tokenData.access_token}; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=${tokenData.expires_in}`;
    return new NextResponse(JSON.stringify({ success: true }), {
      headers: { 'Set-Cookie': newAccessTokenCookie }
    });
  } catch (error) {
    return NextResponse.json({ error: (error as Error).message }, { status: 500 });
  }
};

第五步:客户端封装Fetch函数,自动处理Token过期

客户端调用API的时候,咱们封装一个fetch函数,正常发请求,如果返回401(token过期),就先调用刷新接口拿新token,然后再重试原请求。注意要加上credentials: 'include',让浏览器自动把Cookie带过去:

// 客户端的工具函数,比如utils/api-fetch.ts
async function apiFetch(url: string, options: RequestInit = {}) {
  // 第一次请求API
  const response = await fetch(url, {
    ...options,
    credentials: 'include' // 关键:让浏览器自动携带Cookie
  });

  // 如果返回401,尝试刷新token并重试
  if (response.status === 401) {
    const refreshResponse = await fetch('/api/refresh-token', {
      method: 'POST',
      credentials: 'include'
    });

    if (refreshResponse.ok) {
      // 刷新成功,重试原请求
      return await fetch(url, {
        ...options,
        credentials: 'include'
      });
    } else {
      // 刷新失败,跳转到登录页重新登录
      window.location.href = '/api/auth/login';
      return Promise.reject(new Error('刷新令牌失败,请重新登录'));
    }
  }

  return response;
}

// 用法示例:调用你的.NET API
async function getProtectedData() {
  const response = await apiFetch('https://your-dotnet-api.com/protected-endpoint');
  if (!response.ok) throw new Error('请求失败');
  return await response.json();
}

一些关键注意事项

  • 域名与Cookie配置:如果NextJS和API是不同子域(比如app.yourdomain.com和api.yourdomain.com),Cookie的SameSite要设为Lax,并且要加上Domain=.yourdomain.com(注意前面的点),这样两个子域都能拿到Cookie;如果是完全不同的域名,跨域Cookie的限制会比较多,这时候可能得考虑用Bearer Token的方式(但要注意XSS风险,得加其他安全措施)
  • 安全第一:生产环境一定要开HTTPS,否则Secure Cookie不会生效;HttpOnly Cookie必须开启,防止XSS攻击偷token
  • Auth0控制台配置:要在Auth0控制台给你的应用开启Offline Access权限(在应用的Settings -> Advanced -> Grant Types里勾选Refresh Token),并且设置好Refresh Token的有效期

备注:内容来源于stack exchange,提问作者raezer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 11:07:58