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

NextJS+Next-Auth结合Service Worker:获取原始JWT令牌问题

解决方案:NextJS+Next-Auth结合Service Worker处理API授权头问题

一、全局注册Service Worker

无需在每个页面单独添加注册代码,直接在_app.tsx(或_app.js)中全局处理——这是所有页面的统一入口,能确保无论从哪个页面启动应用,Service Worker都会被注册:

// pages/_app.tsx
import { useEffect } from 'react';
import { SessionProvider } from 'next-auth/react';

function MyApp({ Component, pageProps }) {
  useEffect(() => {
    if ('serviceWorker' in navigator) {
      window.addEventListener('load', async () => {
        try {
          await navigator.serviceWorker.register('/sw.js');
        } catch (err) {
          console.error('Service Worker注册失败:', err);
        }
      });
    }
  }, []);

  return (
    <SessionProvider session={pageProps.session}>
      <Component {...pageProps} />
    </SessionProvider>
  );
}

export default MyApp;

二、让Service Worker自动获取JWT令牌

方案1:扩展Next-Auth API端点(完全可行)

在pages/api/auth/下新增自定义路由,让Service Worker直接调用接口获取JWT,无需页面层传递:

// pages/api/auth/get-jwt.ts
import { getToken } from 'next-auth/jwt';
import type { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET });
  if (!token) return res.status(401).json({ error: '未授权' });
  
  // 假设你的JWT存储在token的accessToken字段中,根据实际调整
  res.status(200).json({ jwt: token.accessToken });
}

然后在Service Worker中启动时调用该接口,后续监听fetch事件自动添加授权头:

// public/sw.js
let jwtToken = null;

// 初始化获取JWT
async function fetchJwt() {
  try {
    const res = await fetch('/api/auth/get-jwt');
    if (res.ok) {
      const data = await res.json();
      jwtToken = data.jwt;
    }
  } catch (err) {
    console.error('获取JWT失败:', err);
  }
}

// 安装阶段触发初始化
self.addEventListener('install', (event) => {
  event.waitUntil(fetchJwt());
});

// 监听fetch请求添加授权头
self.addEventListener('fetch', (event) => {
  // 仅针对目标外部API添加头
  if (event.request.url.startsWith('https://你的外部API域名/')) {
    if (!jwtToken) {
      // 无token时先重新获取再发起请求
      event.respondWith(
        fetchJwt().then(() => {
          const modifiedReq = new Request(event.request, {
            headers: new Headers({
              ...event.request.headers,
              'Authorization': `Bearer ${jwtToken}`
            })
          });
          return fetch(modifiedReq);
        })
      );
    } else {
      const modifiedReq = new Request(event.request, {
        headers: new Headers({
          ...event.request.headers,
          'Authorization': `Bearer ${jwtToken}`
        })
      });
      event.respondWith(fetch(modifiedReq));
    }
  }
});

方案2:全局监听会话变化推送JWT

如果不想扩展API,可在_app.tsx中监听Next-Auth会话更新,自动给Service Worker发送JWT:

// pages/_app.tsx
import { useEffect } from 'react';
import { SessionProvider, useSession } from 'next-auth/react';

function SessionSync() {
  const { data: session } = useSession();

  useEffect(() => {
    if (session?.user?.accessToken && navigator.serviceWorker.controller) {
      navigator.serviceWorker.controller.postMessage({
        type: 'UPDATE_JWT',
        token: session.user.accessToken
      });
    }
  }, [session]);

  return null;
}

function MyApp({ Component, pageProps }) {
  // ... 之前的Service Worker注册代码

  return (
    <SessionProvider session={pageProps.session}>
      <SessionSync />
      <Component {...pageProps} />
    </SessionProvider>
  );
}

export default MyApp;

Service Worker中监听消息存储token:

// public/sw.js
let jwtToken = null;

self.addEventListener('message', (event) => {
  if (event.data.type === 'UPDATE_JWT') {
    jwtToken = event.data.token;
  }
});

// fetch事件处理同上

三、阻止Next-Auth在会话中附加JWT

修改Next-Auth的session()回调,只返回必要的用户信息,移除JWT的前端传递:

// pages/api/auth/[...nextauth].ts
import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';

export default NextAuth({
  providers: [
    // 你的OAuth配置
  ],
  callbacks: {
    async jwt({ token, user, account }) {
      // 这里依然保留JWT的捕获逻辑,用于后端和自定义API接口
      if (account) token.accessToken = account.access_token;
      return token;
    },
    async session({ session, token }) {
      // 仅返回用户基本信息,不传递JWT到前端会话
      session.user.id = token.id;
      session.user.name = token.name;
      session.user.email = token.email;
      return session;
    }
  },
  secret: process.env.NEXTAUTH_SECRET
});

总结

  • 全局注册Service Worker用_app.tsx即可覆盖所有页面入口;
  • 扩展Next-Auth API端点是最简洁的令牌获取方案,无需页面层干预;
  • 修改session()回调可彻底避免JWT在前端会话中冗余传递。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 13:30:56