JWT 来自一个网站的无效签名,但另一个网站没有错误。用户 object 的额外属性被添加到令牌

JWT Invalid signature from one website, but no errors with another. Extra properties of user object get added to token

问题

我有两个网站。一个是生产,但是一个非常非常旧的版本。 另一个只是我目前正在测试的本地主机。 在涉及 NextAuthJS 到 NextJS 网站后,我需要将我的 NextAuthJS session 提供程序集成到远程 ExpressJS 后端。

  1. 我请求 /login ->

    1. 在远程后端我生成一个 JWT 令牌,并获取完整的用户详细信息。
    2. 然后我将数据发送到 NextJS 网站。
    3. 使用 NextAuthJS,将其存储在 cookie 中。

  1. 如果我需要获取任何用户特定的详细信息,例如保存的视频:

    1. 我从 NextJS 网站提出请求。
    2. 在远程 ExpressJS 后端,我解码了在 'authorization' 承载中收到的令牌 header,获取用户的详细信息...

繁荣!此处错误消息重复对我大喊:“JsonWebTokenError:无效签名”

在生产网站上不会发生这种情况,即使远程后端是用于生产和开发的 link。

我尝试和探索的东西

我在使用 JWT token debugger 时注意到的是

  1. 对于生产网站的请求,没有给出任何错误,解码后的用户由{id,角色}组成。正如预期的那样。
  2. 但是如果是本地版本的请求,会给出错误消息,这表明用户包含存储在数据库中的整个用户详细信息object。 并且调试器还会自动将解密算法从 HS256 更改为 HS512。而且,即使显示加密消息,它也会显示 'token verification failed'。

来自本地的令牌,给出错误: eyJhbGciOiJIUzUxMiJ9.eyJlbWFpbCI6ImdtYWlsQGdtYWlsLmNvIiwiYWNjZXNzVG9rZW4iOiJCZWFyZXIgZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SmZhV1FpT2lJMk1UZGpNV0V6TjJFd1lqQXpNakV4TnpFMU0yUTBOek1pTENKeWIyeGxJam9pZFhObGNpSXNJbWxoZENJNk1UWXpOakk1TURrME1Td2laWGh3SWpveE5qTTJNemMzTXpReGZRLmI1YTQ3bF9rcVQ5YUhDVGhFM3VtVFZJOTZZSnpkdkJaZHQxN3hMTFJPU1EiLCJ1c2VyIjp7Il9pZCI6IjYxN2MxYTM3YTBiMDMyMTE3MTUzZDQ3MyIsImZpcnN0TmFtZSI6IiIsImxhc3ROYW1lIjoiIiwiZW1haWwiOiJnbWFpbEBnbWFpbC5jbyIsInJvbGUiOiJ1c2VyIiwiam9iUm9sZSI6IiIsInZpZGVvR29hbCI6IiIsInByb2ZpbGVQaWN0dXJlIjoiIiwiYXBwcm92YWwiOnsiaXNBcHByb3ZlZCI6dHJ1ZSwidHJ4SWQiOiIifSwiYWNjZXNzVHlwZSI6eyJicmFuZGluZzEiOnRydWUsImJyYW5kaW5nMiI6dHJ1ZSwiYnJhbmRpbmczIjp0cnVlLCJicmFuZGluZzQiOnRydWUsInNjcmlwdCI6dHJ1ZSwidGVtcGxhdGUiOnRydWUsImZ1bGxBY2Nlc3MiOnRydWV9LCJyZXNldFBhc3NUb2tlbiI6IiIsImJyYW5kaW5nIjp7ImJyYW5kaW5nMSI6IjYxN2MxYTM3YTBiMDMyMTE3MTUzZDQ3NCIsImJyYW5kaW5nMiI6IjYxN2MxYTM3YTBiMDMyMTE3MTUzZDQ3NSIsImJyYW5kaW5nMyI6IjYxN2MxYTM3YTBiMDMyMTE3MTUzZDQ3NiIsImJyYW5kaW5nNCI6IjY xN2MxYTM3YTBiMDMyMTE3MTUzZDQ3NyJ9LCJzdWJfc3RhdHVzIjoyfSwiaWF0IjoxNjM2MjkxNTk0LCJleHAiOjE2Mzg4ODM1OTR9.ZCnnNZ2a7aeO6GCnyhnWYGMX-4kkJX75ZyzG52lSiWxUBIawXrA37882HFwo_3r6-I3JrrYNv270vxfsMwyBlw


