Асимметричные ключи с паспортом JWT. Verify всегда возвращает неавторизованный - PullRequest
0 голосов
/ 06 марта 2019

Работая над приложением, и я хочу с самого начала обеспечить безопасность, поэтому я создал пару секретный / открытый ключ и настраиваю passport-jwt следующим образом: (key является открытой частьюkeypair)

(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 Ответ

0 голосов
/ 06 марта 2019

Хорошо, проблема была двоякой.Во-первых, я неправильно генерировал ключи для паспорта.В соответствии с документацией для passport-jwt, документации ключи должны быть закодированы в формате PEM, и в соответствии с этим постом на Средний требуется дополнительная конфигурация для паспорта и JWT.

Окончательное решение включало использование библиотеки keypair, доступной по npm.

Вот модификации, используемые для создания работающего результирующего кода.

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);
          });
     })
   );

Надеюсь, это поможетлюбой, кто хочет использовать асимметричные ключи в будущем.

...