Skip to content

Commit a236e40

Browse files
committedJul 28, 2017
Cleanup of PR #108.
Moves the logic that calls secretOrKeyProvider out of verify_jwt.js and back into lib/strategy.js. verify_jwt.js was intended to be a simple call-through function for the real verfier from jsonwebtoken. Changes the signature of secretOrKeyProvider to (request, token, done) from (token, done) to support a wider range of use cases. Updates tests for the above behavior.
1 parent b3846ef commit a236e40

File tree

4 files changed

+107
-57
lines changed

4 files changed

+107
-57
lines changed
 

‎lib/strategy.js

+36-25
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ var passport = require('passport-strategy')
1010
* Strategy constructor
1111
*
1212
* @param options
13-
* secretOrKey: (REQUIRED) String or buffer containing the secret or PEM-encoded public key
13+
* secretOrKey: String or buffer containing the secret or PEM-encoded public key. Required unless secretOrKeyProvider is provided.
14+
* secretOrKeyProvider: callback in the format secretOrKeyProvider(request, token, done)`,
15+
* which should call done with a secret or PEM-encoded public key
16+
* (asymmetric) for the given token and request combination. done
17+
* has the signature function done(err, secret).
18+
* REQUIRED unless `secretOrKey` is provided.
1419
* jwtFromRequest: (REQUIRED) Function that accepts a reqeust as the only parameter and returns the either JWT as a string or null
1520
* issuer: If defined issuer will be verified against this value
1621
* audience: If defined audience will be verified against this value
@@ -31,8 +36,8 @@ function JwtStrategy(options, verify) {
3136
if (this._secretOrKeyProvider) {
3237
throw new TypeError('JwtStrategy has been given both a secretOrKey and a secretOrKeyProvider');
3338
}
34-
this._secretOrKeyProvider = function (token, done) {
35-
done(options.secretOrKey)
39+
this._secretOrKeyProvider = function (request, token, done) {
40+
done(null, options.secretOrKey)
3641
};
3742
}
3843

@@ -91,31 +96,37 @@ JwtStrategy.prototype.authenticate = function(req, options) {
9196
return self.fail(new Error("No auth token"));
9297
}
9398

94-
// Verify the JWT
95-
JwtStrategy.JwtVerifier(token, this._secretOrKeyProvider, this._verifOpts, function(jwt_err, payload) {
96-
if (jwt_err) {
97-
return self.fail(jwt_err);
99+
this._secretOrKeyProvider(req, token, function(secretOrKeyError, secretOrKey) {
100+
if (secretOrKeyError) {
101+
self.fail(secretOrKeyError)
98102
} else {
99-
// Pass the parsed token to the user
100-
var verified = function(err, user, info) {
101-
if(err) {
102-
return self.error(err);
103-
} else if (!user) {
104-
return self.fail(info);
103+
// Verify the JWT
104+
JwtStrategy.JwtVerifier(token, secretOrKey, self._verifOpts, function(jwt_err, payload) {
105+
if (jwt_err) {
106+
return self.fail(jwt_err);
105107
} else {
106-
return self.success(user, info);
108+
// Pass the parsed token to the user
109+
var verified = function(err, user, info) {
110+
if(err) {
111+
return self.error(err);
112+
} else if (!user) {
113+
return self.fail(info);
114+
} else {
115+
return self.success(user, info);
116+
}
117+
};
118+
119+
try {
120+
if (self._passReqToCallback) {
121+
self._verify(req, payload, verified);
122+
} else {
123+
self._verify(payload, verified);
124+
}
125+
} catch(ex) {
126+
self.error(ex);
127+
}
107128
}
108-
};
109-
110-
try {
111-
if (self._passReqToCallback) {
112-
self._verify(req, payload, verified);
113-
} else {
114-
self._verify(payload, verified);
115-
}
116-
} catch(ex) {
117-
self.error(ex);
118-
}
129+
});
119130
}
120131
});
121132
};

‎lib/verify_jwt.js

+2-11
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,5 @@
11
var jwt = require('jsonwebtoken');
22

3-
module.exports = function(token, secretOrKeyProvider, options, callback, verify) {
4-
if (!verify) {
5-
verify = jwt.verify;
6-
}
7-
return secretOrKeyProvider(token, function (err, secretOrKey) {
8-
if (err) {
9-
callback(err);
10-
return;
11-
}
12-
verify(token, secretOrKey, options, callback);
13-
});
3+
module.exports = function(token, secretOrKey, options, callback) {
4+
return jwt.verify(token, secretOrKey, options, callback);
145
};

‎test/strategy-validation-test.js

+3-5
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,11 @@ describe('Strategy', function() {
3939
});
4040

4141

42-
it('should be given a function that calls done with the secretOrKey when passed the token', function() {
43-
const secretOrKeyProvider = Strategy.JwtVerifier.args[0][1];
44-
const doneSpy = sinon.spy();
45-
secretOrKeyProvider(null, doneSpy);
46-
expect(doneSpy.calledWith('secret')).to.be.true;
42+
it('should call with the right secret as an argument', function() {
43+
expect(Strategy.JwtVerifier.args[0][1]).to.equal('secret');
4744
});
4845

46+
4947
it('should call with the right issuer option', function() {
5048
expect(Strategy.JwtVerifier.args[0][2]).to.be.an.object;
5149
expect(Strategy.JwtVerifier.args[0][2].issuer).to.equal('TestIssuer');

‎test/strategy-verify-test.js

+66-16
Original file line numberDiff line numberDiff line change
@@ -170,26 +170,76 @@ describe('Strategy', function() {
170170

171171
});
172172

173-
describe('verify function', function() {
174-
it('should call the secretOrKeyProvider with the token and pass it to verify', function() {
175-
const provider = sinon.spy(function(token, done) {
176-
done(null, 'secret');
173+
174+
describe('handling a request when constructed with a secretOrKeyProvider function that succeeds', function() {
175+
176+
var strategy, fakeSecretOrKeyProvider, expectedReqeust;
177+
178+
before(function(done) {
179+
fakeSecretOrKeyProvider = sinon.spy(function(request, token, done) {
180+
done(null, 'secret from callback');
177181
});
178-
const verifyStub = sinon.stub();
179-
verify(test_data.valid_jwt.payload, provider, null, null, verifyStub);
180-
expect(provider.calledOnce).to.be.true;
181-
expect(provider.calledWith(test_data.valid_jwt.payload)).to.be.true;
182-
expect(verifyStub.calledWith(test_data.valid_jwt.payload, 'secret')).to.be.true;
182+
opts = {
183+
secretOrKeyProvider: fakeSecretOrKeyProvider,
184+
jwtFromRequest: function(request) {
185+
return 'an undecoded jwt string';
186+
}
187+
}
188+
strategy = new Strategy(opts, function(jwtPayload, next) {
189+
return next(null, {user_id: 'dont care'}, {});
190+
});
191+
192+
chai.passport.use(strategy)
193+
.success(function(u, i) {
194+
done();
195+
})
196+
.req(function(req) {
197+
expectedReqeust = req;
198+
})
199+
.authenticate();
200+
});
201+
202+
it('should call the fake secret or key provider with the reqeust', function() {
203+
expect(fakeSecretOrKeyProvider.calledWith(expectedReqeust, sinon.match.any, sinon.match.any)).to.be.true;
204+
});
205+
206+
it('should call the secretOrKeyProvider with the undecoded jwt', function() {
207+
expect(fakeSecretOrKeyProvider.calledWith(sinon.match.any, 'an undecoded jwt string', sinon.match.any)).to.be.true;
183208
});
184209

185-
it('should call the callback with an error if the secretOrKeyProvider fails to return a key', function() {
186-
const providerStub = function(token, done) {
187-
done(new Error('invalid key'));
188-
};
189-
const callback = sinon.spy();
190-
verify(test_data.valid_jwt.payload, providerStub, null, callback);
191-
expect(callback.calledWith(sinon.match.instanceOf(Error)));
210+
it('should call JwtVerifier with the value returned from secretOrKeyProvider', function() {
211+
expect(Strategy.JwtVerifier.calledWith(sinon.match.any, 'secret from callback', sinon.match.any, sinon.match.any)).to.be.true;
192212
});
193213
});
194214

215+
216+
describe('handling a request when constructed with a secretOrKeyProvider function that errors', function() {
217+
var errorMessage;
218+
219+
before(function(done) {
220+
fakeSecretOrKeyProvider = sinon.spy(function(request, token, done) {
221+
done('Error occurred looking for the secret');
222+
});
223+
opts = {
224+
secretOrKeyProvider: fakeSecretOrKeyProvider,
225+
jwtFromRequest: function(request) {
226+
return 'an undecoded jwt string';
227+
}
228+
}
229+
strategy = new Strategy(opts, function(jwtPayload, next) {
230+
return next(null, {user_id: 'dont care'}, {});
231+
});
232+
233+
chai.passport.use(strategy)
234+
.fail(function(i) {
235+
errorMessage = i;
236+
done();
237+
})
238+
.authenticate();
239+
});
240+
241+
it('should fail with the error message from the secretOrKeyProvider', function() {
242+
expect(errorMessage).to.equal('Error occurred looking for the secret');
243+
});
244+
});
195245
});

0 commit comments

Comments
 (0)
Please sign in to comment.