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

如何在Express Node.js服务器验证Next-auth的access_token?

问题解决:Next.js + Express 令牌验证 "jwt malformed" 错误

核心原因:Google的access_token不能用自定义密钥验证

你用jwt.verify(token, secret_key)验证Google返回的access_token肯定会失败——Google的access_token是用自身私钥签名的,不是你的secret_key。用自定义密钥去验证属于签名不匹配,自然会报格式错误或签名无效。


方案一:正确验证Google的access_token

方法1:调用Google官方验证端点

直接请求Google的令牌校验接口,确认有效性:

// Express后端验证代码
app.post('/api/verify-google-token', async (req, res) => {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Invalid token' });
  }
  const token = authHeader.split(' ')[1];
  
  // 请求Google验证接口
  const response = await fetch(`https://oauth2.googleapis.com/tokeninfo?access_token=${token}`);
  const data = await response.json();
  
  // 校验令牌归属:返回的邮箱需与请求头一致
  if (data.error || data.email !== req.headers.email) {
    return res.status(401).json({ error: 'Token invalid or unauthorized' });
  }
  
  res.status(200).json({ message: 'Token verified', user: data });
});

方法2:用Google官方库验证

更可靠的方式是使用google-auth-library:

# 安装依赖
npm install google-auth-library
// Express后端代码
const { OAuth2Client } = require('google-auth-library');
const client = new OAuth2Client(process.env.GOOGLE_CLIENT_ID);

app.post('/api/verify-google-token', async (req, res) => {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Invalid token' });
  }
  const token = authHeader.split(' ')[1];
  
  try {
    const ticket = await client.verifyIdToken({
      idToken: token,
      audience: process.env.GOOGLE_CLIENT_ID, // 你的Google客户端ID
    });
    const payload = ticket.getPayload();
    const userEmail = payload['email'];
    
    if (userEmail !== req.headers.email) {
      return res.status(401).json({ error: 'Unauthorized' });
    }
    
    res.status(200).json({ message: 'Token verified', user: payload });
  } catch (err) {
    res.status(401).json({ error: 'Token invalid', details: err.message });
  }
});

方案二:自定义JWT(Express生成后传给Next-auth)

这种方式完全可控,用自己的密钥管理令牌:

1. Express后端生成JWT接口

# 安装依赖
npm install jsonwebtoken
const jwt = require('jsonwebtoken');
const SECRET_KEY = process.env.JWT_SECRET;

app.post('/api/generate-jwt', async (req, res) => {
  const { email, name } = req.body;
  // 生成自定义JWT,有效期可自行设置
  const token = jwt.sign({ email, name }, SECRET_KEY, { expiresIn: '24h' });
  res.status(200).json({ token });
});

2. Next-auth回调中获取自定义JWT

在pages/api/auth/[...nextauth].js中,通过session回调调用Express接口:

import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";

export default NextAuth({
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    }),
  ],
  callbacks: {
    async session({ session, user }) {
      // 调用Express生成自定义JWT
      const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/generate-jwt`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email: session.user.email, name: session.user.name }),
      });
      const data = await response.json();
      // 将自定义JWT存入session
      session.user.customToken = data.token;
      return session;
    },
  },
});

3. 前端用自定义JWT请求后端

// 前端请求代码
const fetchData = async () => {
  const session = await getSession();
  const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/protected`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      authorization: `Bearer ${session.user.customToken}`,
    },
  });
  const data = await response.json();
};

4. Express后端验证自定义JWT

// 验证中间件
const authenticateToken = (req, res, next) => {
  const authHeader = req.headers.authorization;
  const token = authHeader && authHeader.split(' ')[1];
  
  if (!token) {
    return res.status(401).json({ error: 'No token provided' });
  }
  
  jwt.verify(token, SECRET_KEY, (err, user) => {
    if (err) {
      return res.status(403).json({ error: 'Invalid token' });
    }
    req.user = user;
    next();
  });
};

// 受保护路由示例
app.post('/api/protected', authenticateToken, (req, res) => {
  res.status(200).json({ message: 'Access granted', user: req.user });
});

排查小技巧

  • 先console.log(session.user.access_token),确认令牌是完整的三段式JWT(用.分隔的字符串),如果格式不对,可能是获取令牌的方式有误。
  • 检查后端提取令牌的代码,确保authHeader.split(' ')[1]正确拿到令牌,没有多截或漏截字符。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 18:15:23