Skip to content

Access req object inside serializeUser and deserializeUser functions #111

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

Closed
taxilian opened this issue Mar 6, 2013 · 9 comments
Closed

Comments

@taxilian
Copy link

taxilian commented Mar 6, 2013

I need to enforce that a user is only logged in at one location at a time per device type (we allow them to log in on one computer, one tablet, and one phone); simplest way to do that seems to be to just store the last session id for each type on my user object and then in deserialize I return null if the session id isn't one of the currently authorized session types. (I'd rather invalidate the old session, but the session middleware doesn't let me invalidate a session by user id)

Anyway, the problem with this is that there doesn't seem to be any way to get the req object into the deserializeUser function and thus I can't get the session id to check if the session is still valid.

Could this feature be added? Seems like it'd be simple enough to add an optional options object similar to how the strategies do it for getting the req object into the authenticate callbacks... Also, if you have an alternate suggestion, I'm certainly open to that as well =]

@jaredhanson
Copy link
Owner

I'll give my standard reply. This is more related to authorization rather than authentication. My recommendation is to set this up as additional middleware, something like:

function enforceDeviceLogins(req, res, next) {
  if (isLoggedInOnOtherDevice(req.user)) {
    return res.send('Already logged in on another device');
  } else {
    // save device type and login somewhere
    // log the user in and serialize a session
    req.login(user, function(err) {
      if (err) { return next(err); }
      return next();
    })
  }
}

app.post('/login',
  passport.authenticate('local', { session: false }),
  enforceDeviceLogins
  function(req, res) {
    // If this function gets called, authentication was successful
    // and device checks pass.
    // `req.user` contains the authenticated user.
    res.redirect('/users/' + req.user.username);
  });

That work for you?

@eliperelman
Copy link

My first instinct with the issue I am having was also to have access to the request object, but based on all the other issues of people wanting the request object inside deserializeUser being resolved in some other fashion, maybe you have another recommendation.

With the node-orm2 library, it can be configured to work via express middleware:

app.use(orm.express("mysql://username:password@host/database", {
    define: function (db, models) {
        models.person = db.define("person", { ... });
    }
}));
app.listen(80);

app.get("/", function (req, res) {
    // req.models is a reference to models used above in define()
    req.models.person.find(...);
});

You can see here that once the database is connected and the route is hit, you have access to the models on the request object. When incorporating this into passport configuration:

passport.use({ passReqToCallback: true }, new LocalStrategy(function (request, username, password, done) {
    request.models.User
        .find({ username: username })
        .limit(1)
        .run(function (err, users) {
            var user = users[0];

            if (err) {
                done(err);
            } else if (!hasher.verify(password, user.password)) {
                done(null, false);
            } else {
                done(null, user);
            }
        });
}));

passport.serializeUser(function (user, done) {
    done(null, user.id);
});

passport.deserializeUser(function (id, done) {
    // don't have access to request object here
});

I don't have access to the request object, which has the model I need to connect to the database to deserialize the user. Suggestions?

@jimmyjacobson
Copy link

I would make Models a global object defined by a required instead of trying
to attach it to every incoming request.

Models is a static library for returning DB objects, correct?

Sent from my iJimmy
http://twitter.com/jimmyjacobson

On Apr 13, 2013, at 12:32 PM, Eli Perelman notifications@github.com wrote:

My first instinct with the issue I am having was also to have access to the
request object, but based on all the other issues being resolved in some
other fashion, maybe you have another recommendation.

With the node-orm2 https://github.com/dresende/node-orm2 library, it can
be configured to work via express middleware:

var express = require('express');var orm = require('orm');var app = express();
app.use(orm.express("mysql://username:password@host/database", {
define: function (db, models) {
models.person = db.define("person", { ... });
}}));app.listen(80);
app.get("/", function (req, res) {
// req.models is a reference to models used above in define()
req.models.person.find(...);});

You can see here that once the database is connected and the route is hit,
you have access to the models on the request object. When incorporating
this into passport configuration:

passport.use({ passReqToCallback: true }, new LocalStrategy(function
(request, username, password, done) {
request.models.User
.find({ username: username })
.limit(1)
.run(function (err, users) {
var user = users[0];

        if (err) {
            done(err);
        } else if (!hasher.verify(password, user.password)) {
            done(null, false);
        } else {
            done(null, user);
        }
    });}));

passport.serializeUser(function (user, done) {
done(null, user.id);});
passport.deserializeUser(function (id, done) {
// don't have access to request object here});

I don't have access to the request object, which has the model I need to
connect to the database to deserialize the user. Suggestions?


Reply to this email directly or view it on
GitHubhttps://github.com//issues/111#issuecomment-16339252
.

@eliperelman
Copy link

The node-orm2 library automatically adds the models to every request as part of its middleware implementation. I am sure I could expose them once out of one of the initial requests:

var User;

passport.use({ passReqToCallback: true }, new LocalStrategy(function (request, username, password, done) {
    if (!User) {
        User = request.models.User;
    }

    User
        .find({ username: username })
        .limit(1)
        .run(function (err, users) {
            var user = users[0];

            if (err) {
                done(err);
            } else if (!hasher.verify(password, user.password)) {
                done(null, false);
            } else {
                done(null, user);
            }
        });
}));

passport.deserializeUser(function (id, done) {
    User.get(id, done);
});

but this seems a little hackish. Thoughts?

@KyleAllaire
Copy link

@eliperelman, I'm struggling with the same thing. Were you able to come up with anything better? I've only come up with something similar to what you're doing above or keeping a separate db connection with the models redefined on it.

@ptnplanet
Copy link

I just want to share my solution for anybody struggling with this issue.

Please note, that this will only work as described with the new node-orm2 version that supports a third argument next for the define function (see here)

app.use(orm.express('...', {
    define: function (db, models, next) {}
});

File stack.js

require defineModels = require('./model').define;
app.use(orm.express('sqlite://sqlite3.s3dbt', { define: defineModels }));

File model.js

var async = require('async'),
    definedModels;

module.exports.define = function (db, models, next) {
    definedModels = models;
    var curriedLoad = function (file) { return function (cb) { db.load(file, cb); }; };

    async.waterfall([
            curriedLoad('.model/user'),
            curriedLoad('.model/posting'),
            // ...
    ], function (err) {
        db.models.user.hasMany(db.models.posting);
        db.sync(next);
    });
};

module.exports.model = function (name) {
    return definedModels[name];
}

You can now use the following in your passport's deserializeUser function:

var User = require('./model').model('user');

var deserializeUser = function (id, callback) {
    User.get(id, callback);
};

@mpalmerlee
Copy link

We are having a similar issue to this original problem, access to req object within deserializeUser. This issue seems to have gotten off-topic and is now mostly related to using the node-orm2 library.

We need access to the request object within deserializeUser because we use a REST service to deserialize the user instead of a database and the REST service needs information from the request in order to know what domain name the user requested, etc...

I was just about to fork the project for this but then found this issue, please let me know if there is a work around that you can think of, otherwise I'll just fork and submit a PR.

@jaredhanson
Copy link
Owner

Addressed by merging #160. req is now optionally available in serialization/deserialization functions.

@eliperelman
Copy link

Fantastic news! Thanks @jaredhanson!

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

No branches or pull requests

7 participants