UnhandledPromiseRejectionWarning in mocha tests for modified passport-jwt

  javascript, mocha.js, passport.js

I’m currently working on a fork of passport-jwt which accepts multiple jwks providers in an array.

It is actually working well, but I’m having an issue with automated testing, using the existing tests for passport-jwt as it should be fully backwards compatible.

My repository is here: https://github.com/MeStrak/passport-jwt/tree/9ab3f2d8e99079160ff1a56fd30078c1ec337dee (the link is to the current repo version for future reference and because I’m still refactoring some parts of the new code).

The basic implementation changes compared to passport-jwt are:

  • The constructor accepts an array of configurations, and puts them into a _config array instead of storing a single configuration directly on self
  • The authenticate function implements async.each() to process all configurations in parallel, returning success immediately if there is one, or returning all errors/fails at the end of processing.

Now when running npm test I have 54 passes and 3 fails. All of the fails are of the same nature, like the example below:

handling failing jwt
(node:9620) UnhandledPromiseRejectionWarning: Error: Strategy#error should not be called
    at JwtStrategy.strategy.error (/workspace/passport-jwt/node_modules/chai-passport-strategy/lib/test.js:124:33)
    at /workspace/passport-jwt/lib/strategy.js:131:30

Followed by this output at the end:

  1) Strategy
       handling failing jwt
         "before all" hook for "should not call verify":
     Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/workspace/passport-jwt/test/strategy-validation-test.js)
      at listOnTimeout (internal/timers.js:554:17)
      at processTimers (internal/timers.js:497:7)

The test throwing that error passes fine on the unmodified code, so the issue is definitely related to my implementation:

    describe('handling failing jwt', function() {
        var strategy, info;
        var verify_spy = sinon.spy();

        before(function(done) {

            strategy = new Strategy({jwtFromRequest: extract_jwt.fromAuthHeaderAsBearerToken(), secretOrKey: 'secret'}, verify_spy);

            // Mock errored verification
            Strategy.JwtVerifier = sinon.stub();
            Strategy.JwtVerifier.callsArgWith(3, new Error("jwt expired"), false);

            chai.passport.use(strategy)
                .fail(function(i) {
                    info = i;
                    done();
                })
                .req(function(req) {
                    req.headers['authorization'] = "bearer " + test_data.valid_jwt.token;
                })
                .authenticate();
        });


        it('should not call verify', function() {
            sinon.assert.notCalled(verify_spy);
        });


        it('should fail with error message.', function() {
            expect(info).to.be.an.object;
            expect(info.message).to.equal('jwt expired');
        });

    });

The problem is in the areas of code where self.error or self.fail are called directly.

For example https://github.com/MeStrak/passport-jwt/blob/9ab3f2d8e99079160ff1a56fd30078c1ec337dee/lib/strategy.js#L118:

            if (secretOrKeyError) {
                errors.push(secretOrKeyError)
                if (fails.length + errors.length == self._config.length) {
                    if (self._config.length == 1) {
                        errors.length > 0 ? self.error(errors[0]) : self.fail(fails[0]);
                        
                    }

                    const allErrors = errors.concat(fails);
                    if (errors.length > 0) {
                        self.error(allErrors);
                    }
                    else {
                        self.fail(allErrors);
                    }
                }

However in the section of the code where _verify is called (https://github.com/MeStrak/passport-jwt/blob/9ab3f2d8e99079160ff1a56fd30078c1ec337dee/lib/strategy.js#L186), the same error does not occur.

The issue now is that I do not know what to look for or how to debug this further. I thought that the change I made would be backwards compatible so all test cases should pass, but I guess that there is something about the behaviour of async.each that I don’t understand fully.

Any tips would be appreciated!

Source: Ask Javascript Questions

LEAVE A COMMENT