来自生产的令牌,按预期工作: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MTdjMWEzN2EwYjAzMjExNzE1M2Q0NzMiLCJyb2xlIjoidXNlciIsImlhdCI6MTYzNjI5MTY3MywiZXhwIjoxNjM2Mzc4=MDczf=.


JWT 秘密:jdgfaiodsugfaileufliaeruy


解码token获取用户数据的代码为:

exports.requireSignin = (req, res, next) => {
  if (req.headers.authorization) {
    const token = req.headers.authorization.split(" ")[1];
    console.log( 'token is', token );
    jwt.verify(token, process.env.JWT_SECRET, function (err, decoded) {
      if (err) {
        console.log( err, 'Token given', token, 'Token secret', process.env.JWT_SECRET );
        return res.status(401).json({ error: "Token has been expired!" });
      }
      req.user = decoded;
      console.log( decoded );
    });
    // const user = jwt.verify(token, process.env.JWT_SECRET);
    // console.log(user);
    // req.user = user;
  } else {
    return res.status(401).json({ error: "Authorization required" });
  }
  next();
  //jwt.decode()
};

生成该令牌的代码:

exports.signin = async (req, res) => {
  
  
  User.findOne({ email: req.body.email }).exec((error, user) => {
    if (error){ console.log( 'login error', error ); return res.status(400).json({ error }); }
   
    if (user) {
      bcrypt.compare(req.body.password, user.hash_password, (err, result) => {
        if (err) {
            console.log( req.body.password, user.hash_password );
          return res
            .status(400)
            .json({ error: "Something's wrong, Please try again" });
        }
        if (!result) {
          return res.status(400).json({ error: "Invalid credentials" });
        }

        if (user.approval.isApproved === false)
          return res
            .status(400)
            .json({ error: "Please verify your account" });

        if (user.isSuspended === true)
          return res.status(400).json({ error: "You have been temporarily suspended, please contact support" });

        const token = jwt.sign(
          { _id: user._id, role: user.role },
          process.env.JWT_SECRET,
          {
            expiresIn: "1d",
          }
        );
        console.log( process.env.JWT_SECRET, token, user._id, user.role );

        // some unnecessary code ...

          res.status(200).json({
            success: true,
            token: "Bearer " + token,
            ProfileImageBaseUrl: ProfileImageBaseUrl,
            user: {
              _id,
              firstName,
              lastName,
              email,
              role,
              jobRole,
              videoGoal,
              profilePicture,
              createdBy,
              approval,
              accessType,
              resetPassToken,
              branding,
              sub_status
            },
          });

           
            
        })
       
      });
    } else {
      return res.status(400).json({ error: "Something's wrong, Please try again" });
    }
  });
};

A) 当我遇到错误时,我从后端收到的日志是:


登录控制器,console.log(process.env.JWT_SECRET,token,user._id,user.role ) 是

jdgfaiodsugfaileufliaeruy eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MTdjMWEzN2EwYjAzMjExNzE1M2Q0NzMiLCJyb2xlIjoidXNlciIsImlhdCI6MTYzNjI5MTY3MywiZXhwIjoxNjM2Mzc4MDczfQ.bqY6y0_lmX5dBAHemgv_9UFuupwLBBDcyFpAdXgCiH8 617c1a37a0b032117153d473 user


需要登录控制器,console.log( err, 'Token given', token, 'Token secret', process.env.JWT_SECRET ) 是:

