Skip to content

Commit

Permalink
Merge pull request #125 from galvez/dev
Browse files Browse the repository at this point in the history
feat: allow custom ID generator
  • Loading branch information
SerayaEryn authored Sep 17, 2020
2 parents 8ecbf7b + bf35b70 commit 5e66e73
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 13 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ The options object used to generate the `Set-Cookie` header of the session cooki
* `expires` - The expiration `date` used for the `Expires` attribute. If both `expires` and `maxAge` are set, then `expires` is used.
* `sameSite`- The `boolean` or `string` of the `SameSite` attribute. Using `Secure` mode with `auto` attribute will change the behaviour of the `SameSite` attribute in `http` mode. The `SameSite` attribute will automatically be set to `Lax` with a `http` request. See this [link](https://www.chromium.org/updates/same-site).
* `domain` - The `Domain` attribute.

##### store
A session store. Needs the following methods:
* set(sessionId, session, callback)
Expand All @@ -85,6 +86,10 @@ Defaults to a simple in memory store.</br>
Save sessions to the store, even when they are new and not modified. Defaults to `true`.
Setting this to `false` can be useful to save storage space and to comply with the EU cookie law.

##### idGenerator (optional)

Function used to generate new session IDs. Defaults to [`uid(24)`](https://github.com/crypto-utils/uid-safe).

#### request.session

Allows to access or modify the session data.
Expand Down
31 changes: 21 additions & 10 deletions lib/fastifySession.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict'

const uid = require('uid-safe').sync
const fastifyPlugin = require('fastify-plugin')
const Store = require('./store')
const Session = require('./session')
Expand All @@ -8,7 +9,9 @@ const cookieSignature = require('cookie-signature')

function session (fastify, options, next) {
const error = checkOptions(options)
if (error) return next(error)
if (error) {
return next(error)
}

options = ensureDefaults(options)

Expand All @@ -25,6 +28,7 @@ function session (fastify, options, next) {

function decryptSession (sessionId, options, request, done) {
const cookieOpts = options.cookie
const idGenerator = options.idGenerator
const secrets = options.secret
const secretsLength = secrets.length
const secret = secrets[0]
Expand All @@ -37,26 +41,27 @@ function decryptSession (sessionId, options, request, done) {
}
}
if (decryptedSessionId === false) {
newSession(secret, request, cookieOpts, done)
newSession(secret, request, cookieOpts, idGenerator, done)
} else {
options.store.get(decryptedSessionId, (err, session) => {
if (err) {
if (err.code === 'ENOENT') {
newSession(secret, request, cookieOpts, done)
newSession(secret, request, cookieOpts, idGenerator, done)
} else {
done(err)
}
return
}
if (!session) {
newSession(secret, request, cookieOpts, done)
newSession(secret, request, cookieOpts, idGenerator, done)
return
}
if (session && session.expires && session.expires <= Date.now()) {
options.store.destroy(sessionId, getDestroyCallback(secret, request, done, cookieOpts))
options.store.destroy(sessionId, getDestroyCallback(secret, request, done, cookieOpts, idGenerator))
return
}
request.session = new Session(
idGenerator,
cookieOpts,
secret,
session
Expand All @@ -68,6 +73,7 @@ function decryptSession (sessionId, options, request, done) {

function preValidation (options) {
const cookieOpts = options.cookie
const idGenerator = options.idGenerator
return function handleSession (request, reply, done) {
const url = request.raw.url
if (url.indexOf(cookieOpts.path || '/') !== 0) {
Expand All @@ -77,7 +83,7 @@ function preValidation (options) {
const sessionId = request.cookies[options.cookieName]
const secret = options.secret[0]
if (!sessionId) {
newSession(secret, request, cookieOpts, done)
newSession(secret, request, cookieOpts, idGenerator, done)
} else {
decryptSession(sessionId, options, request, done)
}
Expand Down Expand Up @@ -106,18 +112,18 @@ function onSend (options) {
}
}

function getDestroyCallback (secret, request, done, cookieOpts) {
function getDestroyCallback (secret, request, done, cookieOpts, idGenerator) {
return function destroyCallback (err) {
if (err) {
done(err)
return
}
newSession(secret, request, cookieOpts, done)
newSession(secret, request, cookieOpts, idGenerator, done)
}
}

function newSession (secret, request, cookieOpts, done) {
request.session = new Session(cookieOpts, secret)
function newSession (secret, request, cookieOpts, idGenerator, done) {
request.session = new Session(idGenerator, cookieOpts, secret)
done()
}

Expand All @@ -141,8 +147,13 @@ function checkOptions (options) {
}
}

function idGenerator () {
return uid(24)
}

function ensureDefaults (options) {
options.store = options.store || new Store()
options.idGenerator = options.idGenerator || idGenerator
options.cookieName = options.cookieName || 'sessionId'
options.cookie = options.cookie || {}
options.cookie.secure = option(options.cookie, 'secure', true)
Expand Down
7 changes: 4 additions & 3 deletions lib/session.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
'use strict'

const Cookie = require('./cookie')
const uid = require('uid-safe').sync
const cookieSignature = require('cookie-signature')

const maxAge = Symbol('maxAge')
const secretKey = Symbol('secretKey')
const sign = Symbol('sign')
const addDataToSession = Symbol('addDataToSession')
const generateId = Symbol('generateId')

module.exports = class Session {
constructor (cookieOpts, secret, prevSession = {}) {
constructor (idGenerator, cookieOpts, secret, prevSession = {}) {
this[generateId] = idGenerator
this.expires = null
this.cookie = new Cookie(cookieOpts)
this[maxAge] = cookieOpts.maxAge
Expand All @@ -30,7 +31,7 @@ module.exports = class Session {
}

regenerate () {
this.sessionId = uid(24)
this.sessionId = this[generateId]()
this.encryptedSessionId = this[sign]()
}

Expand Down
21 changes: 21 additions & 0 deletions test/session.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,27 @@ test('should add session.sessionId object to request', async (t) => {
t.is(response.statusCode, 200)
})

test('should use custom sessionId generator if available', async (t) => {
t.plan(2)
const port = await testServer((request, reply) => {
t.truthy(request.session.sessionId.startsWith('custom-'))
reply.send(200)
}, {
idGenerator: () => {
return `custom-${
new Date().getTime()
}-${
Math.random().toString().slice(2)
}`
},
...DEFAULT_OPTIONS
})

const { response } = await request(`http://localhost:${port}`)

t.is(response.statusCode, 200)
})

test('should keep user data in session throughout the time', async (t) => {
t.plan(3)
const fastify = Fastify()
Expand Down

0 comments on commit 5e66e73

Please sign in to comment.