I am playing with passport-local
. Its use is very straightforward, however the way passport establishes the relation between the cookie-session-id and the connected user is still mysterious to me.
I would like to store on a database the connected sessions in order to:
From this not-working snippet below, you can see I have two collections:
Here the snippet:
import passport from 'passport'
import LocalStrategy from 'passport-local'
import mongoose from 'mongoose'
var UserSchema = new mongoose.Schema({
username: String,
password: String
})
var Sessions = new mongoose.Schema({
session: String, // Sessions of connected users
username: { type: Schema.Types.ObjectId, ref: 'User' },
ip: String, // IP of the connected user
lastSeen: Date, // When the user made his last request
})
var User = mongoose.model('UserSchema')
How should I proceed to populate the Session table with passport
?
Well, I think I understood and I should be able to answer my own question.
Passport is not responsible for persistence, but express.session
is. Passport adds a wrapper in the middleware to intercept HTTP requests and populate the request with methods and attributes such as LogOut
or username
.
By default express.session
stores data into memory so this data is destroy when the server is stopped. Passport stores only one field into the session:
{ passport: { user: 'user-id' }}
Now we exonerated Passport, we can focus on the sessions.
Express session automatically creates a cookie with a signed sid
made from the secret:
app.use(express.session({
secret: 'keyboard cat'
})
This sid
is the cookie id and it is used by express.session
to link session data (stored on the server only), with the request made from the client.
To achieve what you wanna do (... What I would like to do), you have to make the session persistent. If you which to use MongoDB, you can use connect-mongo
which store session data into a Mongo collection:
const MongoStore = require('connect-mongo')(express.session);
var app = express();
app.configure(function() {
app.use(express.session({
secret: 'keyboard cat',
rolling: true,
store: new MongoStore({ url: 'mongodb://localhost/db', stringify: false })
}));
//...
}
When you start your server and someone make a request, it will add an entry into the db collection:
{
"_id" : "AcBXmfIXvz_y2Jp4nw1zfJvZTVPsCq7D",
"session" : {
"cookie" : {
"originalMaxAge" : null,
"expires" : null,
"secure" : null,
"httpOnly" : true,
"domain" : null,
"path" : "/"
},
"passport" : {
"user" : NumberInt(1)
}
},
"expires" : ISODate("2018-06-03T18:10:54.237+0000")
}
You can see that Passport only store the user id as explained.
Know the connected users
Now, you have this information in MongoDB. However when a user disconnects, the data session will remains in the database. You may use the compatibility mode:
app.use(session({
store: new MongoStore({
url: 'mongodb://localhost/test-app',
autoRemove: 'interval',
autoRemoveInterval: 10 // In minutes. Default
})
}));
To automatically remove expired session from the database.
Detect IP changes (for security reasons)
You have to store this information into your session with:
req.session.ip = (req.headers['x-forwarded-for'] ||
req.connection.remoteAddress ||
req.socket.remoteAddress ||
req.connection.socket.remoteAddress).split(",")[0];
Auto disconnect users not seen for a long time
You have to implement that manually. You can use an event
from MongoStore such as touch
triggered when a session has been touched.
Change the session number across each request (for security reasons)
This is more complicated and I don't have the answer yet. Changing the cookie sid
across connections means to regenerate a new session id and update the MongoDB. I don't have a working solution yet.
Keep the users connected across server reboot (useful for debugging, and server update)
Well, it works thanks to connect-mongo