-
Notifications
You must be signed in to change notification settings - Fork 0
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
init #2
init #2
Conversation
6756c2e
to
e221f93
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great! :)
I'll get into reviewing the code once we have settled all meta issues.
@@ -0,0 +1,10 @@ | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should add "root": true
to prevent extension of unrelated configs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
.eslintrc.json
Outdated
{ | ||
"extends": ["warp/node", "warp/es6"], | ||
"env": { | ||
"jest": true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's appropriate to state that all code is run with jest
. We should move this statement to test/.eslintrc.json
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are correct, however, I would suggest having one eslintrc and use glob patterns instead so we don't need to write one config file per mocks and tests folders. Check the comment about jest's mocks folder location requirements. What do you think?
.eslintrc.json
Outdated
"extends": ["warp/node", "warp/es6"], | ||
"env": { | ||
"jest": true, | ||
"mongo": true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does the mongo environment add?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing at all, I forgot to remove it :)
.gitignore
Outdated
@@ -57,3 +57,5 @@ typings/ | |||
# dotenv environment variables file | |||
.env | |||
|
|||
# .idea | |||
.idea |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should clean up the .gitignore
to:
- contain only files and directories specific to this project (eg build or installer output); and
- use absolute paths by default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
__mocks__/jsonwebtoken.js
Outdated
|
||
exports.sign = jest.fn(() => 'I am a token'); | ||
|
||
exports.decode = jest.fn(() => ({userId: 123})); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we move the mocks directory under test
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Jest requires the mocks to exist in the same level as the modules to be mocked. Docs. But I see your point, having a mocks directory beside every mocked file might get messy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we can avoid this kind of mocking altogether by using dependency injection (as we discussed offline).
index.js
Outdated
@@ -0,0 +1,3 @@ | |||
'use strict'; | |||
|
|||
module.exports = require('./jwtPlus'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just have everything in this file? We can always change it once we need to split things up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
package-lock.json
Outdated
"dev": true | ||
}, | ||
"semver": { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using a package lock is not appropriate for libraries. Let's remove it and add ./.npmrc
containing package-lock = false
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
package.json
Outdated
@@ -0,0 +1,28 @@ | |||
{ | |||
"name": "jwt-plus", | |||
"version": "1.0.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's start at version 0.0.0 and increment once we publish. We can use https://github.com/davidchambers/xyz for automating this process. It's very nice and I use it on all my libraries.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
package.json
Outdated
{ | ||
"name": "jwt-plus", | ||
"version": "1.0.0", | ||
"description": "", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's think of a description.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
index.js
Outdated
|
||
set algorithm(v) { | ||
assert(v !== 'none', 'Algorithm none is not accepted'); | ||
this[_algorithm] = v; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interesting ) What benefit does using Symbol here bring?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes the property "private by obscurity".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, so you can never bypass the set function, nice
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can bypass it, it just takes more effort. It's the same principle as doing this._____________algorithm = ...
, except that it's "more" obscure.
I discussed offline with Amri the possibility of using a closure to capture private properties, eg:
function JWTPlus({store, algorithm = 'HS512', expiresIn = regularTokenLife, jwt}) {
return {
setAlgorithm(x){ assert(v !== 'none', 'Algorithm none is not accepted'); algorithm = x }
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm, from what I read Symbol would create a unique value, which you can not know if its not on the scope. How can you bypass it?
const s1 = Symbol('s');
const s2 = Symbol('s');
console.log(s1 === s2);//false
const a = {};
a[s1] = 5;
console.log(Object.keys(a));//[]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const hasProp = Object.prototype.hasOwnProperty;
const specialGet = (prop, object) => {
if(hasProp.call(object, prop)) return object[prop];
const symbols = Object.getOwnPropertySymbols(object);
return symbols
.filter(x => x.toString().slice('Symbol('.length, -1) === prop)
.map(x => object[x])
[0];
};
const o = {
'regular': 'public bar',
[Symbol('special')]: 'private bar'
};
console.log(specialGet('regular', o))
console.log(specialGet('special', o))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
haha, well for people ready to go all this way to modify a private property, I mean just let them do it..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think simply not documenting the property name is obscure enough. If you want a private variable, you can use a closure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One of the major issues with JWT is its low misuse-resistance and this library tries amend that. If a user wants to get the symbol property that bad, I think that level of misuse-resistance is outside the scope.
However, I do see your point, it makes sense to use an _, a meant-to-be-private variable with the appropriate naming convention might be sufficiently misuse-resistant. I'll remove the use of Symbols and use _ instead.
Further more, Google JavaScript Style Guide warns against using Symbol and promotes the use of an _ along with an annotation for private fields.
index.js
Outdated
const _algorithm = Symbol('_algorithm'); | ||
const _expiresIn = Symbol('_expiresIn'); | ||
|
||
const Payload = t.struct({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to use interfaces (t.inter
) over structs in most cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
.gitignore
Outdated
@@ -57,3 +57,5 @@ typings/ | |||
# dotenv environment variables file | |||
.env | |||
|
|||
# package-lock | |||
package-lock.json |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's replace all of the contents in this file by:
/node_modules/
All the other stuff is either environment specific (so belongs in ~/.gitignore
) or not related to this project (such as bower_components
, what's that doing here).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
index.js
Outdated
} | ||
get expiresIn() { | ||
return this._expiresIn; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These two pairs of getters and setters are overwritten by plain values upon object construction. They are essentially unreachable code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, never mind 😳
index.js
Outdated
}, 'Payload'); | ||
|
||
// Todo: pending more secret-related opinions | ||
const Secret = t.refinement(t.String, s => s.length >= 20); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could add a name to this refinement for clearer error messages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
index.js
Outdated
}); | ||
|
||
class JWTPlus { | ||
// Todo: select default algorithm |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this TODO for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, I am using HS512 as the default algorithm, I need to read more about recommended jwt algorithms, perhaps I should create an issue for this as well.
index.js
Outdated
userId: t.Any | ||
}, 'Payload'); | ||
|
||
// Todo: pending more secret-related opinions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's remove this comment, and if it's still relevant, create a github issue instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
index.js
Outdated
const refreshToken = generator.generate(256); | ||
await this.store.registerTokens(userId, refreshToken, accessToken, ttl); | ||
return refreshToken; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing newline
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
index.js
Outdated
// Todo: select default algorithm | ||
/** | ||
* Constructor | ||
* @param {Object} store |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the purpose of these comments? Are you going to generate documentation from them? If so, let's at least create an issue for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I am going to generate documentation from these comments, I'll add an issue. 👍
index.js
Outdated
}); | ||
const ttl = getTTL(rememberMe); | ||
return getTokensObj(token, | ||
ms(this.expiresIn), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we represent TTL as strings?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, this library accepts strings and numbers for this.expiresIn, I think strings are more readable, i.e. '1h' than the numbers, but the user can pass either. Is that what you meant?
I also refactored a bit so that we accept string and number formats but always use numbers internally. I will push soon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed offline
index.js
Outdated
* @param {Object} [verifyOptions] Options to pass to jwt.verify. | ||
* @returns {Promise<*>} | ||
*/ | ||
verify(token, secret, verifyOptions) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it necessary to pass down these options? Or can we provide our own?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about asking the user to provide them once when calling the constructor, but I think these options may change between jwt method calls. I decided to give the user the option to pass them. Do you believe it is better not to?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed offline
index.js
Outdated
* @returns {Promise<*>} | ||
*/ | ||
async refresh(refreshToken, oldToken, secret, rememberMe, signOptions) { | ||
Secret(secret); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could also type-check the other arguments, such as oldToken
- even if you're just checking whether they are Strings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
index.js
Outdated
* @param {Object} [signOptions] Options passed to jwt.sign | ||
* @returns {Promise<*>} | ||
*/ | ||
async refresh(refreshToken, oldToken, secret, rememberMe, signOptions) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think taking rememberMe
here makes for a good API. It's puts some party (the front-end or back-end) in charge of remembering whether the user selected "remember me" when they logged in. Perhaps it's better to keep this information in the token, and just carry it forward.
Do we have to pass along the signOptions, or can we provide our own?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a great idea. 👍
index.js
Outdated
// Otherwise, put value in the options | ||
options[optionMapping[key]] = inputToken[key]; | ||
} | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You will have to explain why all this is necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add it in the documentations. But the idea is that some options are already stored in the token (we can also use this for rememberMe), so the refresher does not have to do any processing to generate the options again and the new token will almost match the old one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It turned out that it is not :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks good to me. Let's squash the commits. :)
Fixes #7 Errors thrown now has the same structure as the ones thrown by jsonwebtoken (the default library used) |
Codecov Report
@@ Coverage Diff @@
## master #2 +/- ##
=======================================
Coverage ? 100%
=======================================
Files ? 1
Lines ? 49
Branches ? 3
=======================================
Hits ? 49
Misses ? 0
Partials ? 0
Continue to review full report at Codecov.
|
Fixes #1
Fixes #3
Fixes #4