带有护照 jwt 的非对称密钥。验证总是返回 Unauthorized

Posted

技术标签:

【中文标题】带有护照 jwt 的非对称密钥。验证总是返回 Unauthorized【英文标题】:Asymmetric keys with passport jwt. Verify always returns Unauthorized 【发布时间】:2019-07-27 11:23:17 【问题描述】:

在开发应用程序时,我希望从一开始就获得安全性,因此我创建了一个私钥/公钥对,并且我正在设置 passport-jwt,如下所示:(key 是密钥对)

(passport, key) => 
  const opts = 
    jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
    secretOrKey: key
  ;
   passport.use(
     new JwtStrategy(opts, (payload, done) => 
       log.info(message: 'verifying the token', payload);
       User.findById(payload.id)
         .then(user => 
           if (user) 
             return done(null, 
               id: user._id,
               name: user.userName,
               email: user.emailAddress
             );
           
           log.info(payload);
           return done(null, false);
         )
         .catch(err => 
           log.error(err)
           return done('Unauthorized', false, payload);
          );
     )
   );
;

当用户登录时,我使用私钥对令牌进行签名,如下所示:

router.post('/login', (req, res) => 
        const email = req.body.email;
        const password = req.body.password;

        User.findOne( email ).then(user => 
            if (!user) 
                errors.email = 'No Account Found';
                return res.status(404).json(errors);
            

            bcrypt.compare(password, user.password).then(isMatch => 
                if (isMatch) 
                    const payload = 
                        id: user._id,
                        name: user.userName,
                        email: user.emailAddress
                    ;
                    log.info(payload);
                    jwt.sign(payload, private,  expiresIn: 30000000 , (err, token) => 
                        if (err)
                            res.status(500).json( error: 'Error signing token', raw: err );
                        // const refresh = uuid.v4();
                        res.json( success: true, token: `Bearer $token` );
                    );
                 else 
                    errors.password = 'Password is incorrect';
                    res.status(400).json(errors);
                
            );
        );
    );

我认为我可能缺少某些东西,但我不确定它可能是什么。

此外,我也在初始化时使用以下代码在应用内生成密钥。

const ensureKeys = () => 
    return new Promise((resolve, reject) => 
        ensureFolder('./keys').then(() => 
            /**
             * Ensure that both the private and public keys
             * are created, and if not create them both.
             * Never generate just a single key.
             */
            try 
                if (
                    !fs.existsSync('./keys/private.key') &&
                    !fs.existsSync('./keys/public.key')
                ) 
                    log.info('Keys do not exist. Creating them.');
                    diffHell.generateKeys('base64');
                    const public = diffHell.getPublicKey('base64');
                    const private = diffHell.getPrivateKey('base64');
                    fs.writeFileSync('./keys/public.key', public);
                    fs.writeFileSync('./keys/private.key', private);
                    log.info('keys created and being served to the app.');
                    resolve( private, public );
                 else 
                    log.info('keys are already generated. Loading from key files.');
                    const public = fs.readFileSync('./keys/public.key');
                    const private = fs.readFileSync('./keys/private.key');
                    log.info('keys loaded from files. Serving to the rest of the app.');
                    resolve( private, public );
                
             catch (e) 
                log.error('issue loading or generating keys. Sorry.', e);
                reject(e);
            
        );
    );
;

【问题讨论】:

【参考方案1】:

很好,这对我有帮助,谢谢。

如果它对其他人有帮助,我不必使用该库 - 找到了一个链接,该链接解释了如何将公钥转换为 PEM 格式,这似乎有效(私钥已经采用正确的格式)

ssh-keygen -f id_rsa.pub -m 'PEM' -e > id_rsa.pem

My question

【讨论】:

【参考方案2】:

好的,所以问题有两个方面。首先,我为护照错误地生成了密钥。根据passport-jwt、documentation 的文档,密钥必须以 PEM 格式编码,根据Medium 上的这篇文章,还需要对护照和 JWT 进行更多配置。

最终的解决方案包括使用 npm 上提供的 keypair 库。

这里是用于制作工作结果代码的修改。

const keypair = require('keypair');
const ensureKeys = () => 
    return new Promise((resolve, reject) => 
        ensureFolder('./keys').then(() => 
            /**
             * Ensure that both the private and public keys
             * are created, and if not create them both.
             * Never generate just a single key.
             */
            try 
                if (
                    !fs.existsSync('./keys/private.key') &&
                    !fs.existsSync('./keys/public.key')
                ) 
                    log.info('Keys do not exist. Creating them.');
                    const pair = keypair();
                    fs.writeFileSync('./keys/public.key', pair.public);
                    fs.writeFileSync('./keys/private.key', pair.private);
                    log.info('keys created and being served to the app.');
                    resolve( private: pair.private,public: pair.public );
                 else 
                    log.info('keys are already generated. Loading from key files.');
                    const public = fs.readFileSync('./keys/public.key', 'utf8');
                    const private = fs.readFileSync('./keys/private.key', 'utf8');
                    log.info('keys loaded from files. Serving to the rest of the app.');
                    resolve( private, public );
                
             catch (e) 
                log.error('issue loading or generating keys. Sorry.', e);
                reject(e);
            
        );
    );
;

密钥是用永远不会被共享的私钥签名的。

    router.post('/login', (req, res) => 
        const  errors, isValid  = require('../validation/user').loginUser(
            req.body
        );
        if (!isValid) 
            return res.status(400).json(errors);
        
        const email = req.body.email;
        const password = req.body.password;

        User.findOne( email ).then(user => 
            if (!user) 
                errors.email = 'No Account Found';
                return res.status(404).json(errors);
            

            bcrypt.compare(password, user.password).then(isMatch => 
                if (isMatch) 
                    const payload = 
                        id: user._id,
                        name: user.userName,
                        email: user.emailAddress
                    ;
                    log.info(payload);
                    jwt.sign(payload, private,  
                        expiresIn: 30000000,
                        subject: user.emailAddress,
                        algorithm: 'RS256'
                     , (err, token) => 
                        if (err)
                            res.status(500).json( error: 'Error signing token', raw: err );
                        res.json( success: true, token: `Bearer $token` );
                    );
                 else 
                    errors.password = 'Password is incorrect';
                    res.status(400).json(errors);
                
            );
        );

及验证功能:

  const opts = 
    jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('Bearer'),
    secretOrKey: key,
    algorithm: ["RS256"]
  ;
passport.use(
     new JwtStrategy(opts, (payload, done) => 
       log.info(message: 'verifying the token', payload);
       User.findById(payload.id)
         .then(user => 
           if (user) 
             return done(null, 
               id: user._id,
               name: user.userName,
               email: user.emailAddress
             );
           
           log.info(payload);
           return done(null, false);
         )
         .catch(err => 
           log.error(err)
           return done('Unauthorized', false, payload);
          );
     )
   );

我希望这对将来希望使用非对称密钥的人有所帮助。

【讨论】:

我没有遇到这个问题,因为我找不到有关如何实际执行非对称密钥的资源。感谢您发布!

以上是关于带有护照 jwt 的非对称密钥。验证总是返回 Unauthorized的主要内容,如果未能解决你的问题,请参考以下文章

护照-jwt 总是返回“未经授权” - 401

JWT 密钥 - 非对称和对称

Laravel 护照 oauth 路线总是返回 401 未经授权

与对称密钥相比,用对称密钥签名JWT会更有用吗?

验证使用 JSON Web 加密 (JWE) 加密的安全令牌的颁发者?

如何在 dotnet core 中验证非对称签名的 JWT?