-
Notifications
You must be signed in to change notification settings - Fork 157
Conversation
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.
This is great! Thanks for creating a PR. I've left some minor change requests and brought up some questions. I have not tested locally yet, just a quick code review.
Possible Issue: You could revoke your own session and be locked out, until you login onto another system an un-revoke that session. What are your thoughts on this?
Maybe we can disabled the ability to revoke the current session to avoid this.
package.json
Outdated
"nodemailer": "4.x.x", | ||
"nodemailer-markdown": "1.x.x", | ||
"object-assign": "4.x.x", | ||
"slug": "0.9.x", | ||
"useragent": "^2.2.1", |
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.
"useragent": "2.x.x",
server/api/login.js
Outdated
}); | ||
} | ||
else if (existingSession.revoked) { | ||
|
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.
Remove this empty line.
server/api/login.js
Outdated
} | ||
else if (existingSession.revoked) { | ||
|
||
return reply(Boom.forbidden('Session is revoked')); |
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.
End sentence with a period.
Ex: return done(Boom.forbidden('Session is revoked.'));
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'm not sure what's better; keeping the revoked session or just deleting them when they're revoked.
I think if we just delete the session, it would turn into a session not found, and we shouldn't to handle these 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.
This probably isn't the best way to do session blocking, since you could easy get around it. I will update it to remove the session, and possibly do session blocking in another pull request down the road.
server/api/signup.js
Outdated
@@ -142,7 +142,12 @@ internals.applyRoutes = function (server, next) { | |||
}], | |||
session: ['linkUser', 'linkAccount', function (results, done) { | |||
|
|||
Session.create(results.user._id.toString(), done); | |||
const userAgent = Useragent.lookup(request.headers['user-agent']); | |||
const ip = request.info.remoteAddress; |
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.
In the case of environments like Heroku, and others that are fronted by a load balancer, we'd want to check that first.
Ex: const ip = request.headers['x-forwarded-for'] || request.info.remoteAddress;
server/auth.js
Outdated
|
||
if (!results.session) { | ||
if (results.session && results.session.revoked) { | ||
return done(Boom.forbidden('Session has been revoked')); |
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.
End sentence with a period.
Ex: return done(Boom.forbidden('Session has been revoked.'));
server/models/session.js
Outdated
@@ -33,7 +33,7 @@ class Session extends MongoModels { | |||
}); | |||
} | |||
|
|||
static create(userId, callback) { | |||
static create(userId, ip, browser, os, callback) { |
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.
Instead of passing browser
and os
, maybe we could pass the useragent string all the way down to where it's used and then use the useragent
module to parse it there... or we could send down the parsed useragent object and use the parts of it we're interested in.
server/api/login.js
Outdated
@@ -83,10 +83,17 @@ internals.applyRoutes = function (server, next) { | |||
}); | |||
} | |||
}, { | |||
assign: 'session', | |||
assign: 'existingSession', |
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.
During a login request I don't think we should be looking up the existing session. As an API consumer, when I create a new login request, I would expect a new session to be created. I'm not sure anything needs to change for this handler.
server/api/sessions.js
Outdated
@@ -84,6 +84,35 @@ internals.applyRoutes = function (server, next) { | |||
|
|||
server.route({ | |||
method: 'DELETE', | |||
path: '/sessions/my/{id}', |
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're accepting an id
url parameter, but we never use it.
server/api/sessions.js
Outdated
}, | ||
handler: function (request, reply) { | ||
|
||
const id = request.auth.credentials.roles.account._id.toString(); |
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'm not sure how we're finding a session by the account
id of the user. What if they're an admin
?
I think you meant to use the id
url param from above?
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
server/auth.js
Outdated
lastLogin: new Date() | ||
} | ||
}; | ||
Session.findByIdAndUpdate(results.session._id.toString(),update, done); |
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.
This auth strategy runs for every request, not just during a login event. So I'm not sure that updating the lastLogin
field of the session record is appropriate. If this updated something like lastActive
that would make more sense. What was the intent?
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, that was the intent, lastActive
is a better term
server/models/session.js
Outdated
|
||
const self = this; | ||
|
||
Async.auto({ | ||
keyHash: this.generateKeyHash.bind(this), | ||
newSession: ['keyHash', function (results, done) { | ||
|
||
const Useragent = require('useragent'); |
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.
require
statements should be at the top of the file with the others.
server/models/session.js
Outdated
|
||
const self = this; | ||
|
||
Async.auto({ | ||
keyHash: this.generateKeyHash.bind(this), | ||
newSession: ['keyHash', function (results, done) { | ||
|
||
const Useragent = require('useragent'); | ||
userAgent = Useragent.lookup(userAgent); |
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.
Would it be better to create a new variable here (const parsedAgent = Useragent.lookup(userAgent);
maybe) instead of clobbering the argument passed in.
server/models/session.js
Outdated
lastLogin: Joi.date().required(), | ||
ip: Joi.string().required(), | ||
browser: Joi.string().required(), | ||
os: Joi.string().required() |
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.
How should we handle someone not passing in a user-agent
header? Let's say they logged in via Postman or another custom client?
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.
Logged though postman native app, with recent change, returns back:
"browser": "PostmanRuntime/6.4.1",
"os": "Other 0.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.
Ok maybe Postman was a bad example 😆, I'm curious though what happens if a user-agent
header isn't provided.
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 just says "Other" from the parser as Browser and os is blank
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.
This is looking good. I left some comments and asked some questions.
Could you also rebase your work based on the latest master?
Pull from Frame
We should also add a GET |
Thanks for adding the |
@jedireza This one is done. If I add another feature, I'll open another pull request. |
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.
This worked well in my testing. I found a couple things we need to address before merging. Thanks again for working through this. 👍
server/models/session.js
Outdated
@@ -111,7 +114,11 @@ Session.schema = Joi.object({ | |||
_id: Joi.object(), | |||
userId: Joi.string().required(), | |||
key: Joi.string().required(), | |||
time: Joi.date().required() | |||
time: Joi.date().required(), | |||
lastLogin: Joi.date().required(), |
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.
This should be lastActive
.
$set: { | ||
lastActive: new Date() | ||
} | ||
}; |
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.
add new line
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.
This has been added but does not close the comment
server/auth.js
Outdated
lastActive: new Date() | ||
} | ||
}; | ||
Session.findByIdAndUpdate(results.session._id.toString(),update, done); |
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.
Add space before update
argument.
server/auth.js
Outdated
}], | ||
updateSession: ['scope', function (results, done) { | ||
|
||
if (!results.roles) { |
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.
This should be checking for if (!results.scope) {
since the scope and roles tasks run in parallel and we can't guarantee results.roles
will be around at this point.
I noticed this in my testing after I realized the lastActive
timestamp wasn't being updated.
server/api/sessions.js
Outdated
config: { | ||
auth: { | ||
strategy: 'simple', | ||
scope: 'account' |
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.
This shouldn't be limited to a scope. Admin users should be able to delete their sessions too.
server/api/sessions.js
Outdated
strategy: 'simple', | ||
scope: 'account' | ||
} | ||
}, |
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 we were going to use a pre handler that prevented someone from DELETE
ing their current session. We should add that.
Note: The conventional way of deleting your current session is to use the /api/logout
endpoint.
server/api/signup.js
Outdated
@@ -4,7 +4,6 @@ const Boom = require('boom'); | |||
const Config = require('../../config'); | |||
const Joi = require('joi'); | |||
|
|||
|
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.
leave this empty line
In relation to #180
You can have multiple session.
You the same session on the same device with the same ip is not deleted and re issued, it is simply return back.
User revoke there sessions. I am not sure if I did this correctly.
Admin can revoke any session.
Possible Issue: You could revoke your own session and be locked out, until you login onto another system an un-revoke that session. What are your thoughts on this?