Project rename
This commit is contained in:
+389
@@ -0,0 +1,389 @@
|
||||
1.15.6 / 2017-09-26
|
||||
===================
|
||||
|
||||
* deps: debug@2.6.9
|
||||
* deps: parseurl@~1.3.2
|
||||
- perf: reduce overhead for full URLs
|
||||
- perf: unroll the "fast-path" `RegExp`
|
||||
* deps: uid-safe@~2.1.5
|
||||
- perf: remove only trailing `=`
|
||||
* deps: utils-merge@1.0.1
|
||||
|
||||
1.15.5 / 2017-08-02
|
||||
===================
|
||||
|
||||
* Fix `TypeError` when `req.url` is an empty string
|
||||
* deps: depd@~1.1.1
|
||||
- Remove unnecessary `Buffer` loading
|
||||
|
||||
1.15.4 / 2017-07-18
|
||||
===================
|
||||
|
||||
* deps: debug@2.6.8
|
||||
|
||||
1.15.3 / 2017-05-17
|
||||
===================
|
||||
|
||||
* deps: debug@2.6.7
|
||||
- deps: ms@2.0.0
|
||||
|
||||
1.15.2 / 2017-03-26
|
||||
===================
|
||||
|
||||
* deps: debug@2.6.3
|
||||
- Fix `DEBUG_MAX_ARRAY_LENGTH`
|
||||
* deps: uid-safe@~2.1.4
|
||||
- Remove `base64-url` dependency
|
||||
|
||||
1.15.1 / 2017-02-10
|
||||
===================
|
||||
|
||||
* deps: debug@2.6.1
|
||||
- Fix deprecation messages in WebStorm and other editors
|
||||
- Undeprecate `DEBUG_FD` set to `1` or `2`
|
||||
|
||||
1.15.0 / 2017-01-22
|
||||
===================
|
||||
|
||||
* Fix detecting modified session when session contains "cookie" property
|
||||
* Fix resaving already-saved reloaded session at end of request
|
||||
* deps: crc@3.4.4
|
||||
- perf: use `Buffer.from` when available
|
||||
* deps: debug@2.6.0
|
||||
- Allow colors in workers
|
||||
- Deprecated `DEBUG_FD` environment variable
|
||||
- Use same color for same namespace
|
||||
- Fix error when running under React Native
|
||||
- deps: ms@0.7.2
|
||||
* perf: remove unreachable branch in set-cookie method
|
||||
|
||||
1.14.2 / 2016-10-30
|
||||
===================
|
||||
|
||||
* deps: crc@3.4.1
|
||||
- Fix deprecation warning in Node.js 7.x
|
||||
* deps: uid-safe@~2.1.3
|
||||
- deps: base64-url@1.3.3
|
||||
|
||||
1.14.1 / 2016-08-24
|
||||
===================
|
||||
|
||||
* Fix not always resetting session max age before session save
|
||||
* Fix the cookie `sameSite` option to actually alter the `Set-Cookie`
|
||||
* deps: uid-safe@~2.1.2
|
||||
- deps: base64-url@1.3.2
|
||||
|
||||
1.14.0 / 2016-07-01
|
||||
===================
|
||||
|
||||
* Correctly inherit from `EventEmitter` class in `Store` base class
|
||||
* Fix issue where `Set-Cookie` `Expires` was not always updated
|
||||
* Methods are no longer enumerable on `req.session` object
|
||||
* deps: cookie@0.3.1
|
||||
- Add `sameSite` option
|
||||
- Improve error message when `encode` is not a function
|
||||
- Improve error message when `expires` is not a `Date`
|
||||
- perf: enable strict mode
|
||||
- perf: use for loop in parse
|
||||
- perf: use string concatination for serialization
|
||||
* deps: parseurl@~1.3.1
|
||||
- perf: enable strict mode
|
||||
* deps: uid-safe@~2.1.1
|
||||
- Use `random-bytes` for byte source
|
||||
- deps: base64-url@1.2.2
|
||||
* perf: enable strict mode
|
||||
* perf: remove argument reassignment
|
||||
|
||||
1.13.0 / 2016-01-10
|
||||
===================
|
||||
|
||||
* Fix `rolling: true` to not set cookie when no session exists
|
||||
- Better `saveUninitialized: false` + `rolling: true` behavior
|
||||
* deps: crc@3.4.0
|
||||
|
||||
1.12.1 / 2015-10-29
|
||||
===================
|
||||
|
||||
* deps: cookie@0.2.3
|
||||
- Fix cookie `Max-Age` to never be a floating point number
|
||||
|
||||
1.12.0 / 2015-10-25
|
||||
===================
|
||||
|
||||
* Support the value `'auto'` in the `cookie.secure` option
|
||||
* deps: cookie@0.2.2
|
||||
- Throw on invalid values provided to `serialize`
|
||||
* deps: depd@~1.1.0
|
||||
- Enable strict mode in more places
|
||||
- Support web browser loading
|
||||
* deps: on-headers@~1.0.1
|
||||
- perf: enable strict mode
|
||||
|
||||
1.11.3 / 2015-05-22
|
||||
===================
|
||||
|
||||
* deps: cookie@0.1.3
|
||||
- Slight optimizations
|
||||
* deps: crc@3.3.0
|
||||
|
||||
1.11.2 / 2015-05-10
|
||||
===================
|
||||
|
||||
* deps: debug@~2.2.0
|
||||
- deps: ms@0.7.1
|
||||
* deps: uid-safe@~2.0.0
|
||||
|
||||
1.11.1 / 2015-04-08
|
||||
===================
|
||||
|
||||
* Fix mutating `options.secret` value
|
||||
|
||||
1.11.0 / 2015-04-07
|
||||
===================
|
||||
|
||||
* Support an array in `secret` option for key rotation
|
||||
* deps: depd@~1.0.1
|
||||
|
||||
1.10.4 / 2015-03-15
|
||||
===================
|
||||
|
||||
* deps: debug@~2.1.3
|
||||
- Fix high intensity foreground color for bold
|
||||
- deps: ms@0.7.0
|
||||
|
||||
1.10.3 / 2015-02-16
|
||||
===================
|
||||
|
||||
* deps: cookie-signature@1.0.6
|
||||
* deps: uid-safe@1.1.0
|
||||
- Use `crypto.randomBytes`, if available
|
||||
- deps: base64-url@1.2.1
|
||||
|
||||
1.10.2 / 2015-01-31
|
||||
===================
|
||||
|
||||
* deps: uid-safe@1.0.3
|
||||
- Fix error branch that would throw
|
||||
- deps: base64-url@1.2.0
|
||||
|
||||
1.10.1 / 2015-01-08
|
||||
===================
|
||||
|
||||
* deps: uid-safe@1.0.2
|
||||
- Remove dependency on `mz`
|
||||
|
||||
1.10.0 / 2015-01-05
|
||||
===================
|
||||
|
||||
* Add `store.touch` interface for session stores
|
||||
* Fix `MemoryStore` expiration with `resave: false`
|
||||
* deps: debug@~2.1.1
|
||||
|
||||
1.9.3 / 2014-12-02
|
||||
==================
|
||||
|
||||
* Fix error when `req.sessionID` contains a non-string value
|
||||
|
||||
1.9.2 / 2014-11-22
|
||||
==================
|
||||
|
||||
* deps: crc@3.2.1
|
||||
- Minor fixes
|
||||
|
||||
1.9.1 / 2014-10-22
|
||||
==================
|
||||
|
||||
* Remove unnecessary empty write call
|
||||
- Fixes Node.js 0.11.14 behavior change
|
||||
- Helps work-around Node.js 0.10.1 zlib bug
|
||||
|
||||
1.9.0 / 2014-09-16
|
||||
==================
|
||||
|
||||
* deps: debug@~2.1.0
|
||||
- Implement `DEBUG_FD` env variable support
|
||||
* deps: depd@~1.0.0
|
||||
|
||||
1.8.2 / 2014-09-15
|
||||
==================
|
||||
|
||||
* Use `crc` instead of `buffer-crc32` for speed
|
||||
* deps: depd@0.4.5
|
||||
|
||||
1.8.1 / 2014-09-08
|
||||
==================
|
||||
|
||||
* Keep `req.session.save` non-enumerable
|
||||
* Prevent session prototype methods from being overwritten
|
||||
|
||||
1.8.0 / 2014-09-07
|
||||
==================
|
||||
|
||||
* Do not resave already-saved session at end of request
|
||||
* deps: cookie-signature@1.0.5
|
||||
* deps: debug@~2.0.0
|
||||
|
||||
1.7.6 / 2014-08-18
|
||||
==================
|
||||
|
||||
* Fix exception on `res.end(null)` calls
|
||||
|
||||
1.7.5 / 2014-08-10
|
||||
==================
|
||||
|
||||
* Fix parsing original URL
|
||||
* deps: on-headers@~1.0.0
|
||||
* deps: parseurl@~1.3.0
|
||||
|
||||
1.7.4 / 2014-08-05
|
||||
==================
|
||||
|
||||
* Fix response end delay for non-chunked responses
|
||||
|
||||
1.7.3 / 2014-08-05
|
||||
==================
|
||||
|
||||
* Fix `res.end` patch to call correct upstream `res.write`
|
||||
|
||||
1.7.2 / 2014-07-27
|
||||
==================
|
||||
|
||||
* deps: depd@0.4.4
|
||||
- Work-around v8 generating empty stack traces
|
||||
|
||||
1.7.1 / 2014-07-26
|
||||
==================
|
||||
|
||||
* deps: depd@0.4.3
|
||||
- Fix exception when global `Error.stackTraceLimit` is too low
|
||||
|
||||
1.7.0 / 2014-07-22
|
||||
==================
|
||||
|
||||
* Improve session-ending error handling
|
||||
- Errors are passed to `next(err)` instead of `console.error`
|
||||
* deps: debug@1.0.4
|
||||
* deps: depd@0.4.2
|
||||
- Add `TRACE_DEPRECATION` environment variable
|
||||
- Remove non-standard grey color from color output
|
||||
- Support `--no-deprecation` argument
|
||||
- Support `--trace-deprecation` argument
|
||||
|
||||
1.6.5 / 2014-07-11
|
||||
==================
|
||||
|
||||
* Do not require `req.originalUrl`
|
||||
* deps: debug@1.0.3
|
||||
- Add support for multiple wildcards in namespaces
|
||||
|
||||
1.6.4 / 2014-07-07
|
||||
==================
|
||||
|
||||
* Fix blank responses for stores with synchronous operations
|
||||
|
||||
1.6.3 / 2014-07-04
|
||||
==================
|
||||
|
||||
* Fix resave deprecation message
|
||||
|
||||
1.6.2 / 2014-07-04
|
||||
==================
|
||||
|
||||
* Fix confusing option deprecation messages
|
||||
|
||||
1.6.1 / 2014-06-28
|
||||
==================
|
||||
|
||||
* Fix saveUninitialized deprecation message
|
||||
|
||||
1.6.0 / 2014-06-28
|
||||
==================
|
||||
|
||||
* Add deprecation message to undefined `resave` option
|
||||
* Add deprecation message to undefined `saveUninitialized` option
|
||||
* Fix `res.end` patch to return correct value
|
||||
* Fix `res.end` patch to handle multiple `res.end` calls
|
||||
* Reject cookies with missing signatures
|
||||
|
||||
1.5.2 / 2014-06-26
|
||||
==================
|
||||
|
||||
* deps: cookie-signature@1.0.4
|
||||
- fix for timing attacks
|
||||
|
||||
1.5.1 / 2014-06-21
|
||||
==================
|
||||
|
||||
* Move hard-to-track-down `req.secret` deprecation message
|
||||
|
||||
1.5.0 / 2014-06-19
|
||||
==================
|
||||
|
||||
* Debug name is now "express-session"
|
||||
* Deprecate integration with `cookie-parser` middleware
|
||||
* Deprecate looking for secret in `req.secret`
|
||||
* Directly read cookies; `cookie-parser` no longer required
|
||||
* Directly set cookies; `res.cookie` no longer required
|
||||
* Generate session IDs with `uid-safe`, faster and even less collisions
|
||||
|
||||
1.4.0 / 2014-06-17
|
||||
==================
|
||||
|
||||
* Add `genid` option to generate custom session IDs
|
||||
* Add `saveUninitialized` option to control saving uninitialized sessions
|
||||
* Add `unset` option to control unsetting `req.session`
|
||||
* Generate session IDs with `rand-token` by default; reduce collisions
|
||||
* deps: buffer-crc32@0.2.3
|
||||
|
||||
1.3.1 / 2014-06-14
|
||||
==================
|
||||
|
||||
* Add description in package for npmjs.org listing
|
||||
|
||||
1.3.0 / 2014-06-14
|
||||
==================
|
||||
|
||||
* Integrate with express "trust proxy" by default
|
||||
* deps: debug@1.0.2
|
||||
|
||||
1.2.1 / 2014-05-27
|
||||
==================
|
||||
|
||||
* Fix `resave` such that `resave: true` works
|
||||
|
||||
1.2.0 / 2014-05-19
|
||||
==================
|
||||
|
||||
* Add `resave` option to control saving unmodified sessions
|
||||
|
||||
1.1.0 / 2014-05-12
|
||||
==================
|
||||
|
||||
* Add `name` option; replacement for `key` option
|
||||
* Use `setImmediate` in MemoryStore for node.js >= 0.10
|
||||
|
||||
1.0.4 / 2014-04-27
|
||||
==================
|
||||
|
||||
* deps: debug@0.8.1
|
||||
|
||||
1.0.3 / 2014-04-19
|
||||
==================
|
||||
|
||||
* Use `res.cookie()` instead of `res.setHeader()`
|
||||
* deps: cookie@0.1.2
|
||||
|
||||
1.0.2 / 2014-02-23
|
||||
==================
|
||||
|
||||
* Add missing dependency to `package.json`
|
||||
|
||||
1.0.1 / 2014-02-15
|
||||
==================
|
||||
|
||||
* Add missing dependencies to `package.json`
|
||||
|
||||
1.0.0 / 2014-02-15
|
||||
==================
|
||||
|
||||
* Genesis from `connect`
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2010 Sencha Inc.
|
||||
Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>
|
||||
Copyright (c) 2014-2015 Douglas Christopher Wilson <doug@somethingdoug.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+756
@@ -0,0 +1,756 @@
|
||||
# express-session
|
||||
|
||||
[![NPM Version][npm-image]][npm-url]
|
||||
[![NPM Downloads][downloads-image]][downloads-url]
|
||||
[![Build Status][travis-image]][travis-url]
|
||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
||||
[![Gratipay][gratipay-image]][gratipay-url]
|
||||
|
||||
## Installation
|
||||
|
||||
This is a [Node.js](https://nodejs.org/en/) module available through the
|
||||
[npm registry](https://www.npmjs.com/). Installation is done using the
|
||||
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
|
||||
|
||||
```sh
|
||||
$ npm install express-session
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
```js
|
||||
var session = require('express-session')
|
||||
```
|
||||
|
||||
### session(options)
|
||||
|
||||
Create a session middleware with the given `options`.
|
||||
|
||||
**Note** Session data is _not_ saved in the cookie itself, just the session ID.
|
||||
Session data is stored server-side.
|
||||
|
||||
**Note** Since version 1.5.0, the [`cookie-parser` middleware](https://www.npmjs.com/package/cookie-parser)
|
||||
no longer needs to be used for this module to work. This module now directly reads
|
||||
and writes cookies on `req`/`res`. Using `cookie-parser` may result in issues
|
||||
if the `secret` is not the same between this module and `cookie-parser`.
|
||||
|
||||
**Warning** The default server-side session storage, `MemoryStore`, is _purposely_
|
||||
not designed for a production environment. It will leak memory under most
|
||||
conditions, does not scale past a single process, and is meant for debugging and
|
||||
developing.
|
||||
|
||||
For a list of stores, see [compatible session stores](#compatible-session-stores).
|
||||
|
||||
#### Options
|
||||
|
||||
`express-session` accepts these properties in the options object.
|
||||
|
||||
##### cookie
|
||||
|
||||
Settings object for the session ID cookie. The default value is
|
||||
`{ path: '/', httpOnly: true, secure: false, maxAge: null }`.
|
||||
|
||||
The following are options that can be set in this object.
|
||||
|
||||
##### cookie.domain
|
||||
|
||||
Specifies the value for the `Domain` `Set-Cookie` attribute. By default, no domain
|
||||
is set, and most clients will consider the cookie to apply to only the current
|
||||
domain.
|
||||
|
||||
##### cookie.expires
|
||||
|
||||
Specifies the `Date` object to be the value for the `Expires` `Set-Cookie` attribute.
|
||||
By default, no expiration is set, and most clients will consider this a
|
||||
"non-persistent cookie" and will delete it on a condition like exiting a web browser
|
||||
application.
|
||||
|
||||
**Note** If both `expires` and `maxAge` are set in the options, then the last one
|
||||
defined in the object is what is used.
|
||||
|
||||
**Note** The `expires` option should not be set directly; instead only use the `maxAge`
|
||||
option.
|
||||
|
||||
##### cookie.httpOnly
|
||||
|
||||
Specifies the `boolean` value for the `HttpOnly` `Set-Cookie` attribute. When truthy,
|
||||
the `HttpOnly` attribute is set, otherwise it is not. By default, the `HttpOnly`
|
||||
attribute is set.
|
||||
|
||||
**Note** be careful when setting this to `true`, as compliant clients will not allow
|
||||
client-side JavaScript to see the cookie in `document.cookie`.
|
||||
|
||||
##### cookie.maxAge
|
||||
|
||||
Specifies the `number` (in milliseconds) to use when calculating the `Expires`
|
||||
`Set-Cookie` attribute. This is done by taking the current server time and adding
|
||||
`maxAge` milliseconds to the value to calculate an `Expires` datetime. By default,
|
||||
no maximum age is set.
|
||||
|
||||
**Note** If both `expires` and `maxAge` are set in the options, then the last one
|
||||
defined in the object is what is used.
|
||||
|
||||
##### cookie.path
|
||||
|
||||
Specifies the value for the `Path` `Set-Cookie`. By default, this is set to `'/'`, which
|
||||
is the root path of the domain.
|
||||
|
||||
##### cookie.sameSite
|
||||
|
||||
Specifies the `boolean` or `string` to be the value for the `SameSite` `Set-Cookie` attribute.
|
||||
|
||||
- `true` will set the `SameSite` attribute to `Strict` for strict same site enforcement.
|
||||
- `false` will not set the `SameSite` attribute.
|
||||
- `'lax'` will set the `SameSite` attribute to `Lax` for lax same site enforcement.
|
||||
- `'strict'` will set the `SameSite` attribute to `Strict` for strict same site enforcement.
|
||||
|
||||
More information about the different enforcement levels can be found in the specification
|
||||
https://tools.ietf.org/html/draft-west-first-party-cookies-07#section-4.1.1
|
||||
|
||||
**Note** This is an attribute that has not yet been fully standardized, and may change in
|
||||
the future. This also means many clients may ignore this attribute until they understand it.
|
||||
|
||||
##### cookie.secure
|
||||
|
||||
Specifies the `boolean` value for the `Secure` `Set-Cookie` attribute. When truthy,
|
||||
the `Secure` attribute is set, otherwise it is not. By default, the `Secure`
|
||||
attribute is not set.
|
||||
|
||||
**Note** be careful when setting this to `true`, as compliant clients will not send
|
||||
the cookie back to the server in the future if the browser does not have an HTTPS
|
||||
connection.
|
||||
|
||||
Please note that `secure: true` is a **recommended** option. However, it requires
|
||||
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 }
|
||||
}))
|
||||
```
|
||||
|
||||
For using secure cookies in production, but allowing for testing in development,
|
||||
the following is an example of enabling this setup based on `NODE_ENV` in express:
|
||||
|
||||
```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))
|
||||
```
|
||||
|
||||
The `cookie.secure` option can also be set to the special value `'auto'` to have
|
||||
this setting automatically match the determined security of the connection. Be
|
||||
careful when using this setting if the site is available both as HTTP and HTTPS,
|
||||
as once the cookie is set on HTTPS, it will no longer be visible over HTTP. This
|
||||
is useful when the Express `"trust proxy"` setting is properly setup to simplify
|
||||
development vs production configuration.
|
||||
|
||||
##### genid
|
||||
|
||||
Function to call to generate a new session ID. Provide a function that returns
|
||||
a string that will be used as a session ID. The function is given `req` as the
|
||||
first argument if you want to use some value attached to `req` when generating
|
||||
the ID.
|
||||
|
||||
The default value is a function which uses the `uid-safe` library to generate IDs.
|
||||
|
||||
**NOTE** be careful to generate unique IDs so your sessions do not conflict.
|
||||
|
||||
```js
|
||||
app.use(session({
|
||||
genid: function(req) {
|
||||
return genuuid() // use UUIDs for session IDs
|
||||
},
|
||||
secret: 'keyboard cat'
|
||||
}))
|
||||
```
|
||||
|
||||
##### name
|
||||
|
||||
The name of the session ID cookie to set in the response (and read from in the
|
||||
request).
|
||||
|
||||
The default value is `'connect.sid'`.
|
||||
|
||||
**Note** if you have multiple apps running on the same hostname (this is just
|
||||
the name, i.e. `localhost` or `127.0.0.1`; different schemes and ports do not
|
||||
name a different hostname), then you need to separate the session cookies from
|
||||
each other. The simplest method is to simply set different `name`s per app.
|
||||
|
||||
##### proxy
|
||||
|
||||
Trust the reverse proxy when setting secure cookies (via the "X-Forwarded-Proto"
|
||||
header).
|
||||
|
||||
The default value is `undefined`.
|
||||
|
||||
- `true` The "X-Forwarded-Proto" header will be used.
|
||||
- `false` All headers are ignored and the connection is considered secure only
|
||||
if there is a direct TLS/SSL connection.
|
||||
- `undefined` Uses the "trust proxy" setting from express
|
||||
|
||||
##### resave
|
||||
|
||||
Forces the session to be saved back to the session store, even if the session
|
||||
was never modified during the request. Depending on your store this may be
|
||||
necessary, but it can also create race conditions where a client makes two
|
||||
parallel requests to your server and changes made to the session in one
|
||||
request may get overwritten when the other request ends, even if it made no
|
||||
changes (this behavior also depends on what store you're using).
|
||||
|
||||
The default value is `true`, but using the default has been deprecated,
|
||||
as the default will change in the future. Please research into this setting
|
||||
and choose what is appropriate to your use-case. Typically, you'll want
|
||||
`false`.
|
||||
|
||||
How do I know if this is necessary for my store? The best way to know is to
|
||||
check with your store if it implements the `touch` method. If it does, then
|
||||
you can safely set `resave: false`. If it does not implement the `touch`
|
||||
method and your store sets an expiration date on stored sessions, then you
|
||||
likely need `resave: true`.
|
||||
|
||||
##### rolling
|
||||
|
||||
Force a session identifier cookie to be set on every response. The expiration
|
||||
is reset to the original [`maxAge`](#cookiemaxage), resetting the expiration
|
||||
countdown.
|
||||
|
||||
The default value is `false`.
|
||||
|
||||
**Note** When this option is set to `true` but the `saveUninitialized` option is
|
||||
set to `false`, the cookie will not be set on a response with an uninitialized
|
||||
session.
|
||||
|
||||
##### saveUninitialized
|
||||
|
||||
Forces a session that is "uninitialized" to be saved to the store. A session is
|
||||
uninitialized when it is new but not modified. Choosing `false` is useful for
|
||||
implementing login sessions, reducing server storage usage, or complying with
|
||||
laws that require permission before setting a cookie. Choosing `false` will also
|
||||
help with race conditions where a client makes multiple parallel requests
|
||||
without a session.
|
||||
|
||||
The default value is `true`, but using the default has been deprecated, as the
|
||||
default will change in the future. Please research into this setting and
|
||||
choose what is appropriate to your use-case.
|
||||
|
||||
**Note** if you are using Session in conjunction with PassportJS, Passport
|
||||
will add an empty Passport object to the session for use after a user is
|
||||
authenticated, which will be treated as a modification to the session, causing
|
||||
it to be saved. *This has been fixed in PassportJS 0.3.0*
|
||||
|
||||
##### secret
|
||||
|
||||
**Required option**
|
||||
|
||||
This is the secret used to sign the session ID cookie. This can be either a string
|
||||
for a single secret, or an array of multiple secrets. If an array of secrets is
|
||||
provided, only the first element will be used to sign the session ID cookie, while
|
||||
all the elements will be considered when verifying the signature in requests.
|
||||
|
||||
##### store
|
||||
|
||||
The session store instance, defaults to a new `MemoryStore` instance.
|
||||
|
||||
##### unset
|
||||
|
||||
Control the result of unsetting `req.session` (through `delete`, setting to `null`,
|
||||
etc.).
|
||||
|
||||
The default value is `'keep'`.
|
||||
|
||||
- `'destroy'` The session will be destroyed (deleted) when the response ends.
|
||||
- `'keep'` The session in the store will be kept, but modifications made during
|
||||
the request are ignored and not saved.
|
||||
|
||||
### req.session
|
||||
|
||||
To store or access session data, simply use the request property `req.session`,
|
||||
which is (generally) serialized as JSON by the store, so nested objects
|
||||
are typically fine. For example below is a user-specific view counter:
|
||||
|
||||
```js
|
||||
// Use the session middleware
|
||||
app.use(session({ secret: 'keyboard cat', cookie: { maxAge: 60000 }}))
|
||||
|
||||
// Access the session as req.session
|
||||
app.get('/', function(req, res, next) {
|
||||
if (req.session.views) {
|
||||
req.session.views++
|
||||
res.setHeader('Content-Type', 'text/html')
|
||||
res.write('<p>views: ' + req.session.views + '</p>')
|
||||
res.write('<p>expires in: ' + (req.session.cookie.maxAge / 1000) + 's</p>')
|
||||
res.end()
|
||||
} else {
|
||||
req.session.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) {
|
||||
// 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
|
||||
})
|
||||
```
|
||||
|
||||
#### Session.reload(callback)
|
||||
|
||||
Reloads the session data from the store and re-populates the
|
||||
`req.session` object. Once complete, the `callback` will be invoked.
|
||||
|
||||
```js
|
||||
req.session.reload(function(err) {
|
||||
// session updated
|
||||
})
|
||||
```
|
||||
|
||||
#### Session.save(callback)
|
||||
|
||||
Save the session back to the store, replacing the contents on the store with the
|
||||
contents in memory (though a store may do something else--consult the store's
|
||||
documentation for exact behavior).
|
||||
|
||||
This method is automatically called at the end of the HTTP response if the
|
||||
session data has been altered (though this behavior can be altered with various
|
||||
options in the middleware constructor). Because of this, typically this method
|
||||
does not need to be called.
|
||||
|
||||
There are some cases where it is useful to call this method, for example,
|
||||
redirects, long-lived requests or in WebSockets.
|
||||
|
||||
```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 is an
|
||||
alias of [`req.sessionID`](#reqsessionid-1) and cannot be modified.
|
||||
It has been added to make the session ID accessible from the `session`
|
||||
object.
|
||||
|
||||
### req.session.cookie
|
||||
|
||||
Each session has a unique cookie object accompany it. This allows
|
||||
you to alter the session cookie per visitor. For example we can
|
||||
set `req.session.cookie.expires` to `false` to enable the cookie
|
||||
to remain for only the duration of the user-agent.
|
||||
|
||||
#### Cookie.maxAge
|
||||
|
||||
Alternatively `req.session.cookie.maxAge` will return the time
|
||||
remaining in milliseconds, which we may also re-assign a new value
|
||||
to adjust the `.expires` property appropriately. The following
|
||||
are essentially equivalent
|
||||
|
||||
```js
|
||||
var hour = 3600000
|
||||
req.session.cookie.expires = new Date(Date.now() + hour)
|
||||
req.session.cookie.maxAge = hour
|
||||
```
|
||||
|
||||
For example when `maxAge` is set to `60000` (one minute), and 30 seconds
|
||||
has elapsed it will return `30000` until the current request has completed,
|
||||
at which time `req.session.touch()` is called to reset `req.session.maxAge`
|
||||
to its original value.
|
||||
|
||||
```js
|
||||
req.session.cookie.maxAge // => 30000
|
||||
```
|
||||
|
||||
### req.sessionID
|
||||
|
||||
To get the ID of the loaded session, access the request property
|
||||
`req.sessionID`. This is simply a read-only value set when a session
|
||||
is loaded/created.
|
||||
|
||||
## Session Store Implementation
|
||||
|
||||
Every session store _must_ be an `EventEmitter` and implement specific
|
||||
methods. The following methods are the list of **required**, **recommended**,
|
||||
and **optional**.
|
||||
|
||||
* Required methods are ones that this module will always call on the store.
|
||||
* 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)
|
||||
|
||||
**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)
|
||||
|
||||
**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)
|
||||
|
||||
**Required**
|
||||
|
||||
This required method is used to get a session from the store given a session
|
||||
ID (`sid`). The `callback` should be called as `callback(error, session)`.
|
||||
|
||||
The `session` argument should be a session if found, otherwise `null` or
|
||||
`undefined` if the session was not found (and there was no error). A special
|
||||
case is made when `error.code === 'ENOENT'` to act like `callback(null, null)`.
|
||||
|
||||
### store.set(sid, session, callback)
|
||||
|
||||
**Required**
|
||||
|
||||
This required method is used to upsert a session into the store given a
|
||||
session ID (`sid`) and session (`session`) object. The callback should be
|
||||
called as `callback(error)` once the session has been set in the store.
|
||||
|
||||
### store.touch(sid, session, callback)
|
||||
|
||||
**Recommended**
|
||||
|
||||
This recommended method is used to "touch" a given session given a
|
||||
session ID (`sid`) and session (`session`) object. The `callback` should be
|
||||
called as `callback(error)` once the session has been touched.
|
||||
|
||||
This is primarily used when the store will automatically delete idle sessions
|
||||
and this method is used to signal to the store the given session is active,
|
||||
potentially resetting the idle timer.
|
||||
|
||||
## Compatible Session Stores
|
||||
|
||||
The following modules implement a session store that is compatible with this
|
||||
module. Please make a PR to add additional modules :)
|
||||
|
||||
[![★][aerospike-session-store-image] aerospike-session-store][aerospike-session-store-url] A session store using [Aerospike](http://www.aerospike.com/).
|
||||
|
||||
[aerospike-session-store-url]: https://www.npmjs.com/package/aerospike-session-store
|
||||
[aerospike-session-store-image]: https://img.shields.io/github/stars/aerospike/aerospike-session-store-expressjs.svg?label=%E2%98%85
|
||||
|
||||
[![★][cassandra-store-image] cassandra-store][cassandra-store-url] An Apache Cassandra-based session store.
|
||||
|
||||
[cassandra-store-url]: https://www.npmjs.com/package/cassandra-store
|
||||
[cassandra-store-image]: https://img.shields.io/github/stars/webcc/cassandra-store.svg?label=%E2%98%85
|
||||
|
||||
[![★][cluster-store-image] cluster-store][cluster-store-url] A wrapper for using in-process / embedded
|
||||
stores - such as SQLite (via knex), leveldb, files, or memory - with node cluster (desirable for Raspberry Pi 2
|
||||
and other multi-core embedded devices).
|
||||
|
||||
[cluster-store-url]: https://www.npmjs.com/package/cluster-store
|
||||
[cluster-store-image]: https://img.shields.io/github/stars/coolaj86/cluster-store.svg?label=%E2%98%85
|
||||
|
||||
[![★][connect-azuretables-image] connect-azuretables][connect-azuretables-url] An [Azure Table Storage](https://azure.microsoft.com/en-gb/services/storage/tables/)-based session store.
|
||||
|
||||
[connect-azuretables-url]: https://www.npmjs.com/package/connect-azuretables
|
||||
[connect-azuretables-image]: https://img.shields.io/github/stars/mike-goodwin/connect-azuretables.svg?label=%E2%98%85
|
||||
|
||||
[![★][connect-cloudant-store-image] connect-cloudant-store][connect-cloudant-store-url] An [IBM Cloudant](https://cloudant.com/)-based session store.
|
||||
|
||||
[connect-cloudant-store-url]: https://www.npmjs.com/package/connect-cloudant-store
|
||||
[connect-cloudant-store-image]: https://img.shields.io/github/stars/adriantanasa/connect-cloudant-store.svg?label=%E2%98%85
|
||||
|
||||
[![★][connect-couchbase-image] connect-couchbase][connect-couchbase-url] A [couchbase](http://www.couchbase.com/)-based session store.
|
||||
|
||||
[connect-couchbase-url]: https://www.npmjs.com/package/connect-couchbase
|
||||
[connect-couchbase-image]: https://img.shields.io/github/stars/christophermina/connect-couchbase.svg?label=%E2%98%85
|
||||
|
||||
[![★][connect-datacache-image] connect-datacache][connect-datacache-url] An [IBM Bluemix Data Cache](http://www.ibm.com/cloud-computing/bluemix/)-based session store.
|
||||
|
||||
[connect-datacache-url]: https://www.npmjs.com/package/connect-datacache
|
||||
[connect-datacache-image]: https://img.shields.io/github/stars/adriantanasa/connect-datacache.svg?label=%E2%98%85
|
||||
|
||||
[![★][connect-db2-image] connect-db2][connect-db2-url] An IBM DB2-based session store built using [ibm_db](https://www.npmjs.com/package/ibm_db) module.
|
||||
|
||||
[connect-db2-url]: https://www.npmjs.com/package/connect-db2
|
||||
[connect-db2-image]: https://img.shields.io/github/stars/wallali/connect-db2.svg?label=%E2%98%85
|
||||
|
||||
[![★][connect-dynamodb-image] connect-dynamodb][connect-dynamodb-url] A DynamoDB-based session store.
|
||||
|
||||
[connect-dynamodb-url]: https://github.com/ca98am79/connect-dynamodb
|
||||
[connect-dynamodb-image]: https://img.shields.io/github/stars/ca98am79/connect-dynamodb.svg?label=%E2%98%85
|
||||
|
||||
[![★][connect-loki-image] connect-loki][connect-loki-url] A Loki.js-based session store.
|
||||
|
||||
[connect-loki-url]: https://www.npmjs.com/package/connect-loki
|
||||
[connect-loki-image]: https://img.shields.io/github/stars/Requarks/connect-loki.svg?label=%E2%98%85
|
||||
|
||||
[![★][connect-ml-image] connect-ml][connect-ml-url] A MarkLogic Server-based session store.
|
||||
|
||||
[connect-ml-url]: https://www.npmjs.com/package/connect-ml
|
||||
[connect-ml-image]: https://img.shields.io/github/stars/bluetorch/connect-ml.svg?label=%E2%98%85
|
||||
|
||||
[![★][connect-mssql-image] connect-mssql][connect-mssql-url] A SQL Server-based session store.
|
||||
|
||||
[connect-mssql-url]: https://www.npmjs.com/package/connect-mssql
|
||||
[connect-mssql-image]: https://img.shields.io/github/stars/patriksimek/connect-mssql.svg?label=%E2%98%85
|
||||
|
||||
[![★][connect-monetdb-image] connect-monetdb][connect-monetdb-url] A MonetDB-based session store.
|
||||
|
||||
[connect-monetdb-url]: https://www.npmjs.com/package/connect-monetdb
|
||||
[connect-monetdb-image]: https://img.shields.io/github/stars/MonetDB/npm-connect-monetdb.svg?label=%E2%98%85
|
||||
|
||||
[![★][connect-mongo-image] connect-mongo][connect-mongo-url] A MongoDB-based session store.
|
||||
|
||||
[connect-mongo-url]: https://www.npmjs.com/package/connect-mongo
|
||||
[connect-mongo-image]: https://img.shields.io/github/stars/kcbanner/connect-mongo.svg?label=%E2%98%85
|
||||
|
||||
[![★][connect-mongodb-session-image] connect-mongodb-session][connect-mongodb-session-url] Lightweight MongoDB-based session store built and maintained by MongoDB.
|
||||
|
||||
[connect-mongodb-session-url]: https://www.npmjs.com/package/connect-mongodb-session
|
||||
[connect-mongodb-session-image]: https://img.shields.io/github/stars/mongodb-js/connect-mongodb-session.svg?label=%E2%98%85
|
||||
|
||||
[![★][connect-pg-simple-image] connect-pg-simple][connect-pg-simple-url] A PostgreSQL-based session store.
|
||||
|
||||
[connect-pg-simple-url]: https://www.npmjs.com/package/connect-pg-simple
|
||||
[connect-pg-simple-image]: https://img.shields.io/github/stars/voxpelli/node-connect-pg-simple.svg?label=%E2%98%85
|
||||
|
||||
[![★][connect-redis-image] connect-redis][connect-redis-url] A Redis-based session store.
|
||||
|
||||
[connect-redis-url]: https://www.npmjs.com/package/connect-redis
|
||||
[connect-redis-image]: https://img.shields.io/github/stars/tj/connect-redis.svg?label=%E2%98%85
|
||||
|
||||
[![★][connect-memcached-image] connect-memcached][connect-memcached-url] A memcached-based session store.
|
||||
|
||||
[connect-memcached-url]: https://www.npmjs.com/package/connect-memcached
|
||||
[connect-memcached-image]: https://img.shields.io/github/stars/balor/connect-memcached.svg?label=%E2%98%85
|
||||
|
||||
[![★][connect-memjs-image] connect-memjs][connect-memjs-url] A memcached-based session store using
|
||||
[memjs](https://www.npmjs.com/package/memjs) as the memcached client.
|
||||
|
||||
[connect-memjs-url]: https://www.npmjs.com/package/connect-memjs
|
||||
[connect-memjs-image]: https://img.shields.io/github/stars/liamdon/connect-memjs.svg?label=%E2%98%85
|
||||
|
||||
[![★][connect-session-knex-image] connect-session-knex][connect-session-knex-url] A session store using
|
||||
[Knex.js](http://knexjs.org/), which is a SQL query builder for PostgreSQL, MySQL, MariaDB, SQLite3, and Oracle.
|
||||
|
||||
[connect-session-knex-url]: https://www.npmjs.com/package/connect-session-knex
|
||||
[connect-session-knex-image]: https://img.shields.io/github/stars/llambda/connect-session-knex.svg?label=%E2%98%85
|
||||
|
||||
[![★][connect-session-sequelize-image] connect-session-sequelize][connect-session-sequelize-url] A session store using
|
||||
[Sequelize.js](http://sequelizejs.com/), which is a Node.js / io.js ORM for PostgreSQL, MySQL, SQLite and MSSQL.
|
||||
|
||||
[connect-session-sequelize-url]: https://www.npmjs.com/package/connect-session-sequelize
|
||||
[connect-session-sequelize-image]: https://img.shields.io/github/stars/mweibel/connect-session-sequelize.svg?label=%E2%98%85
|
||||
|
||||
[![★][dynamodb-store-image] dynamodb-store][dynamodb-store-url] A DynamoDB-based session store.
|
||||
|
||||
[dynamodb-store-url]: https://www.npmjs.com/package/dynamodb-store
|
||||
[dynamodb-store-image]: https://img.shields.io/github/stars/rafaelrpinto/dynamodb-store.svg?label=%E2%98%85
|
||||
|
||||
[![★][express-mysql-session-image] express-mysql-session][express-mysql-session-url] A session store using native
|
||||
[MySQL](https://www.mysql.com/) via the [node-mysql](https://github.com/felixge/node-mysql) module.
|
||||
|
||||
[express-mysql-session-url]: https://www.npmjs.com/package/express-mysql-session
|
||||
[express-mysql-session-image]: https://img.shields.io/github/stars/chill117/express-mysql-session.svg?label=%E2%98%85
|
||||
|
||||
[![★][express-oracle-session-image] express-oracle-session][express-oracle-session-url] A session store using native
|
||||
[oracle](https://www.oracle.com/) via the [node-oracledb](https://www.npmjs.com/package/oracledb) module.
|
||||
|
||||
[express-oracle-session-url]: https://www.npmjs.com/package/express-oracle-session
|
||||
[express-oracle-session-image]: https://img.shields.io/github/stars/slumber86/express-oracle-session.svg?label=%E2%98%85
|
||||
|
||||
[![★][express-sessions-image] express-sessions][express-sessions-url]: A session store supporting both MongoDB and Redis.
|
||||
|
||||
[express-sessions-url]: https://www.npmjs.com/package/express-sessions
|
||||
[express-sessions-image]: https://img.shields.io/github/stars/konteck/express-sessions.svg?label=%E2%98%85
|
||||
|
||||
[![★][connect-sqlite3-image] connect-sqlite3][connect-sqlite3-url] A [SQLite3](https://github.com/mapbox/node-sqlite3) session store modeled after the TJ's `connect-redis` store.
|
||||
|
||||
[connect-sqlite3-url]: https://www.npmjs.com/package/connect-sqlite3
|
||||
[connect-sqlite3-image]: https://img.shields.io/github/stars/rawberg/connect-sqlite3.svg?label=%E2%98%85
|
||||
|
||||
[![★][documentdb-session-image] documentdb-session][documentdb-session-url] A session store for Microsoft Azure's [DocumentDB](https://azure.microsoft.com/en-us/services/documentdb/) NoSQL database service.
|
||||
|
||||
[documentdb-session-url]: https://www.npmjs.com/package/documentdb-session
|
||||
[documentdb-session-image]: https://img.shields.io/github/stars/dwhieb/documentdb-session.svg?label=%E2%98%85
|
||||
|
||||
[![★][express-nedb-session-image] express-nedb-session][express-nedb-session-url] A NeDB-based session store.
|
||||
|
||||
[express-nedb-session-url]: https://www.npmjs.com/package/express-nedb-session
|
||||
[express-nedb-session-image]: https://img.shields.io/github/stars/louischatriot/express-nedb-session.svg?label=%E2%98%85
|
||||
|
||||
[![★][express-session-cache-manager-image] express-session-cache-manager][express-session-cache-manager-url]
|
||||
A store that implements [cache-manager](https://www.npmjs.com/package/cache-manager), which supports
|
||||
a [variety of storage types](https://www.npmjs.com/package/cache-manager#store-engines).
|
||||
|
||||
[express-session-cache-manager-url]: https://www.npmjs.com/package/express-session-cache-manager
|
||||
[express-session-cache-manager-image]: https://img.shields.io/github/stars/theogravity/express-session-cache-manager.svg?label=%E2%98%85
|
||||
|
||||
[![★][express-session-level-image] express-session-level][express-session-level-url] A [LevelDB](https://github.com/Level/levelup) based session store.
|
||||
|
||||
[express-session-level-url]: https://www.npmjs.com/package/express-session-level
|
||||
[express-session-level-image]: https://img.shields.io/github/stars/tgohn/express-session-level.svg?label=%E2%98%85
|
||||
|
||||
[![★][express-etcd-image] express-etcd][express-etcd-url] An [etcd](https://github.com/stianeikeland/node-etcd) based session store.
|
||||
|
||||
[express-etcd-url]: https://www.npmjs.com/package/express-etcd
|
||||
[express-etcd-image]: https://img.shields.io/github/stars/gildean/express-etcd.svg?label=%E2%98%85
|
||||
|
||||
[![★][fortune-session-image] fortune-session][fortune-session-url] A [Fortune.js](https://github.com/fortunejs/fortune)
|
||||
based session store. Supports all backends supported by Fortune (MongoDB, Redis, Postgres, NeDB).
|
||||
|
||||
[fortune-session-url]: https://www.npmjs.com/package/fortune-session
|
||||
[fortune-session-image]: https://img.shields.io/github/stars/aliceklipper/fortune-session.svg?label=%E2%98%85
|
||||
|
||||
[![★][hazelcast-store-image] hazelcast-store][hazelcast-store-url] A Hazelcast-based session store built on the [Hazelcast Node Client](https://www.npmjs.com/package/hazelcast-client).
|
||||
|
||||
[hazelcast-store-url]: https://www.npmjs.com/package/hazelcast-store
|
||||
[hazelcast-store-image]: https://img.shields.io/github/stars/jackspaniel/hazelcast-store.svg?label=%E2%98%85
|
||||
|
||||
[![★][level-session-store-image] level-session-store][level-session-store-url] A LevelDB-based session store.
|
||||
|
||||
[level-session-store-url]: https://www.npmjs.com/package/level-session-store
|
||||
[level-session-store-image]: https://img.shields.io/github/stars/scriptollc/level-session-store.svg?label=%E2%98%85
|
||||
|
||||
[![★][medea-session-store-image] medea-session-store][medea-session-store-url] A Medea-based session store.
|
||||
|
||||
[medea-session-store-url]: https://www.npmjs.com/package/medea-session-store
|
||||
[medea-session-store-image]: https://img.shields.io/github/stars/BenjaminVadant/medea-session-store.svg?label=%E2%98%85
|
||||
|
||||
[![★][memorystore-image] memorystore][memorystore-url] A memory session store made for production.
|
||||
|
||||
[memorystore-url]: https://www.npmjs.com/package/memorystore
|
||||
[memorystore-image]: https://img.shields.io/github/stars/roccomuso/memorystore.svg?label=%E2%98%85
|
||||
|
||||
[![★][mssql-session-store-image] mssql-session-store][mssql-session-store-url] A SQL Server-based session store.
|
||||
|
||||
[mssql-session-store-url]: https://www.npmjs.com/package/mssql-session-store
|
||||
[mssql-session-store-image]: https://img.shields.io/github/stars/jwathen/mssql-session-store.svg?label=%E2%98%85
|
||||
|
||||
[![★][nedb-session-store-image] nedb-session-store][nedb-session-store-url] An alternate NeDB-based (either in-memory or file-persisted) session store.
|
||||
|
||||
[nedb-session-store-url]: https://www.npmjs.com/package/nedb-session-store
|
||||
[nedb-session-store-image]: https://img.shields.io/github/stars/JamesMGreene/nedb-session-store.svg?label=%E2%98%85
|
||||
|
||||
[![★][sequelstore-connect-image] sequelstore-connect][sequelstore-connect-url] A session store using [Sequelize.js](http://sequelizejs.com/).
|
||||
|
||||
[sequelstore-connect-url]: https://www.npmjs.com/package/sequelstore-connect
|
||||
[sequelstore-connect-image]: https://img.shields.io/github/stars/MattMcFarland/sequelstore-connect.svg?label=%E2%98%85
|
||||
|
||||
[![★][session-file-store-image] session-file-store][session-file-store-url] A file system-based session store.
|
||||
|
||||
[session-file-store-url]: https://www.npmjs.com/package/session-file-store
|
||||
[session-file-store-image]: https://img.shields.io/github/stars/valery-barysok/session-file-store.svg?label=%E2%98%85
|
||||
|
||||
[![★][session-rethinkdb-image] session-rethinkdb][session-rethinkdb-url] A [RethinkDB](http://rethinkdb.com/)-based session store.
|
||||
|
||||
[session-rethinkdb-url]: https://www.npmjs.com/package/session-rethinkdb
|
||||
[session-rethinkdb-image]: https://img.shields.io/github/stars/llambda/session-rethinkdb.svg?label=%E2%98%85
|
||||
|
||||
## Example
|
||||
|
||||
A simple example using `express-session` to store page views for a user.
|
||||
|
||||
```js
|
||||
var express = require('express')
|
||||
var parseurl = require('parseurl')
|
||||
var session = require('express-session')
|
||||
|
||||
var app = express()
|
||||
|
||||
app.use(session({
|
||||
secret: 'keyboard cat',
|
||||
resave: false,
|
||||
saveUninitialized: true
|
||||
}))
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
if (!req.session.views) {
|
||||
req.session.views = {}
|
||||
}
|
||||
|
||||
// get the url pathname
|
||||
var pathname = parseurl(req).pathname
|
||||
|
||||
// count the views
|
||||
req.session.views[pathname] = (req.session.views[pathname] || 0) + 1
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
app.get('/foo', function (req, res, next) {
|
||||
res.send('you viewed this page ' + req.session.views['/foo'] + ' times')
|
||||
})
|
||||
|
||||
app.get('/bar', function (req, res, next) {
|
||||
res.send('you viewed this page ' + req.session.views['/bar'] + ' times')
|
||||
})
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
[npm-image]: https://img.shields.io/npm/v/express-session.svg
|
||||
[npm-url]: https://npmjs.org/package/express-session
|
||||
[travis-image]: https://img.shields.io/travis/expressjs/session/master.svg
|
||||
[travis-url]: https://travis-ci.org/expressjs/session
|
||||
[coveralls-image]: https://img.shields.io/coveralls/expressjs/session/master.svg
|
||||
[coveralls-url]: https://coveralls.io/r/expressjs/session?branch=master
|
||||
[downloads-image]: https://img.shields.io/npm/dm/express-session.svg
|
||||
[downloads-url]: https://npmjs.org/package/express-session
|
||||
[gratipay-image]: https://img.shields.io/gratipay/dougwilson.svg
|
||||
[gratipay-url]: https://gratipay.com/dougwilson/
|
||||
+664
@@ -0,0 +1,664 @@
|
||||
/*!
|
||||
* express-session
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var cookie = require('cookie');
|
||||
var crc = require('crc').crc32;
|
||||
var debug = require('debug')('express-session');
|
||||
var deprecate = require('depd')('express-session');
|
||||
var parseUrl = require('parseurl');
|
||||
var uid = require('uid-safe').sync
|
||||
, onHeaders = require('on-headers')
|
||||
, signature = require('cookie-signature')
|
||||
|
||||
var Session = require('./session/session')
|
||||
, MemoryStore = require('./session/memory')
|
||||
, Cookie = require('./session/cookie')
|
||||
, Store = require('./session/store')
|
||||
|
||||
// environment
|
||||
|
||||
var env = process.env.NODE_ENV;
|
||||
|
||||
/**
|
||||
* Expose the middleware.
|
||||
*/
|
||||
|
||||
exports = module.exports = session;
|
||||
|
||||
/**
|
||||
* Expose constructors.
|
||||
*/
|
||||
|
||||
exports.Store = Store;
|
||||
exports.Cookie = Cookie;
|
||||
exports.Session = Session;
|
||||
exports.MemoryStore = MemoryStore;
|
||||
|
||||
/**
|
||||
* Warning message for `MemoryStore` usage in production.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var warning = 'Warning: connect.session() MemoryStore is not\n'
|
||||
+ 'designed for a production environment, as it will leak\n'
|
||||
+ 'memory, and will not scale past a single process.';
|
||||
|
||||
/**
|
||||
* Node.js 0.8+ async implementation.
|
||||
* @private
|
||||
*/
|
||||
|
||||
/* istanbul ignore next */
|
||||
var defer = typeof setImmediate === 'function'
|
||||
? setImmediate
|
||||
: function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) }
|
||||
|
||||
/**
|
||||
* Setup session store with the given `options`.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* @param {Object} [options.cookie] Options for cookie
|
||||
* @param {Function} [options.genid]
|
||||
* @param {String} [options.name=connect.sid] Session ID cookie name
|
||||
* @param {Boolean} [options.proxy]
|
||||
* @param {Boolean} [options.resave] Resave unmodified sessions back to the store
|
||||
* @param {Boolean} [options.rolling] Enable/disable rolling session expiration
|
||||
* @param {Boolean} [options.saveUninitialized] Save uninitialized sessions to the store
|
||||
* @param {String|Array} [options.secret] Secret for signing session ID
|
||||
* @param {Object} [options.store=MemoryStore] Session store
|
||||
* @param {String} [options.unset]
|
||||
* @return {Function} middleware
|
||||
* @public
|
||||
*/
|
||||
|
||||
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) {
|
||||
// touch session
|
||||
req.session.touch()
|
||||
touched = true
|
||||
}
|
||||
|
||||
// set cookie
|
||||
setcookie(res, name, req.sessionID, secrets[0], req.session.cookie.data);
|
||||
});
|
||||
|
||||
// proxy end() to commit the session
|
||||
var _end = res.end;
|
||||
var _write = res.write;
|
||||
var ended = false;
|
||||
res.end = function end(chunk, encoding) {
|
||||
if (ended) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ended = true;
|
||||
|
||||
var ret;
|
||||
var sync = true;
|
||||
|
||||
function writeend() {
|
||||
if (sync) {
|
||||
ret = _end.call(res, chunk, encoding);
|
||||
sync = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_end.call(res);
|
||||
}
|
||||
|
||||
function writetop() {
|
||||
if (!sync) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (chunk == null) {
|
||||
ret = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
var contentLength = Number(res.getHeader('Content-Length'));
|
||||
|
||||
if (!isNaN(contentLength) && contentLength > 0) {
|
||||
// measure chunk
|
||||
chunk = !Buffer.isBuffer(chunk)
|
||||
? new Buffer(chunk, encoding)
|
||||
: chunk;
|
||||
encoding = undefined;
|
||||
|
||||
if (chunk.length !== 0) {
|
||||
debug('split response');
|
||||
ret = _write.call(res, chunk.slice(0, chunk.length - 1));
|
||||
chunk = chunk.slice(chunk.length - 1, chunk.length);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = _write.call(res, chunk, encoding);
|
||||
sync = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (shouldDestroy(req)) {
|
||||
// destroy session
|
||||
debug('destroying');
|
||||
store.destroy(req.sessionID, function ondestroy(err) {
|
||||
if (err) {
|
||||
defer(next, err);
|
||||
}
|
||||
|
||||
debug('destroyed');
|
||||
writeend();
|
||||
});
|
||||
|
||||
return writetop();
|
||||
}
|
||||
|
||||
// no session to save
|
||||
if (!req.session) {
|
||||
debug('no session');
|
||||
return _end.call(res, chunk, encoding);
|
||||
}
|
||||
|
||||
if (!touched) {
|
||||
// touch session
|
||||
req.session.touch()
|
||||
touched = true
|
||||
}
|
||||
|
||||
if (shouldSave(req)) {
|
||||
req.session.save(function onsave(err) {
|
||||
if (err) {
|
||||
defer(next, err);
|
||||
}
|
||||
|
||||
writeend();
|
||||
});
|
||||
|
||||
return writetop();
|
||||
} else if (storeImplementsTouch && shouldTouch(req)) {
|
||||
// store implements touch method
|
||||
debug('touching');
|
||||
store.touch(req.sessionID, req.session, function ontouch(err) {
|
||||
if (err) {
|
||||
defer(next, err);
|
||||
}
|
||||
|
||||
debug('touched');
|
||||
writeend();
|
||||
});
|
||||
|
||||
return writetop();
|
||||
}
|
||||
|
||||
return _end.call(res, chunk, encoding);
|
||||
};
|
||||
|
||||
// generate the session
|
||||
function generate() {
|
||||
store.generate(req);
|
||||
originalId = req.sessionID;
|
||||
originalHash = hash(req.session);
|
||||
wrapmethods(req.session);
|
||||
}
|
||||
|
||||
// wrap session methods
|
||||
function wrapmethods(sess) {
|
||||
var _reload = sess.reload
|
||||
var _save = sess.save;
|
||||
|
||||
function reload(callback) {
|
||||
debug('reloading %s', this.id)
|
||||
_reload.call(this, function () {
|
||||
wrapmethods(req.session)
|
||||
callback.apply(this, arguments)
|
||||
})
|
||||
}
|
||||
|
||||
function save() {
|
||||
debug('saving %s', this.id);
|
||||
savedHash = hash(this);
|
||||
_save.apply(this, arguments);
|
||||
}
|
||||
|
||||
Object.defineProperty(sess, 'reload', {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: reload,
|
||||
writable: true
|
||||
})
|
||||
|
||||
Object.defineProperty(sess, 'save', {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: save,
|
||||
writable: true
|
||||
});
|
||||
}
|
||||
|
||||
// check if session has been modified
|
||||
function isModified(sess) {
|
||||
return originalId !== sess.id || originalHash !== hash(sess);
|
||||
}
|
||||
|
||||
// check if session has been saved
|
||||
function isSaved(sess) {
|
||||
return originalId === sess.id && savedHash === hash(sess);
|
||||
}
|
||||
|
||||
// determine if session should be destroyed
|
||||
function shouldDestroy(req) {
|
||||
return req.sessionID && unsetDestroy && req.session == null;
|
||||
}
|
||||
|
||||
// determine if session should be saved to store
|
||||
function shouldSave(req) {
|
||||
// cannot set cookie without a session ID
|
||||
if (typeof req.sessionID !== 'string') {
|
||||
debug('session ignored because of bogus req.sessionID %o', req.sessionID);
|
||||
return false;
|
||||
}
|
||||
|
||||
return !saveUninitializedSession && cookieId !== req.sessionID
|
||||
? isModified(req.session)
|
||||
: !isSaved(req.session)
|
||||
}
|
||||
|
||||
// determine if session should be touched
|
||||
function shouldTouch(req) {
|
||||
// cannot set cookie without a session ID
|
||||
if (typeof req.sessionID !== 'string') {
|
||||
debug('session ignored because of bogus req.sessionID %o', req.sessionID);
|
||||
return false;
|
||||
}
|
||||
|
||||
return cookieId === req.sessionID && !shouldSave(req);
|
||||
}
|
||||
|
||||
// determine if cookie should be set on response
|
||||
function shouldSetCookie(req) {
|
||||
// cannot set cookie without a session ID
|
||||
if (typeof req.sessionID !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return cookieId != req.sessionID
|
||||
? saveUninitializedSession || isModified(req.session)
|
||||
: rollingSessions || req.session.cookie.expires != null && isModified(req.session);
|
||||
}
|
||||
|
||||
// generate a session if the browser doesn't send a sessionID
|
||||
if (!req.sessionID) {
|
||||
debug('no SID sent, generating session');
|
||||
generate();
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
// generate the session object
|
||||
debug('fetching %s', req.sessionID);
|
||||
store.get(req.sessionID, function(err, sess){
|
||||
// error handling
|
||||
if (err) {
|
||||
debug('error %j', err);
|
||||
|
||||
if (err.code !== 'ENOENT') {
|
||||
next(err);
|
||||
return;
|
||||
}
|
||||
|
||||
generate();
|
||||
// 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
|
||||
}
|
||||
|
||||
wrapmethods(req.session);
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate a session ID for a new session.
|
||||
*
|
||||
* @return {String}
|
||||
* @private
|
||||
*/
|
||||
|
||||
function generateSessionId(sess) {
|
||||
return uid(24);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the session ID cookie from request.
|
||||
*
|
||||
* @return {string}
|
||||
* @private
|
||||
*/
|
||||
|
||||
function getcookie(req, name, secrets) {
|
||||
var header = req.headers.cookie;
|
||||
var raw;
|
||||
var val;
|
||||
|
||||
// read from cookie header
|
||||
if (header) {
|
||||
var cookies = cookie.parse(header);
|
||||
|
||||
raw = cookies[name];
|
||||
|
||||
if (raw) {
|
||||
if (raw.substr(0, 2) === 's:') {
|
||||
val = unsigncookie(raw.slice(2), secrets);
|
||||
|
||||
if (val === false) {
|
||||
debug('cookie signature invalid');
|
||||
val = undefined;
|
||||
}
|
||||
} else {
|
||||
debug('cookie unsigned')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// back-compat read from cookieParser() signedCookies data
|
||||
if (!val && req.signedCookies) {
|
||||
val = req.signedCookies[name];
|
||||
|
||||
if (val) {
|
||||
deprecate('cookie should be available in req.headers.cookie');
|
||||
}
|
||||
}
|
||||
|
||||
// back-compat read from cookieParser() cookies data
|
||||
if (!val && req.cookies) {
|
||||
raw = req.cookies[name];
|
||||
|
||||
if (raw) {
|
||||
if (raw.substr(0, 2) === 's:') {
|
||||
val = unsigncookie(raw.slice(2), secrets);
|
||||
|
||||
if (val) {
|
||||
deprecate('cookie should be available in req.headers.cookie');
|
||||
}
|
||||
|
||||
if (val === false) {
|
||||
debug('cookie signature invalid');
|
||||
val = undefined;
|
||||
}
|
||||
} else {
|
||||
debug('cookie unsigned')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash the given `sess` object omitting changes to `.cookie`.
|
||||
*
|
||||
* @param {Object} sess
|
||||
* @return {String}
|
||||
* @private
|
||||
*/
|
||||
|
||||
function hash(sess) {
|
||||
return crc(JSON.stringify(sess, function (key, val) {
|
||||
// ignore sess.cookie property
|
||||
if (this === sess && key === 'cookie') {
|
||||
return
|
||||
}
|
||||
|
||||
return val
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if request is secure.
|
||||
*
|
||||
* @param {Object} req
|
||||
* @param {Boolean} [trustProxy]
|
||||
* @return {Boolean}
|
||||
* @private
|
||||
*/
|
||||
|
||||
function issecure(req, trustProxy) {
|
||||
// socket is https server
|
||||
if (req.connection && req.connection.encrypted) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// do not trust proxy
|
||||
if (trustProxy === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// no explicit trust; try req.secure from express
|
||||
if (trustProxy !== true) {
|
||||
var secure = req.secure;
|
||||
return typeof secure === 'boolean'
|
||||
? secure
|
||||
: false;
|
||||
}
|
||||
|
||||
// read the proto from x-forwarded-proto header
|
||||
var header = req.headers['x-forwarded-proto'] || '';
|
||||
var index = header.indexOf(',');
|
||||
var proto = index !== -1
|
||||
? header.substr(0, index).toLowerCase().trim()
|
||||
: header.toLowerCase().trim()
|
||||
|
||||
return proto === 'https';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify and decode the given `val` with `secrets`.
|
||||
*
|
||||
* @param {String} val
|
||||
* @param {Array} secrets
|
||||
* @returns {String|Boolean}
|
||||
* @private
|
||||
*/
|
||||
function unsigncookie(val, secrets) {
|
||||
for (var i = 0; i < secrets.length; i++) {
|
||||
var result = signature.unsign(val, secrets[i]);
|
||||
|
||||
if (result !== false) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
{
|
||||
"_args": [
|
||||
[
|
||||
{
|
||||
"raw": "express-session",
|
||||
"scope": null,
|
||||
"escapedName": "express-session",
|
||||
"name": "express-session",
|
||||
"rawSpec": "",
|
||||
"spec": "latest",
|
||||
"type": "tag"
|
||||
},
|
||||
"/Users/gerrit/Documents/dev/nodejs/rentfor.camp/html/RentForCamp"
|
||||
]
|
||||
],
|
||||
"_from": "express-session@latest",
|
||||
"_id": "express-session@1.15.6",
|
||||
"_inCache": true,
|
||||
"_location": "/express-session",
|
||||
"_nodeVersion": "6.11.3",
|
||||
"_npmOperationalInternal": {
|
||||
"host": "s3://npm-registry-packages",
|
||||
"tmp": "tmp/express-session-1.15.6.tgz_1506453181688_0.8409461784176528"
|
||||
},
|
||||
"_npmUser": {
|
||||
"name": "dougwilson",
|
||||
"email": "doug@somethingdoug.com"
|
||||
},
|
||||
"_npmVersion": "5.3.0",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"raw": "express-session",
|
||||
"scope": null,
|
||||
"escapedName": "express-session",
|
||||
"name": "express-session",
|
||||
"rawSpec": "",
|
||||
"spec": "latest",
|
||||
"type": "tag"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"#USER",
|
||||
"/"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/express-session/-/express-session-1.15.6.tgz",
|
||||
"_shasum": "47b4160c88f42ab70fe8a508e31cbff76757ab0a",
|
||||
"_shrinkwrap": null,
|
||||
"_spec": "express-session",
|
||||
"_where": "/Users/gerrit/Documents/dev/nodejs/rentfor.camp/html/RentForCamp",
|
||||
"author": {
|
||||
"name": "TJ Holowaychuk",
|
||||
"email": "tj@vision-media.ca",
|
||||
"url": "http://tjholowaychuk.com"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/expressjs/session/issues"
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Douglas Christopher Wilson",
|
||||
"email": "doug@somethingdoug.com"
|
||||
},
|
||||
{
|
||||
"name": "Joe Wagner",
|
||||
"email": "njwjs722@gmail.com"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"cookie": "0.3.1",
|
||||
"cookie-signature": "1.0.6",
|
||||
"crc": "3.4.4",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~1.1.1",
|
||||
"on-headers": "~1.0.1",
|
||||
"parseurl": "~1.3.2",
|
||||
"uid-safe": "~2.1.5",
|
||||
"utils-merge": "1.0.1"
|
||||
},
|
||||
"description": "Simple session middleware for Express",
|
||||
"devDependencies": {
|
||||
"after": "0.8.2",
|
||||
"cookie-parser": "1.4.3",
|
||||
"eslint": "3.19.0",
|
||||
"eslint-plugin-markdown": "1.0.0-beta.6",
|
||||
"express": "4.15.5",
|
||||
"istanbul": "0.4.5",
|
||||
"mocha": "2.5.3",
|
||||
"supertest": "1.1.0"
|
||||
},
|
||||
"directories": {},
|
||||
"dist": {
|
||||
"integrity": "sha512-r0nrHTCYtAMrFwZ0kBzZEXa1vtPVrw0dKvGSrKP4dahwBQ1BJpF2/y1Pp4sCD/0kvxV4zZeclyvfmw0B4RMJQA==",
|
||||
"shasum": "47b4160c88f42ab70fe8a508e31cbff76757ab0a",
|
||||
"tarball": "https://registry.npmjs.org/express-session/-/express-session-1.15.6.tgz"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
},
|
||||
"files": [
|
||||
"session/",
|
||||
"HISTORY.md",
|
||||
"LICENSE",
|
||||
"index.js"
|
||||
],
|
||||
"gitHead": "89fd7156129210f2b0c350afcbdf226665a8328c",
|
||||
"homepage": "https://github.com/expressjs/session#readme",
|
||||
"license": "MIT",
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "defunctzombie",
|
||||
"email": "shtylman@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "dougwilson",
|
||||
"email": "doug@somethingdoug.com"
|
||||
},
|
||||
{
|
||||
"name": "mscdex",
|
||||
"email": "mscdex@mscdex.net"
|
||||
}
|
||||
],
|
||||
"name": "express-session",
|
||||
"optionalDependencies": {},
|
||||
"readme": "ERROR: No README data found!",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/expressjs/session.git"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint --plugin markdown --ext js,md .",
|
||||
"test": "mocha --check-leaks --bail --no-exit --reporter spec test/",
|
||||
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --check-leaks --no-exit --reporter dot test/",
|
||||
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --check-leaks --no-exit --reporter spec test/"
|
||||
},
|
||||
"version": "1.15.6"
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
/*!
|
||||
* Connect - session - Cookie
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var merge = require('utils-merge')
|
||||
, cookie = require('cookie');
|
||||
|
||||
/**
|
||||
* Initialize a new `Cookie` with the given `options`.
|
||||
*
|
||||
* @param {IncomingMessage} req
|
||||
* @param {Object} options
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var Cookie = module.exports = 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;
|
||||
};
|
||||
|
||||
/*!
|
||||
* Prototype.
|
||||
*/
|
||||
|
||||
Cookie.prototype = {
|
||||
|
||||
/**
|
||||
* Set expires `date`.
|
||||
*
|
||||
* @param {Date} date
|
||||
* @api public
|
||||
*/
|
||||
|
||||
set expires(date) {
|
||||
this._expires = date;
|
||||
this.originalMaxAge = this.maxAge;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get expires `date`.
|
||||
*
|
||||
* @return {Date}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
get expires() {
|
||||
return this._expires;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set expires via max-age in `ms`.
|
||||
*
|
||||
* @param {Number} ms
|
||||
* @api public
|
||||
*/
|
||||
|
||||
set maxAge(ms) {
|
||||
this.expires = 'number' == typeof ms
|
||||
? new Date(Date.now() + ms)
|
||||
: ms;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get expires max-age in `ms`.
|
||||
*
|
||||
* @return {Number}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
get maxAge() {
|
||||
return this.expires instanceof Date
|
||||
? this.expires.valueOf() - Date.now()
|
||||
: this.expires;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return cookie data object.
|
||||
*
|
||||
* @return {Object}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
get data() {
|
||||
return {
|
||||
originalMaxAge: this.originalMaxAge
|
||||
, expires: this._expires
|
||||
, secure: this.secure
|
||||
, httpOnly: this.httpOnly
|
||||
, domain: this.domain
|
||||
, path: this.path
|
||||
, sameSite: this.sameSite
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a serialized cookie string.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
serialize: function(name, val){
|
||||
return cookie.serialize(name, val, this.data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return JSON representation of this cookie.
|
||||
*
|
||||
* @return {Object}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
toJSON: function(){
|
||||
return this.data;
|
||||
}
|
||||
};
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
/*!
|
||||
* express-session
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* Copyright(c) 2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var Store = require('./store')
|
||||
var util = require('util')
|
||||
|
||||
/**
|
||||
* Shim setImmediate for node.js < 0.10
|
||||
* @private
|
||||
*/
|
||||
|
||||
/* istanbul ignore next */
|
||||
var defer = typeof setImmediate === 'function'
|
||||
? setImmediate
|
||||
: function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) }
|
||||
|
||||
/**
|
||||
* Module exports.
|
||||
*/
|
||||
|
||||
module.exports = MemoryStore
|
||||
|
||||
/**
|
||||
* A session store in memory.
|
||||
* @public
|
||||
*/
|
||||
|
||||
function MemoryStore() {
|
||||
Store.call(this)
|
||||
this.sessions = Object.create(null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from Store.
|
||||
*/
|
||||
|
||||
util.inherits(MemoryStore, Store)
|
||||
|
||||
/**
|
||||
* Get all active sessions.
|
||||
*
|
||||
* @param {function} callback
|
||||
* @public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.all = 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all sessions.
|
||||
*
|
||||
* @param {function} callback
|
||||
* @public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.clear = function clear(callback) {
|
||||
this.sessions = Object.create(null)
|
||||
callback && defer(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the session associated with the given session ID.
|
||||
*
|
||||
* @param {string} sessionId
|
||||
* @public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.destroy = function destroy(sessionId, callback) {
|
||||
delete this.sessions[sessionId]
|
||||
callback && defer(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch session by the given session ID.
|
||||
*
|
||||
* @param {string} sessionId
|
||||
* @param {function} callback
|
||||
* @public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.get = function get(sessionId, callback) {
|
||||
defer(callback, null, getSession.call(this, sessionId))
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit the given session associated with the given sessionId to the store.
|
||||
*
|
||||
* @param {string} sessionId
|
||||
* @param {object} session
|
||||
* @param {function} callback
|
||||
* @public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.set = function set(sessionId, session, callback) {
|
||||
this.sessions[sessionId] = JSON.stringify(session)
|
||||
callback && defer(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of active sessions.
|
||||
*
|
||||
* @param {function} callback
|
||||
* @public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.length = function length(callback) {
|
||||
this.all(function (err, sessions) {
|
||||
if (err) return callback(err)
|
||||
callback(null, Object.keys(sessions).length)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Touch the given session object associated with the given session ID.
|
||||
*
|
||||
* @param {string} sessionId
|
||||
* @param {object} session
|
||||
* @param {function} callback
|
||||
* @public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.touch = 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get session from the store.
|
||||
* @private
|
||||
*/
|
||||
|
||||
function getSession(sessionId) {
|
||||
var sess = this.sessions[sessionId]
|
||||
|
||||
if (!sess) {
|
||||
return
|
||||
}
|
||||
|
||||
// parse
|
||||
sess = JSON.parse(sess)
|
||||
|
||||
var expires = typeof sess.cookie.expires === 'string'
|
||||
? new Date(sess.cookie.expires)
|
||||
: sess.cookie.expires
|
||||
|
||||
// destroy expired session
|
||||
if (expires && expires <= Date.now()) {
|
||||
delete this.sessions[sessionId]
|
||||
return
|
||||
}
|
||||
|
||||
return sess
|
||||
}
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
/*!
|
||||
* Connect - session - Session
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Expose Session.
|
||||
*/
|
||||
|
||||
module.exports = Session;
|
||||
|
||||
/**
|
||||
* Create a new `Session` with the given request and `data`.
|
||||
*
|
||||
* @param {IncomingRequest} req
|
||||
* @param {Object} data
|
||||
* @api private
|
||||
*/
|
||||
|
||||
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]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update reset `.cookie.maxAge` to prevent
|
||||
* the cookie from expiring when the
|
||||
* session is still active.
|
||||
*
|
||||
* @return {Session} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
defineMethod(Session.prototype, 'touch', function touch() {
|
||||
return this.resetMaxAge();
|
||||
});
|
||||
|
||||
/**
|
||||
* Reset `.maxAge` to `.originalMaxAge`.
|
||||
*
|
||||
* @return {Session} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
defineMethod(Session.prototype, 'resetMaxAge', function resetMaxAge() {
|
||||
this.cookie.maxAge = this.cookie.originalMaxAge;
|
||||
return this;
|
||||
});
|
||||
|
||||
/**
|
||||
* Save the session data with optional callback `fn(err)`.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @return {Session} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
defineMethod(Session.prototype, 'save', function save(fn) {
|
||||
this.req.sessionStore.set(this.id, this, fn || function(){});
|
||||
return this;
|
||||
});
|
||||
|
||||
/**
|
||||
* Re-loads the session data _without_ altering
|
||||
* the maxAge properties. Invokes the callback `fn(err)`,
|
||||
* after which time if no exception has occurred the
|
||||
* `req.session` property will be a new `Session` object,
|
||||
* although representing the same session.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @return {Session} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
defineMethod(Session.prototype, 'reload', function reload(fn) {
|
||||
var req = this.req
|
||||
, store = this.req.sessionStore;
|
||||
store.get(this.id, function(err, sess){
|
||||
if (err) return fn(err);
|
||||
if (!sess) return fn(new Error('failed to load session'));
|
||||
store.createSession(req, sess);
|
||||
fn();
|
||||
});
|
||||
return this;
|
||||
});
|
||||
|
||||
/**
|
||||
* Destroy `this` session.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @return {Session} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
defineMethod(Session.prototype, 'destroy', function destroy(fn) {
|
||||
delete this.req.session;
|
||||
this.req.sessionStore.destroy(this.id, fn);
|
||||
return this;
|
||||
});
|
||||
|
||||
/**
|
||||
* Regenerate this request's session.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @return {Session} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
defineMethod(Session.prototype, 'regenerate', function regenerate(fn) {
|
||||
this.req.sessionStore.regenerate(this.req, fn);
|
||||
return this;
|
||||
});
|
||||
|
||||
/**
|
||||
* Helper function for creating a method on a prototype.
|
||||
*
|
||||
* @param {Object} obj
|
||||
* @param {String} name
|
||||
* @param {Function} fn
|
||||
* @private
|
||||
*/
|
||||
function defineMethod(obj, name, fn) {
|
||||
Object.defineProperty(obj, name, {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: fn,
|
||||
writable: true
|
||||
});
|
||||
};
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*!
|
||||
* Connect - session - Store
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var Cookie = require('./cookie')
|
||||
var EventEmitter = require('events').EventEmitter
|
||||
var Session = require('./session')
|
||||
var util = require('util')
|
||||
|
||||
/**
|
||||
* Module exports.
|
||||
* @public
|
||||
*/
|
||||
|
||||
module.exports = Store
|
||||
|
||||
/**
|
||||
* Abstract base class for session stores.
|
||||
* @public
|
||||
*/
|
||||
|
||||
function Store () {
|
||||
EventEmitter.call(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from EventEmitter.
|
||||
*/
|
||||
|
||||
util.inherits(Store, EventEmitter)
|
||||
|
||||
/**
|
||||
* Re-generate the given requests's session.
|
||||
*
|
||||
* @param {IncomingRequest} req
|
||||
* @return {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Store.prototype.regenerate = function(req, fn){
|
||||
var self = this;
|
||||
this.destroy(req.sessionID, function(err){
|
||||
self.generate(req);
|
||||
fn(err);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Load a `Session` instance via the given `sid`
|
||||
* and invoke the callback `fn(err, sess)`.
|
||||
*
|
||||
* @param {String} sid
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Store.prototype.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))
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Create session from JSON `sess` data.
|
||||
*
|
||||
* @param {IncomingRequest} req
|
||||
* @param {Object} sess
|
||||
* @return {Session}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Store.prototype.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;
|
||||
};
|
||||
Reference in New Issue
Block a user