Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Secret callback revisited #480

Merged
merged 11 commits into from Jun 11, 2018
Merged

Secret callback revisited #480

merged 11 commits into from Jun 11, 2018

Conversation

JacoKoster
Copy link
Contributor

@JacoKoster JacoKoster commented Jun 3, 2018

Based on PR 413, without breaking existing functionality.

Added features

  • verify's secretOrToken parameter can now be a callback function, see updated README.md for details
  • updated tests and readme

Performance improvements

  • merged two decodes into one

Dependencies

  • Removed xtend and use Object.assign instead

See issue 406

Without the more contentious 'none'-changes
I should really add a editor.config and eslint to this project ;-)
@JacoKoster JacoKoster changed the title Add secret for callback Secret callback revisited Jun 3, 2018
@@ -59,23 +59,9 @@ module.exports = function (jwtString, secretOrPublicKey, options, callback) {
return done(new JsonWebTokenError('secret or public key must be provided'));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should move these ^ asserts inside the getSecrect callback too, since you could have secretOrPublicKey here but it could be a function that returns value / no-value later on. I mean:

   var hasSignature = parts[2].trim() !== '';
 	 
   if (!hasSignature && secretOrPublicKey){
     return done(new JsonWebTokenError('jwt signature is required'));
   }
 	 
   if (hasSignature && !secretOrPublicKey) {
     return done(new JsonWebTokenError('secret or public key must be provided'));
   }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do believe, as the other PR-owner, that the jws-package already handles these cases. But will move them anyway ;-)

verify.js Outdated
var payload;

try {
payload = decode(jwtString);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 wasn't this the decode you wanted to clean in favor of the decode(..., { complete: true }) call you add at the beginning of the verify() function?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI Not sure why but in github if I see the diff omitting whitespaces it shows me you removed this, however, in the normal view or even if I click in View button for this file I can still see this decode call

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, should be okay now :)

README.md Outdated
@@ -197,6 +198,17 @@ jwt.verify(token, cert, { algorithms: ['RS256'] }, function (err, payload) {
// if token alg != RS256, err == invalid signature
});

// Verify using getKey callback
function getKey(header, callback) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to use this function for JWK + openid cases we would need to add the issuer as well.

The options are:

  • We add it here like getKey(header, issuer, callback), and think if we want to check the issuer, if passed in options, before even calling the getSecret function.
  • Leave it as is in this PR. Give support for that in a different one by checking the length of getKey function (to see if the caller expects only header and callback or header, issuer and callback)

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the sake of progress, i would like to keep it as is. Let's make an separate issue for it and i will pick it up after this one ;-)

README.md Outdated
@@ -29,6 +29,7 @@ $ npm install jsonwebtoken

`secretOrPrivateKey` is a string, buffer, or object containing either the secret for HMAC algorithms or the PEM
encoded private key for RSA and ECDSA. In case of a private key with passphrase an object `{ key, passphrase }` can be used (based on [crypto documentation](https://nodejs.org/api/crypto.html#crypto_sign_sign_private_key_output_format)), in this case be sure you pass the `algorithm` option.
If `jwt.verify` is called asynchronous, `secretOrPublicKey` can be a function that should fetch the secret or public key. See below for a detailed example
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is in wrong place, right? it should be under jwt.verify section, where we should indicate secretOrPublicKey can be a function too.

verify.js Outdated

if (parts.length !== 3){
return done(new JsonWebTokenError('jwt malformed'));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be kept here, it refers to the token format, not secret related, there is no need to potentially call the getSecret function if the jwt is a malformed

verify.js Outdated
} catch (err) {
return done(err);
}
var payload = decodedToken.payload;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️


it('simple callback', function (done) {
var keyFunc = function(header, callback) {
callback(undefined, key);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just saw that even when you are passing the decodedToken the tests pass. Can you assert here you get just the content of the header from the token?

verify.js Outdated
}

return done(null, payload);
return getSecret(decodedToken, function(err, secretOrPublicKey) {
Copy link
Contributor

@ziluvatar ziluvatar Jun 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 why do you pass all the decoded token now?, I think it was fine passing just the decodedToken.header, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we will be using both the header and the payload of the token now

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, wait, yes. that is wrong.

verify.js Outdated

return done(null, payload);
else {
getSecret = function(decodedToken, secretCallback) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

even if it is not used, can you rename it back to header as well?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JacoKoster I saw your last commit after this comment, but that commit does not address this (not sure if you realized about it)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes, i forgot this one.

@JacoKoster JacoKoster mentioned this pull request Jun 8, 2018
README.md Outdated
callback(null, key);
}

verify(token, getKey, options, function(err, decoded) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jwt.verify

README.md Outdated
@@ -197,6 +198,17 @@ jwt.verify(token, cert, { algorithms: ['RS256'] }, function (err, payload) {
// if token alg != RS256, err == invalid signature
});

// Verify using getKey callback
function getKey(header, callback) {
// fetch secret or public key

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd add some comment encouraging the users to cache the key with the kid so they avoid fetching it multiple times.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add https://github.com/auth0/node-jwks-rsa as an example to load the keys, that should make it more clear.

verify.js Outdated
return done(e);
}

if (!valid)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please be consistent on the braces usage. A few lines above this one, another one liner if clause uses them.

Copy link
Contributor Author

@JacoKoster JacoKoster Jun 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noticed this as well, but because it was already on master this way. I was planning to add eslint and fixing it in one go.

verify.js Outdated
});
});

if (!match)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

braces

try {
decodedToken = jws.decode(jwtString);
decodedToken = decode(jwtString, { complete: true });

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this {complete:true} option wasn't being passed before. How does that affect the output @ziluvatar ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's mostly the same, however, our decode implementation tries to parse always the JSON (if string), the jws.decode only does it when the type is JWT. With this and other change afterwards we reduce the amount of parsing to half.

verify.js Outdated
};
}

return getSecret(header, function(err, secretOrPublicKey) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't enough with the kid? What's on the header that the user might want to use for key retrieval?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may want to use other headers to find the key: https://tools.ietf.org/html/rfc7515#page-10

@lbalmaceda
Copy link

LGTM 👨‍🎓

Copy link
Contributor

@ziluvatar ziluvatar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will release this on Monday as a new minor version. (I don't want to publish anything on Friday :) )

@@ -67,6 +69,90 @@ describe('verify', function() {
});
});

describe('secret or token as callback', function () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is not properly indented with the rest of the file and doesn't follow the newly introduced .editorconfig

Copy link
Contributor Author

@JacoKoster JacoKoster Jun 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to make this a thing with #486

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @jfromaniello on this one. Since the bad indentation is introduced in this PR there's no need to wait for a different one to fix it.

@ziluvatar ziluvatar merged commit d01cc7b into auth0:master Jun 11, 2018
@ziluvatar
Copy link
Contributor

🎉 Released on v8.3.0!! Awesome work @JacoKoster 👏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants