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

使用Auth0 withMiddlewareAuthRequired致Next.js+Hono API路由CORS问题

问题分析

你在基于子域名的多租户应用中遇到了Auth0中间件与Hono CORS冲突、API路由保护与C兼容问题:

  • 当用withMiddlewareAuthRequired保护所有路由时,Hono API路由触发CORS错误(缺少Access-Control-Allow-Origin头)
  • 排除API路由后,CORS问题解决但API失去Auth0保护
  • withApiAuthRequired依赖Node.js原生req/res,无法直接适配Hono的Context对象

核心原因是:Next.js中间件会优先拦截API请求并执行Auth0认证逻辑,此时Hono的CORS中间件还未处理响应头,导致跨域请求失败;同时Auth0的API认证工具与Hono的上下文模型不兼容。


解决方案

方案1:在Next.js中间件中统一处理CORS与API认证

修改middleware.ts,提前为API路由添加CORS头,并针对API请求调整权限校验的响应方式(避免页面重定向):

export default withMiddlewareAuthRequired(async function middleware(request: NextRequest) {
  const response = NextResponse.next();
  const pathname = request.nextUrl.pathname;
  const host = request.headers.get('host');
  const session = await getSession(request, response);

  // 1. 优先处理API路由的CORS
  if (pathname.startsWith('/api')) {
    const origin = request.headers.get('origin');
    // 配置允许的源(生产环境替换为你的主域名后缀)
    const allowedOrigins = process.env.NODE_ENV === 'production'
      ? [new RegExp(`^https://.*\\.yourdomain\\.com$`)]
      : [/^http:\/\/.*\.localhost:\d+$/];
    
    if (origin && allowedOrigins.some(regex => regex.test(origin))) {
      response.headers.set('Access-Control-Allow-Origin', origin);
      response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
      response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
      response.headers.set('Access-Control-Allow-Credentials', 'true');
    }

    // 处理OPTIONS预检请求
    if (request.method === 'OPTIONS') {
      return new NextResponse(null, { status: 200, headers: response.headers });
    }
  }

  // 2. 原有子域名逻辑(针对API调整权限校验响应)
  if (host) {
    const domain = getDomain(host, true);
    const segments = host.split('.');
    const hasSubdomain = host.includes('localhost') ? segments.length > 1 : segments.length > 2;

    if (hasSubdomain) {
      if (pathname === '/') {
        return NextResponse.redirect(
          process.env.NODE_ENV === 'production' ? `https://${host}/main` : `http://${host}/main` 
        );
      } else if (session && segments[0] !== session.user.org_name) {
        // API路由返回JSON错误,而非页面重定向
        if (pathname.startsWith('/api')) {
          return new NextResponse(JSON.stringify({ error: 'Unauthorized organization access' }), {
            status: 401,
            headers: {
              ...response.headers,
              'Content-Type': 'application/json',
            },
          });
        }
        return NextResponse.redirect(
          process.env.NODE_ENV === 'production'
            ? `https://${domain}/error/401/unauthorized_org`
            : `http://${domain}/error/401/unauthorized_org`
        );
      }
    } else if (pathname === '/main') {
      return NextResponse.redirect(
        process.env.NODE_ENV === 'production' ? `https://${host}` : `http://${host}` 
      );
    }
  }

  return response;
})

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};

关键修改点:

  • 提前为API路由注入CORS头,覆盖Auth0中间件的响应头缺失问题
  • 针对API请求的权限校验失败,返回JSON格式错误而非页面重定向(符合API响应规范)
  • 保留所有路由的Auth0保护,无需排除API路径

方案2:封装适配Hono的Auth0认证中间件

如果希望在Hono层面处理API认证,可以封装一个适配HonoContext的Auth0中间件:

import { createMiddleware } from 'hono/factory';
import { getSession } from '@auth0/nextjs-auth0';
import type { Context } from 'hono';

// 适配Hono的Auth0认证中间件
export const withHonoApiAuthRequired = createMiddleware(async (c: Context, next) => {
  // 从Hono Context中提取Node.js原生req
  const req = c.req.raw;
  // 用NextResponse适配getSession的res参数
  const res = new Response();

  const session = await getSession(req, res as any);

  if (!session) {
    return c.json({ error: 'Unauthorized' }, 401);
  }

  // 将session挂载到Hono Context,后续路由可直接调用
  c.set('session', session);
  await next();
});

在Hono路由中使用:

const app = createApp().basePath('/api');
// 移除Hono全局CORS,改用Next.js中间件处理(避免双重CORS冲突)
// app.use('*', cors()); 

// 为需要保护的API路由应用认证中间件
app.use('/organizationFromId/*', withHonoApiAuthRequired);

app.get('/organizationFromId/:orgId', async (c) => {
  const session = c.get('session');
  const orgId = c.req.param('orgId');
  
  // 子域名权限校验
  const host = c.req.header('host');
  if (host) {
    const segments = host.split('.');
    const subdomain = segments[0];
    if (subdomain !== session.user.org_name) {
      return c.json({ error: 'Unauthorized organization access' }, 401);
    }
  }

  // 业务逻辑
  return c.json({ orgId, organization: session.user.org_name });
});

优势:

  • 完全在Hono生态内处理API认证,逻辑更集中
  • 保留Auth0的session校验能力,同时适配Hono的上下文模型

注意事项
  1. 避免双重CORS处理:要么在Next.js中间件配置CORS,要么在Hono配置,不要同时开启,否则会导致响应头重复或冲突
  2. API响应规范:API请求失败时不要返回页面重定向,应返回JSON格式的错误信息,避免前端处理异常
  3. 权限逻辑一致性:确保页面路由和API路由的子域名校验逻辑完全一致,避免出现权限漏洞

内容的提问来源于stack exchange,提问作者user26923635

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 19:28:11