Token given eyJhbGciOiJIUzUxMiJ9.eyJlbWFpbCI6ImdtYWlsQGdtYWlsLmNvIiwiYWNjZXNzVG9rZW4iOiJCZWFyZXIgZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SmZhV1FpT2lJMk1UZGpNV0V6TjJFd1lqQXpNakV4TnpFMU0yUTBOek1pTENKeWIyeGxJam9pZFhObGNpSXNJbWxoZENJNk1UWXpOakk1TURrME1Td2laWGh3SWpveE5qTTJNemMzTXpReGZRLmI1YTQ3bF9rcVQ5YUhDVGhFM3VtVFZJOTZZSnpkdkJaZHQxN3hMTFJPU1EiLCJ1c2VyIjp7Il9pZCI6IjYxN2MxYTM3YTBiMDMyMTE3MTUzZDQ3MyIsImZpcnN0TmFtZSI6IiIsImxhc3ROYW1lIjoiIiwiZW1haWwiOiJnbWFpbEBnbWFpbC5jbyIsInJvbGUiOiJ1c2VyIiwiam9iUm9sZSI6IiIsInZpZGVvR29hbCI6IiIsInByb2ZpbGVQaWN0dXJlIjoiIiwiYXBwcm92YWwiOnsiaXNBcHByb3ZlZCI6dHJ1ZSwidHJ4SWQiOiIifSwiYWNjZXNzVHlwZSI6eyJicmFuZGluZzEiOnRydWUsImJyYW5kaW5nMiI6dHJ1ZSwiYnJhbmRpbmczIjp0cnVlLCJicmFuZGluZzQiOnRydWUsInNjcmlwdCI6dHJ1ZSwidGVtcGxhdGUiOnRydWUsImZ1bGxBY2Nlc3MiOnRydWV9LCJyZXNldFBhc3NUb2tlbiI6IiIsImJyYW5kaW5nIjp7ImJyYW5kaW5nMSI6IjYxN2MxYTM3YTBiMDMyMTE3MTUzZDQ3NCIsImJyYW5kaW5nMiI6IjYxN2MxYTM3YTBiMDMyMTE3MTUzZDQ3NSIsImJyYW5kaW5nMyI6IjYxN2MxYTM3YTBiMDMyMTE3MTUzZDQ3NiIsI mJyYW5kaW5nNCI6IjYxN2MxYTM3YTBiMDMyMTE3MTUzZDQ3NyJ9LCJzdWJfc3RhdHVzIjoyfSwaWF0IjoxNjM2MjkxNTk0LCJleHAiOjE2Mzg4ODM1OTR9.ZCnnNZ2a7aeO6GCnyhnWYGMX-4kkJX75ZyzG52lSiWxUBIawXrA37882HFwo_3r6-I3JrrYNv270vxfsMwyBlw 令牌机密 jdgfaiodsugfaileufliaeruy


B) 当没有错误时,这些是:


需要登录:令牌是eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MTdjMWEzN2EwYjAzMjExNzE1M2Q0NzMiLCJyb2xlIjoidXNlciIsImlhdCI6MTYzNjI5MTYyNSwiZXhwIjoxNjM2Mzc4MDI1fQ.K-inQfI77mepKjkG_5g4obr3sfcVnDwCOXeQlDPra00


登录:

jdgfaiodsugfaileufliaeruy eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MTdjMWEzN2EwYjAzMjExNzE1M2Q0NzMiLCJyb2xlIjoidXNlciIsImlhdCI6MTYzNjI5MTYyNSwiZXhwIjoxNjM2Mzc4MDI1fQ.K-inQfI77mepKjkG_5g4obr3sfcVnDwCOXeQlDPra00617c1a37a0b032117153d473 用户

我的想法

我觉得这只是我没有注意到的简单问题,我已经尝试修复它 9 个小时, 但我还没有确定是什么。

伙计们,请帮帮我!

非常感谢任何帮助或提示!

在此先感谢您!

感谢所有帮助过我的人! @Bergi 和@eol!

这个问题是 nextauthjs 特有的,并没有像它出现的那样关注 expressjs。

所以我再也没有收到 nextauthjs 返回的令牌。

相反,我从存储在会话中的用户对象的 authToken 属性 获取令牌。


所以就像:

MyApp.getInitialProps = async ({ router, ctx }) => {
  const session = await getSession(ctx);
  if (router.asPath === '/login' || router.asPath === '/register') {
    return { session: session };
  }
  if (!session || !session.user) {
    ctx.res.writeHead(302, {
      Location: '/login',
    });
    ctx.res.end();
  }
  console.log('Access token', session.accessToken);
  axios.defaults.headers.common = {
    authorization: `${session.accessToken}`,
  };
  return { token: `${session.accessToken}`, session: session };
};