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

init #2

Merged
merged 2 commits into from
Jan 12, 2018
Merged

init #2

merged 2 commits into from
Jan 12, 2018

Conversation

Amri91
Copy link
Owner

@Amri91 Amri91 commented Dec 29, 2017

Fixes #1
Fixes #3
Fixes #4

Copy link
Collaborator

@Avaq Avaq left a 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 @@
{
Copy link
Collaborator

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.

Copy link
Owner Author

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,
Copy link
Collaborator

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.

Copy link
Owner Author

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
Copy link
Collaborator

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?

Copy link
Owner Author

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
Copy link
Collaborator

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.

Copy link
Owner Author

Choose a reason for hiding this comment

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

👍


exports.sign = jest.fn(() => 'I am a token');

exports.decode = jest.fn(() => ({userId: 123}));
Copy link
Collaborator

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?

Copy link
Owner Author

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.

Copy link
Collaborator

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');
Copy link
Collaborator

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.

Copy link
Owner Author

Choose a reason for hiding this comment

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

👍

"dev": true
},
"semver": {

Copy link
Collaborator

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.

Copy link
Owner Author

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",
Copy link
Collaborator

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.

Copy link
Owner Author

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": "",
Copy link
Collaborator

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.

Copy link
Owner Author

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;

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?

Copy link
Collaborator

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".

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

Copy link
Collaborator

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

Copy link

@miselaytes-anton miselaytes-anton Jan 2, 2018

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));//[]

Copy link
Collaborator

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

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..

Copy link
Collaborator

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.

Copy link
Owner Author

@Amri91 Amri91 Jan 4, 2018

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({
Copy link
Collaborator

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.

Copy link
Owner Author

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
Copy link
Collaborator

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).

Copy link
Owner Author

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;
}
Copy link
Collaborator

@Avaq Avaq Jan 4, 2018

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.

Copy link
Collaborator

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);
Copy link
Collaborator

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.

Copy link
Owner Author

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
Copy link
Collaborator

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?

Copy link
Owner Author

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
Copy link
Collaborator

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.

Copy link
Owner Author

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;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Missing newline

Copy link
Owner Author

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
Copy link
Collaborator

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.

Copy link
Owner Author

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),
Copy link
Collaborator

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?

Copy link
Owner Author

@Amri91 Amri91 Jan 5, 2018

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.

Copy link
Owner Author

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) {
Copy link
Collaborator

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?

Copy link
Owner Author

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?

Copy link
Owner Author

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);
Copy link
Collaborator

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.

Copy link
Owner Author

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) {
Copy link
Collaborator

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?

Copy link
Owner Author

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];
}
});
Copy link
Collaborator

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.

Copy link
Owner Author

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.

Copy link
Owner Author

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 :)

@Amri91 Amri91 requested a review from dicearr January 8, 2018 12:35
Copy link
Collaborator

@Avaq Avaq left a 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. :)

@Amri91
Copy link
Owner Author

Amri91 commented Jan 12, 2018

Fixes #7

Errors thrown now has the same structure as the ones thrown by jsonwebtoken (the default library used)

@codecov
Copy link

codecov bot commented Jan 12, 2018

Codecov Report

❗ No coverage uploaded for pull request base (master@f2bae6a). Click here to learn what that means.
The diff coverage is 100%.

Impacted file tree graph

@@           Coverage Diff           @@
##             master     #2   +/-   ##
=======================================
  Coverage          ?   100%           
=======================================
  Files             ?      1           
  Lines             ?     49           
  Branches          ?      3           
=======================================
  Hits              ?     49           
  Misses            ?      0           
  Partials          ?      0
Impacted Files Coverage Δ
index.js 100% <100%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f2bae6a...a0d06d0. Read the comment docs.

@Amri91 Amri91 merged commit 9e05268 into master Jan 12, 2018
@Amri91 Amri91 deleted the inital-commit branch January 12, 2018 12:25
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.

3 participants