Skip to content
This repository has been archived by the owner on Jul 12, 2020. It is now read-only.

Add Multi Session and Session Revoke #182

Merged
merged 6 commits into from
Nov 21, 2017
Merged

Add Multi Session and Session Revoke #182

merged 6 commits into from
Nov 21, 2017

Conversation

gregfrasco
Copy link
Contributor

@gregfrasco gregfrasco commented Oct 23, 2017

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?

@gregfrasco gregfrasco changed the title Add Multi Session and Session Revoke #180 Add Multi Session and Session Revoke Oct 23, 2017
Copy link
Owner

@jedireza jedireza left a 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",
Copy link
Owner

Choose a reason for hiding this comment

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

"useragent": "2.x.x",

});
}
else if (existingSession.revoked) {

Copy link
Owner

Choose a reason for hiding this comment

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

Remove this empty line.

}
else if (existingSession.revoked) {

return reply(Boom.forbidden('Session is revoked'));
Copy link
Owner

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

Copy link
Owner

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.

Copy link
Contributor Author

@gregfrasco gregfrasco Oct 24, 2017

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.

@@ -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;
Copy link
Owner

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

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

@@ -33,7 +33,7 @@ class Session extends MongoModels {
});
}

static create(userId, callback) {
static create(userId, ip, browser, os, callback) {
Copy link
Owner

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.

@@ -83,10 +83,17 @@ internals.applyRoutes = function (server, next) {
});
}
}, {
assign: 'session',
assign: 'existingSession',
Copy link
Owner

@jedireza jedireza Nov 2, 2017

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.

@@ -84,6 +84,35 @@ internals.applyRoutes = function (server, next) {

server.route({
method: 'DELETE',
path: '/sessions/my/{id}',
Copy link
Owner

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.

},
handler: function (request, reply) {

const id = request.auth.credentials.roles.account._id.toString();
Copy link
Owner

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?

Copy link
Contributor Author

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

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?

Copy link
Contributor Author

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


const self = this;

Async.auto({
keyHash: this.generateKeyHash.bind(this),
newSession: ['keyHash', function (results, done) {

const Useragent = require('useragent');
Copy link
Owner

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.


const self = this;

Async.auto({
keyHash: this.generateKeyHash.bind(this),
newSession: ['keyHash', function (results, done) {

const Useragent = require('useragent');
userAgent = Useragent.lookup(userAgent);
Copy link
Owner

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.

lastLogin: Joi.date().required(),
ip: Joi.string().required(),
browser: Joi.string().required(),
os: Joi.string().required()
Copy link
Owner

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?

Copy link
Contributor Author

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

Copy link
Owner

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.

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 think it just says "Other" from the parser as Browser and os is blank

Copy link
Owner

@jedireza jedireza left a 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?

@jedireza
Copy link
Owner

jedireza commented Nov 3, 2017

We should also add a GET /sessions/my endpoint so we can get a list of that users sessions.

@jedireza
Copy link
Owner

Thanks for adding the session/my support. Is this ready for a final review? Are you planning on adding anything else?

@gregfrasco
Copy link
Contributor Author

@jedireza This one is done. If I add another feature, I'll open another pull request.

Copy link
Owner

@jedireza jedireza left a 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. 👍

@@ -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(),
Copy link
Owner

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()
}
};
Copy link
Owner

Choose a reason for hiding this comment

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

add new line

Copy link
Contributor Author

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

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

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.

config: {
auth: {
strategy: 'simple',
scope: 'account'
Copy link
Owner

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.

strategy: 'simple',
scope: 'account'
}
},
Copy link
Owner

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 DELETEing their current session. We should add that.

Note: The conventional way of deleting your current session is to use the /api/logout endpoint.

@@ -4,7 +4,6 @@ const Boom = require('boom');
const Config = require('../../config');
const Joi = require('joi');


Copy link
Owner

Choose a reason for hiding this comment

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

leave this empty line

@jedireza jedireza merged commit 85361f3 into jedireza:master Nov 21, 2017
@jedireza jedireza mentioned this pull request Feb 3, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants