function session(options) { var opts = options || {} // get the cookie options var cookieOptions = opts.cookie || {} // get the session id generate function var generateId = opts.genid || generateSessionId // get the session cookie name var name = opts.name || opts.key || 'connect.sid' // get the session store var store = opts.store || new MemoryStore() // get the trust proxy setting var trustProxy = opts.proxy // get the resave session option var resaveSession = opts.resave; // get the rolling session option var rollingSessions = Boolean(opts.rolling) // get the save uninitialized session option var saveUninitializedSession = opts.saveUninitialized // get the cookie signing secret var secret = opts.secret if (typeof generateId !== 'function') { throw new TypeError('genid option must be a function'); } if (resaveSession === undefined) { deprecate('undefined resave option; provide resave option'); resaveSession = true; } if (saveUninitializedSession === undefined) { deprecate('undefined saveUninitialized option; provide saveUninitialized option'); saveUninitializedSession = true; } if (opts.unset && opts.unset !== 'destroy' && opts.unset !== 'keep') { throw new TypeError('unset option must be "destroy" or "keep"'); } // TODO: switch to "destroy" on next major var unsetDestroy = opts.unset === 'destroy' if (Array.isArray(secret) && secret.length === 0) { throw new TypeError('secret option array must contain one or more strings'); } if (secret && !Array.isArray(secret)) { secret = [secret]; } if (!secret) { deprecate('req.secret; provide secret option'); } // notify user that this store is not // meant for a production environment /* istanbul ignore next: not tested */ if ('production' == env && store instanceof MemoryStore) { console.warn(warning); } // generates the new session store.generate = function(req){ req.sessionID = generateId(req); req.session = new Session(req); req.session.cookie = new Cookie(cookieOptions); if (cookieOptions.secure === 'auto') { req.session.cookie.secure = issecure(req, trustProxy); } }; var storeImplementsTouch = typeof store.touch === 'function'; // register event listeners for the store to track readiness var storeReady = true store.on('disconnect', function ondisconnect() { storeReady = false }) store.on('connect', function onconnect() { storeReady = true }) return function session(req, res, next) { // self-awareness if (req.session) { next() return } // Handle connection as if there is no session if // the store has temporarily disconnected etc if (!storeReady) { debug('store is disconnected') next() return } // pathname mismatch var originalPath = parseUrl.original(req).pathname; if (originalPath.indexOf(cookieOptions.path || '/') !== 0) return next(); // ensure a secret is available or bail if (!secret && !req.secret) { next(new Error('secret option required for sessions')); return; } // backwards compatibility for signed cookies // req.secret is passed from the cookie parser middleware var secrets = secret || [req.secret]; var originalHash; var originalId; var savedHash; var touched = false // expose store req.sessionStore = store; // get the session ID from the cookie var cookieId = req.sessionID = getcookie(req, name, secrets); // set-cookie onHeaders(res, function(){ if (!req.session) { debug('no session'); return; } if (!shouldSetCookie(req)) { return; } // only send secure cookies via https if (req.session.cookie.secure && !issecure(req, trustProxy)) { debug('not secured'); return; } if (!touched) ...
n/a
function Cookie(options) { this.path = '/'; this.maxAge = null; this.httpOnly = true; if (options) merge(this, options); this.originalMaxAge = undefined == this.originalMaxAge ? this.maxAge : this.originalMaxAge; }
n/a
function MemoryStore() { Store.call(this) this.sessions = Object.create(null) }
n/a
function Session(req, data) { Object.defineProperty(this, 'req', { value: req }); Object.defineProperty(this, 'id', { value: req.sessionID }); if (typeof data === 'object' && data !== null) { // merge data into this, ignoring prototype properties for (var prop in data) { if (!(prop in this)) { this[prop] = data[prop] } } } }
n/a
function Store() { EventEmitter.call(this) }
n/a
function Cookie(options) { this.path = '/'; this.maxAge = null; this.httpOnly = true; if (options) merge(this, options); this.originalMaxAge = undefined == this.originalMaxAge ? this.maxAge : this.originalMaxAge; }
n/a
serialize = function (name, val){ return cookie.serialize(name, val, this.data); }
...
* Set cookie on response.
*
* @private
*/
function setcookie(res, name, val, secret, options) {
var signed = 's:' + signature.sign(val, secret);
var data = cookie.serialize(name, signed, options);
debug('set-cookie %s', data);
var prev = res.getHeader('set-cookie') || [];
var header = Array.isArray(prev) ? prev.concat(data) : [prev, data];
res.setHeader('set-cookie', header)
...
toJSON = function (){ return this.data; }
n/a
function MemoryStore() { Store.call(this) this.sessions = Object.create(null) }
n/a
function Store() { EventEmitter.call(this) }
n/a
function all(callback) { var sessionIds = Object.keys(this.sessions) var sessions = Object.create(null) for (var i = 0; i < sessionIds.length; i++) { var sessionId = sessionIds[i] var session = getSession.call(this, sessionId) if (session) { sessions[sessionId] = session; } } callback && defer(callback, null, sessions) }
...
* Recommended methods are ones that this module will call on the store if
available.
* Optional methods are ones this module does not call at all, but helps
present uniform stores to users.
For an example implementation view the [connect-redis](http://github.com/visionmedia/connect-redis) repo.
### store.all(callback)
**Optional**
This optional method is used to get all sessions in the store as an array. The
`callback` should be called as `callback(error, sessions)`.
### store.destroy(sid, callback)
...
function clear(callback) { this.sessions = Object.create(null) callback && defer(callback) }
...
**Required**
This required method is used to destroy/delete a session from the store given
a session ID (`sid`). The `callback` should be called as `callback(error)` once
the session is destroyed.
### store.clear(callback)
**Optional**
This optional method is used to delete all sessions from the store. The
`callback` should be called as `callback(error)` once the store is cleared.
### store.length(callback)
...
function destroy(sessionId, callback) { delete this.sessions[sessionId] callback && defer(callback) }
...
```js
req.session.regenerate(function(err) {
// will have a new session here
})
```
#### Session.destroy(callback)
Destroys the session and will unset the `req.session` property.
Once complete, the `callback` will be invoked.
```js
req.session.destroy(function(err) {
// cannot access session here
...
function get(sessionId, callback) { defer(callback, null, getSession.call(this, sessionId)) }
...
```js
var app = express()
var sess = {
secret: 'keyboard cat',
cookie: {}
}
if (app.get('env') === 'production') {
app.set('trust proxy', 1) // trust first proxy
sess.cookie.secure = true // serve secure cookies
}
app.use(session(sess))
```
...
function length(callback) { this.all(function (err, sessions) { if (err) return callback(err) callback(null, Object.keys(sessions).length) }) }
...
### store.clear(callback)
**Optional**
This optional method is used to delete all sessions from the store. The
`callback` should be called as `callback(error)` once the store is cleared.
### store.length(callback)
**Optional**
This optional method is used to get the count of all sessions in the store.
The `callback` should be called as `callback(error, len)`.
### store.get(sid, callback)
...
function set(sessionId, session, callback) { this.sessions[sessionId] = JSON.stringify(session) callback && defer(callback) }
...
an https-enabled website, i.e., HTTPS is necessary for secure cookies. If `secure`
is set, and you access your site over HTTP, the cookie will not be set. If you
have your node.js behind a proxy and are using `secure: true`, you need to set
"trust proxy" in express:
```js
var app = express()
app.set('trust proxy', 1) // trust first proxy
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
cookie: { secure: true }
}))
```
...
function touch(sessionId, session, callback) { var currentSession = getSession.call(this, sessionId) if (currentSession) { // update expiration currentSession.cookie = session.cookie this.sessions[sessionId] = JSON.stringify(currentSession) } callback && defer(callback) }
...
```js
req.session.save(function(err) {
// session saved
})
```
#### Session.touch()
Updates the `.maxAge` property. Typically this is
not necessary to call, as the session middleware does this for you.
### req.session.id
Each session has a unique ID associated with it. This property will
...
function Store() { EventEmitter.call(this) }
n/a
function EventEmitter() { EventEmitter.init.call(this); }
n/a
createSession = function (req, sess){ var expires = sess.cookie.expires , orig = sess.cookie.originalMaxAge; sess.cookie = new Cookie(sess.cookie); if ('string' == typeof expires) sess.cookie.expires = new Date(expires); sess.cookie.originalMaxAge = orig; req.session = new Session(req, sess); return req.session; }
...
// no session
} else if (!sess) {
debug('no session found');
generate();
// populate req.session
} else {
debug('session found');
store.createSession(req, sess);
originalId = req.sessionID;
originalHash = hash(sess);
if (!resaveSession) {
savedHash = originalHash
}
...
load = function (sid, fn){ var self = this; this.get(sid, function(err, sess){ if (err) return fn(err); if (!sess) return fn(); var req = { sessionID: sid, sessionStore: self }; fn(null, self.createSession(req, sess)) }); }
n/a
regenerate = function (req, fn){ var self = this; this.destroy(req.sessionID, function(err){ self.generate(req); fn(err); }); }
...
} else {
sess.views = 1
res.end('welcome to the session demo. refresh!')
}
})
```
#### Session.regenerate(callback)
To regenerate the session simply invoke the method. Once complete,
a new SID and `Session` instance will be initialized at `req.session`
and the `callback` will be invoked.
```js
req.session.regenerate(function(err) {
...