1 | # cookie-session
|
2 |
|
3 | [![NPM Version][npm-version-image]][npm-url]
|
4 | [![NPM Downloads][npm-downloads-image]][npm-url]
|
5 | [![Build Status][ci-image]][ci-url]
|
6 | [![Test Coverage][coveralls-image]][coveralls-url]
|
7 |
|
8 | Simple cookie-based session middleware.
|
9 |
|
10 | A user session can be stored in two main ways with cookies: on the server or on
|
11 | the client. This module stores the session data on the client within a cookie,
|
12 | while a module like [express-session](https://www.npmjs.com/package/express-session)
|
13 | stores only a session identifier on the client within a cookie and stores the
|
14 | session data on the server, typically in a database.
|
15 |
|
16 | The following points can help you choose which to use:
|
17 |
|
18 | * `cookie-session` does not require any database / resources on the server side,
|
19 | though the total session data cannot exceed the browser's max cookie size.
|
20 | * `cookie-session` can simplify certain load-balanced scenarios.
|
21 | * `cookie-session` can be used to store a "light" session and include an identifier
|
22 | to look up a database-backed secondary store to reduce database lookups.
|
23 |
|
24 | **NOTE** This module does not encrypt the session contents in the cookie, only provides
|
25 | signing to prevent tampering. The client will be able to read the session data by
|
26 | examining the cookie's value. Secret data should not be set in `req.session` without
|
27 | encrypting it, or use a server-side session instead.
|
28 |
|
29 | **NOTE** This module does not prevent session replay, as the expiration set is that
|
30 | of the cookie only; if that is a concern of your application, you can store an expiration
|
31 | date in `req.session` object and validate it on the sever, and implement any other logic
|
32 | to extend the session as your application needs.
|
33 |
|
34 | ## Install
|
35 |
|
36 | This is a [Node.js](https://nodejs.org/en/) module available through the
|
37 | [npm registry](https://www.npmjs.com/). Installation is done using the
|
38 | [`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
|
39 |
|
40 | ```sh
|
41 | $ npm install cookie-session
|
42 | ```
|
43 |
|
44 | ## API
|
45 |
|
46 | ```js
|
47 | var cookieSession = require('cookie-session')
|
48 | var express = require('express')
|
49 |
|
50 | var app = express()
|
51 |
|
52 | app.use(cookieSession({
|
53 | name: 'session',
|
54 | keys: [/* secret keys */],
|
55 |
|
56 | // Cookie Options
|
57 | maxAge: 24 * 60 * 60 * 1000 // 24 hours
|
58 | }))
|
59 | ```
|
60 |
|
61 | ### cookieSession(options)
|
62 |
|
63 | Create a new cookie session middleware with the provided options. This middleware
|
64 | will attach the property `session` to `req`, which provides an object representing
|
65 | the loaded session. This session is either a new session if no valid session was
|
66 | provided in the request, or a loaded session from the request.
|
67 |
|
68 | The middleware will automatically add a `Set-Cookie` header to the response if the
|
69 | contents of `req.session` were altered. _Note_ that no `Set-Cookie` header will be
|
70 | in the response (and thus no session created for a specific user) unless there are
|
71 | contents in the session, so be sure to add something to `req.session` as soon as
|
72 | you have identifying information to store for the session.
|
73 |
|
74 | #### Options
|
75 |
|
76 | Cookie session accepts these properties in the options object.
|
77 |
|
78 | ##### name
|
79 |
|
80 | The name of the cookie to set, defaults to `session`.
|
81 |
|
82 | ##### keys
|
83 |
|
84 | The list of keys to use to sign & verify cookie values, or a configured
|
85 | [`Keygrip`](https://www.npmjs.com/package/keygrip) instance. Set cookies are always
|
86 | signed with `keys[0]`, while the other keys are valid for verification, allowing
|
87 | for key rotation. If a `Keygrip` instance is provided, it can be used to
|
88 | change signature parameters like the algorithm of the signature.
|
89 |
|
90 | ##### secret
|
91 |
|
92 | A string which will be used as single key if `keys` is not provided.
|
93 |
|
94 | ##### Cookie Options
|
95 |
|
96 | Other options are passed to `cookies.get()` and `cookies.set()` allowing you
|
97 | to control security, domain, path, and signing among other settings.
|
98 |
|
99 | The options can also contain any of the following (for the full list, see
|
100 | [cookies module documentation](https://www.npmjs.org/package/cookies#readme):
|
101 |
|
102 | - `maxAge`: a number representing the milliseconds from `Date.now()` for expiry
|
103 | - `expires`: a `Date` object indicating the cookie's expiration date (expires at the end of session by default).
|
104 | - `path`: a string indicating the path of the cookie (`/` by default).
|
105 | - `domain`: a string indicating the domain of the cookie (no default).
|
106 | - `partitioned`: a boolean indicating whether to partition the cookie in Chrome for the [CHIPS Update](https://developers.google.com/privacy-sandbox/3pcd/chips) (`false` by default). If this is true, Cookies from embedded sites will be partitioned and only readable from the same top level site from which it was created.
|
107 | - `priority`: a string indicating the cookie priority. This can be set to `'low'`, `'medium'`, or `'high'`.
|
108 | - `sameSite`: a boolean or string indicating whether the cookie is a "same site" cookie (`false` by default). This can be set to `'strict'`, `'lax'`, `'none'`, or `true` (which maps to `'strict'`).
|
109 | - `secure`: a boolean indicating whether the cookie is only to be sent over HTTPS (`false` by default for HTTP, `true` by default for HTTPS). If this is set to `true` and Node.js is not directly over a TLS connection, be sure to read how to [setup Express behind proxies](https://expressjs.com/en/guide/behind-proxies.html) or the cookie may not ever set correctly.
|
110 | - `httpOnly`: a boolean indicating whether the cookie is only to be sent over HTTP(S), and not made available to client JavaScript (`true` by default).
|
111 | - `signed`: a boolean indicating whether the cookie is to be signed (`true` by default).
|
112 | - `overwrite`: a boolean indicating whether to overwrite previously set cookies of the same name (`true` by default).
|
113 |
|
114 | ### req.session
|
115 |
|
116 | Represents the session for the given request.
|
117 |
|
118 | #### .isChanged
|
119 |
|
120 | Is `true` if the session has been changed during the request.
|
121 |
|
122 | #### .isNew
|
123 |
|
124 | Is `true` if the session is new.
|
125 |
|
126 | #### .isPopulated
|
127 |
|
128 | Determine if the session has been populated with data or is empty.
|
129 |
|
130 | ### req.sessionOptions
|
131 |
|
132 | Represents the session options for the current request. These options are a
|
133 | shallow clone of what was provided at middleware construction and can be
|
134 | altered to change cookie setting behavior on a per-request basis.
|
135 |
|
136 | ### Destroying a session
|
137 |
|
138 | To destroy a session simply set it to `null`:
|
139 |
|
140 | ```js
|
141 | req.session = null
|
142 | ```
|
143 |
|
144 | ### Saving a session
|
145 |
|
146 | Since the entire contents of the session is kept in a client-side cookie, the
|
147 | session is "saved" by writing a cookie out in a `Set-Cookie` response header.
|
148 | This is done automatically if there has been a change made to the session when
|
149 | the Node.js response headers are being written to the client and the session
|
150 | was not destroyed.
|
151 |
|
152 | ## Examples
|
153 |
|
154 | ### Simple view counter example
|
155 |
|
156 | ```js
|
157 | var cookieSession = require('cookie-session')
|
158 | var express = require('express')
|
159 |
|
160 | var app = express()
|
161 |
|
162 | app.set('trust proxy', 1) // trust first proxy
|
163 |
|
164 | app.use(cookieSession({
|
165 | name: 'session',
|
166 | keys: ['key1', 'key2']
|
167 | }))
|
168 |
|
169 | app.get('/', function (req, res, next) {
|
170 | // Update views
|
171 | req.session.views = (req.session.views || 0) + 1
|
172 |
|
173 | // Write response
|
174 | res.end(req.session.views + ' views')
|
175 | })
|
176 |
|
177 | app.listen(3000)
|
178 | ```
|
179 |
|
180 | ### Per-user sticky max age
|
181 |
|
182 | ```js
|
183 | var cookieSession = require('cookie-session')
|
184 | var express = require('express')
|
185 |
|
186 | var app = express()
|
187 |
|
188 | app.set('trust proxy', 1) // trust first proxy
|
189 |
|
190 | app.use(cookieSession({
|
191 | name: 'session',
|
192 | keys: ['key1', 'key2']
|
193 | }))
|
194 |
|
195 | // This allows you to set req.session.maxAge to let certain sessions
|
196 | // have a different value than the default.
|
197 | app.use(function (req, res, next) {
|
198 | req.sessionOptions.maxAge = req.session.maxAge || req.sessionOptions.maxAge
|
199 | next()
|
200 | })
|
201 |
|
202 | // ... your logic here ...
|
203 | ```
|
204 |
|
205 | ### Extending the session expiration
|
206 |
|
207 | This module does not send a `Set-Cookie` header if the contents of the session
|
208 | have not changed. This means that to extend the expiration of a session in the
|
209 | user's browser (in response to user activity, for example) some kind of
|
210 | modification to the session needs be made.
|
211 |
|
212 | ```js
|
213 | var cookieSession = require('cookie-session')
|
214 | var express = require('express')
|
215 |
|
216 | var app = express()
|
217 |
|
218 | app.use(cookieSession({
|
219 | name: 'session',
|
220 | keys: ['key1', 'key2']
|
221 | }))
|
222 |
|
223 | // Update a value in the cookie so that the set-cookie will be sent.
|
224 | // Only changes every minute so that it's not sent with every request.
|
225 | app.use(function (req, res, next) {
|
226 | req.session.nowInMinutes = Math.floor(Date.now() / 60e3)
|
227 | next()
|
228 | })
|
229 |
|
230 | // ... your logic here ...
|
231 | ```
|
232 |
|
233 | ### Using a custom signature algorithm
|
234 |
|
235 | This example shows creating a custom `Keygrip` instance as the `keys` option
|
236 | to provide keys and additional signature configuration.
|
237 |
|
238 | ```js
|
239 | var cookieSession = require('cookie-session')
|
240 | var express = require('express')
|
241 | var Keygrip = require('keygrip')
|
242 |
|
243 | var app = express()
|
244 |
|
245 | app.use(cookieSession({
|
246 | name: 'session',
|
247 | keys: new Keygrip(['key1', 'key2'], 'SHA384', 'base64')
|
248 | }))
|
249 |
|
250 | // ... your logic here ...
|
251 | ```
|
252 |
|
253 | ## Usage Limitations
|
254 |
|
255 | ### Max Cookie Size
|
256 |
|
257 | Because the entire session object is encoded and stored in a cookie, it is
|
258 | possible to exceed the maximum cookie size limits on different browsers. The
|
259 | [RFC6265 specification](https://tools.ietf.org/html/rfc6265#section-6.1)
|
260 | recommends that a browser **SHOULD** allow
|
261 |
|
262 | > At least 4096 bytes per cookie (as measured by the sum of the length of
|
263 | > the cookie's name, value, and attributes)
|
264 |
|
265 | In practice this limit differs slightly across browsers. See a list of
|
266 | [browser limits here](http://browsercookielimits.iain.guru). As a rule
|
267 | of thumb **don't exceed 4093 bytes per domain**.
|
268 |
|
269 | If your session object is large enough to exceed a browser limit when encoded,
|
270 | in most cases the browser will refuse to store the cookie. This will cause the
|
271 | following requests from the browser to either a) not have any session
|
272 | information or b) use old session information that was small enough to not
|
273 | exceed the cookie limit.
|
274 |
|
275 | If you find your session object is hitting these limits, it is best to
|
276 | consider if data in your session should be loaded from a database on the
|
277 | server instead of transmitted to/from the browser with every request. Or
|
278 | move to an [alternative session strategy](https://github.com/expressjs/session#compatible-session-stores)
|
279 |
|
280 | ## License
|
281 |
|
282 | [MIT](LICENSE)
|
283 |
|
284 | [ci-image]: https://badgen.net/github/checks/expressjs/cookie-session/master?label=ci
|
285 | [ci-url]: https://github.com/expressjs/cookie-session/actions?query=workflow%3Aci
|
286 | [coveralls-image]: https://badgen.net/coveralls/c/github/expressjs/cookie-session/master
|
287 | [coveralls-url]: https://coveralls.io/r/expressjs/cookie-session?branch=master
|
288 | [npm-downloads-image]: https://badgen.net/npm/dm/cookie-session
|
289 | [npm-url]: https://npmjs.org/package/cookie-session
|
290 | [npm-version-image]: https://badgen.net/npm/v/cookie-session
|