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

生产环境Passport.js报TokenError: Malformed auth code问题求助

Passport.js Google OAuth2生产环境出现TokenError: Malformed auth code的解决方案

问题描述

使用Passport.js实现Google OAuth2登录功能,开发环境运行正常,但部署到生产环境后触发以下错误:

TokenError: Malformed auth code.
    at Strategy.OAuth2Strategy.parseErrorResponse (/var/task/node_modules/passport-oauth2/lib/strategy.js:373:12)
    at Strategy.OAuth2Strategy._createOAuthError (/var/task/node_modules/passport-oauth2/lib/strategy.js:420:16)
    at /var/task/node_modules/passport-oauth2/lib/strategy.js:177:45
    at /var/task/node_modules/oauth/lib/oauth2.js:191:18
    at passBackControl (/var/task/node_modules/oauth/lib/oauth2.js:132:9)
    at IncomingMessage.<anonymous> (/var/task/node_modules/oauth/lib/oauth2.js:157:7)
    at IncomingMessage.emit (node:events:539:35)
    at endReadableNT (node:internal/streams/readable:1345:12)
    at processTicksAndRejections (node:internal/process/task_queues:83:21)

已尝试设置access_type: 'offline'、decodeUri等方法,耗时四天未解决。

相关代码片段

认证路由代码

const express = require('express');
const router = express.Router();
const passport = require('passport');
const { auth, requestdecode } = require('../middleware/reqAuth');
require('../config/passportAuth');
const { signToken } = require('../middleware/jwt');

router.get('/failed', (req, res) => {
    res.status(401).json({
        success: false,
        message: 'failure'
    });
});

router.post('/success', auth, (req, res) => {
    res.status(200).json({
        success: true,
        message: 'successful',
        user: req.user
    });
});

router.get(
    '/google',
    passport.authenticate('google', {
        scope: ['email', 'profile'],
        session: false,
        accessType: 'offline',
        prompt: 'consent'
    })
);

router.get(
    '/google/callback',
    passport.authenticate('google', {
        session: false,
        failureRedirect: '/failed'
    }),
    function (req, res) {
        try {
            const token = signToken(req);
            res
                .cookie('jwt_auth', token, {
                    maxAge: 11 * 60 * 60 * 1000,
                    httpOnly: false,
                    sameSite: false
                })
                .redirect(process.env.CLIENT_URL);
        } catch (err) {
            throw err;
        }
    }
);

router.get('/logout', (req, res) => {
    req.session = null;
    req.logout();
    res.redirect('/auth/google');
});

module.exports = router;

Passport配置代码

require('dotenv').config();
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const User = require('../model/user');

passport.serializeUser(function (user, done) {
    done(null, user);
});

passport.deserializeUser(function (user, done) {
    done(null, user);
});

passport.use(
    new GoogleStrategy(
        {
            clientID: process.env.CLIENT,
            clientSecret: process.env.CLIENT_SECRET,
            callbackURL: process.env.CALLBACK_URL,
            userProfileURL: 'https://www.googleapis.com/oauth2/v3/userinfo',
            accessType: 'offline',
            passReqToCallback: true
        },
        async function (request, accessToken, refreshToken, profile, done) {
            console.log(request);
            try {
                let existingUser = await User.findOne({ oauthId: profile.id });
                if (!existingUser) {
                    const newUser = await User.create({
                        oauthId: profile.id,
                        name: profile.displayName,
                        email: profile.emails[0].value,
                        imageLink: profile.photos[0].value
                    });
                    return done(null, newUser);
                }
                done(null, existingUser);
            } catch (error) {
                console.log(error);
            }
        }
    )
);

排查与解决步骤

1. 严格校验回调URL一致性

  • 登录Google Cloud控制台,进入OAuth 2.0客户端ID配置页面,确保已授权重定向URI与代码中process.env.CALLBACK_URL完全匹配,包括协议(必须是HTTPS)、域名、路径,不能有任何拼写错误或多余字符。
  • 注意:如果部署在第三方平台,需确认平台分配的实际域名是否与配置一致,避免使用本地测试域名。

2. 修正参数大小写问题

OAuth2标准参数为access_type(下划线),将路由和Passport配置中的accessType: 'offline'统一改为access_type: 'offline',避免因参数名兼容问题导致的异常。

3. 检查请求参数是否被篡改

在回调路由最上层添加中间件,打印接收到的auth code,对比开发环境的code格式,确认生产环境中code是否被代理、CDN或平台中间件修改:

router.get('/google/callback', (req, res, next) => {
  console.log('Received auth code:', req.query.code);
  next();
}, passport.authenticate('google', { session: false, failureRedirect: '/failed' }), ...);

如果code格式异常(如被重复编码),需调整平台的请求处理规则。

4. 验证生产环境变量配置

在Passport配置中添加日志,打印clientID、clientSecret、callbackURL的值,确保生产环境的环境变量没有多余空格、换行或配置错误:

console.log('Google OAuth Config:', {
  clientID: process.env.CLIENT,
  callbackURL: process.env.CALLBACK_URL
});

5. 修正Cookie安全配置

生产环境下sameSite: false必须配合secure: true(仅HTTPS环境生效),否则浏览器可能拦截Cookie导致认证流程中断,修改Cookie配置:

res.cookie('jwt_auth', token, {
  maxAge: 11 * 60 * 60 * 1000,
  httpOnly: false,
  sameSite: 'none',
  secure: true
})

6. 升级依赖版本

检查passport、passport-google-oauth20、oauth等依赖的版本,旧版本可能存在生产环境兼容性问题,执行以下命令升级到最新稳定版:

npm update passport passport-google-oauth20 oauth

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 11:37:02