forked from fastify/session
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5188eeb
commit 2a04ae5
Showing
14 changed files
with
661 additions
and
678 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
package-lock=false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,160 +1,160 @@ | ||
'use strict'; | ||
|
||
const fastifyPlugin = require('fastify-plugin'); | ||
const uid = require('uid-safe').sync; | ||
const cookieSignature = require('cookie-signature'); | ||
const Store = require('./lib/store'); | ||
const Session = require('./lib/session'); | ||
const metadata = require('./lib/metadata'); | ||
|
||
function session(fastify, opts, next) { | ||
fastify.addHook('preHandler', handleSession); | ||
fastify.addHook('onSend', saveSession); | ||
|
||
const store = opts.store || new Store(); | ||
fastify.decorateRequest('sessionStore', store); | ||
fastify.decorateRequest('session', {}); | ||
const cookieName = opts.cookieName || 'sessionId'; | ||
const secret = opts.secret; | ||
const cookieOpts = opts.cookie || {}; | ||
cookieOpts.secure = option(cookieOpts, 'secure', true); | ||
const saveUninitialized = option(opts, 'saveUninitialized', true); | ||
'use strict' | ||
|
||
const fastifyPlugin = require('fastify-plugin') | ||
const uid = require('uid-safe').sync | ||
const cookieSignature = require('cookie-signature') | ||
const Store = require('./lib/store') | ||
const Session = require('./lib/session') | ||
const metadata = require('./lib/metadata') | ||
|
||
function session (fastify, opts, next) { | ||
fastify.addHook('preHandler', handleSession) | ||
fastify.addHook('onSend', saveSession) | ||
|
||
const store = opts.store || new Store() | ||
fastify.decorateRequest('sessionStore', store) | ||
fastify.decorateRequest('session', {}) | ||
const cookieName = opts.cookieName || 'sessionId' | ||
const secret = opts.secret | ||
const cookieOpts = opts.cookie || {} | ||
cookieOpts.secure = option(cookieOpts, 'secure', true) | ||
const saveUninitialized = option(opts, 'saveUninitialized', false) | ||
|
||
if (!secret) { | ||
next(new Error('the secret option is required!')); | ||
return; | ||
next(new Error('the secret option is required!')) | ||
return | ||
} | ||
|
||
if (secret.length < 32) { | ||
next(new Error('the secret must have length 32 or greater')); | ||
next(new Error('the secret must have length 32 or greater')) | ||
} | ||
|
||
function handleSession(request, reply, done) { | ||
const url = request.req.url; | ||
function handleSession (request, reply, done) { | ||
const url = request.req.url | ||
if (url.indexOf(cookieOpts.path || '/') !== 0) { | ||
done(); | ||
return; | ||
done() | ||
return | ||
} | ||
let sessionId = request.cookies[cookieName]; | ||
let sessionId = request.cookies[cookieName] | ||
if (!sessionId) { | ||
newSession(secret, request, reply, done); | ||
newSession(secret, request, reply, done) | ||
} else { | ||
const decryptedSessionId = cookieSignature.unsign(sessionId, secret); | ||
const decryptedSessionId = cookieSignature.unsign(sessionId, secret) | ||
if (decryptedSessionId === false) { | ||
newSession(secret, request, reply, done); | ||
newSession(secret, request, reply, done) | ||
} else { | ||
store.get(decryptedSessionId, (err, session) => { | ||
if (err) { | ||
if (err.code === 'ENOENT') { | ||
newSession(secret, request, reply, done); | ||
newSession(secret, request, reply, done) | ||
} else { | ||
done(err); | ||
done(err) | ||
} | ||
return; | ||
return | ||
} | ||
if(!session) { | ||
newSession(secret, request, reply, done); | ||
return; | ||
if (!session) { | ||
newSession(secret, request, reply, done) | ||
return | ||
} | ||
if (session && session.expires && session.expires <= Date.now()) { | ||
store.destroy(sessionId, getDestroyCallback(secret, request, reply, done)); | ||
return; | ||
store.destroy(sessionId, getDestroyCallback(secret, request, reply, done)) | ||
return | ||
} | ||
if (cookieOpts.maxAge) { | ||
session.expires = Date.now() + cookieOpts.maxAge; | ||
session.expires = Date.now() + cookieOpts.maxAge | ||
} | ||
request.session = session; | ||
done(); | ||
}); | ||
request.session = session | ||
done() | ||
}) | ||
} | ||
} | ||
} | ||
|
||
function getDestroyCallback(secret, request, reply, done) { | ||
return function destroyCallback(err) { | ||
function getDestroyCallback (secret, request, reply, done) { | ||
return function destroyCallback (err) { | ||
if (err) { | ||
done(err); | ||
return; | ||
done(err) | ||
return | ||
} | ||
newSession(secret, request, reply, done); | ||
newSession(secret, request, reply, done) | ||
} | ||
} | ||
|
||
function newSession(secret, request, reply, done) { | ||
const sessionId = uid(24); | ||
const encryptedSessionId = cookieSignature.sign(sessionId, secret); | ||
let expires = null; | ||
const sessionId = uid(24) | ||
const encryptedSessionId = cookieSignature.sign(sessionId, secret) | ||
let expires = null | ||
if (cookieOpts.maxAge) { | ||
expires = Date.now() + cookieOpts.maxAge; | ||
expires = Date.now() + cookieOpts.maxAge | ||
} | ||
const session = new Session(sessionId, encryptedSessionId, expires, cookieOpts); | ||
request.session = session; | ||
done(); | ||
const session = new Session(sessionId, encryptedSessionId, expires, cookieOpts) | ||
request.session = session | ||
done() | ||
} | ||
|
||
function saveSession(request, reply, payload, done) { | ||
const session = request.session; | ||
function saveSession (request, reply, payload, done) { | ||
const session = request.session | ||
if (!session || !session.sessionId || !shouldSaveSession(request, cookieOpts)) { | ||
done(); | ||
return; | ||
done() | ||
return | ||
} | ||
store.set(session.sessionId, session, (err) => { | ||
if (err) { | ||
done(err); | ||
return; | ||
done(err) | ||
return | ||
} | ||
const cookieOptions = getCookieOptions(); | ||
reply.setCookie(cookieName, session.encryptedSessionId, cookieOptions); | ||
done(); | ||
}); | ||
const cookieOptions = getCookieOptions() | ||
reply.setCookie(cookieName, session.encryptedSessionId, cookieOptions) | ||
done() | ||
}) | ||
} | ||
|
||
function getCookieOptions() { | ||
function getCookieOptions () { | ||
return { | ||
path: cookieOpts.path || '/', | ||
httpOnly: cookieOpts.httpOnly !== undefined ? cookieOpts.httpOnly : true, | ||
secure: cookieOpts.secure, | ||
expires: getExpires(cookieOpts), | ||
sameSite: cookieOpts.sameSite || null, | ||
domain: cookieOpts.domain || null | ||
}; | ||
} | ||
} | ||
|
||
function shouldSaveSession(request, cookieOpts) { | ||
if(!saveUninitialized && !isSessionModified(request.session)) { | ||
return false; | ||
function shouldSaveSession (request, cookieOpts) { | ||
if (!saveUninitialized && !isSessionModified(request.session)) { | ||
return false | ||
} | ||
if (cookieOpts.secure !== true) { | ||
return true; | ||
return true | ||
} | ||
const connection = request.req.connection; | ||
const connection = request.req.connection | ||
if (connection && connection.encrypted === true) { | ||
return true; | ||
return true | ||
} | ||
const forwardedProto = request.headers['x-forwarded-proto']; | ||
return forwardedProto === 'https'; | ||
const forwardedProto = request.headers['x-forwarded-proto'] | ||
return forwardedProto === 'https' | ||
} | ||
|
||
function isSessionModified(session) { | ||
return (Object.keys(session).length !== 4); | ||
function isSessionModified (session) { | ||
return (Object.keys(session).length !== 4) | ||
} | ||
|
||
next(); | ||
next() | ||
} | ||
|
||
function getExpires(cookieOpts) { | ||
let expires = null; | ||
function getExpires (cookieOpts) { | ||
let expires = null | ||
if (cookieOpts.expires) { | ||
expires = cookieOpts.expires; | ||
} else if(cookieOpts.maxAge) { | ||
expires = new Date(Date.now() + cookieOpts.maxAge); | ||
expires = cookieOpts.expires | ||
} else if (cookieOpts.maxAge) { | ||
expires = new Date(Date.now() + cookieOpts.maxAge) | ||
} | ||
return expires; | ||
return expires | ||
} | ||
|
||
function option(options, key, def) { | ||
return options[key] === undefined ? def : options[key]; | ||
function option (options, key, def) { | ||
return options[key] === undefined ? def : options[key] | ||
} | ||
|
||
exports = module.exports = fastifyPlugin(session, metadata); | ||
module.exports.Store = Store; | ||
exports = module.exports = fastifyPlugin(session, metadata) | ||
module.exports.Store = Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,10 @@ | ||
'use strict'; | ||
'use strict' | ||
|
||
module.exports = class Cookie { | ||
constructor(cookieOpts) { | ||
this.path = '/'; | ||
this.maxAge = null; | ||
this.httpOnly = true; | ||
Object.assign(this, cookieOpts); | ||
constructor (cookieOpts) { | ||
this.path = '/' | ||
this.maxAge = null | ||
this.httpOnly = true | ||
Object.assign(this, cookieOpts) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,9 @@ | ||
'use strict'; | ||
'use strict' | ||
|
||
module.exports = { | ||
fastify: '>=1.0.0', | ||
name: 'fastify-session', | ||
dependencies: [ | ||
'fastify-cookie' | ||
] | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,12 @@ | ||
'use strict'; | ||
'use strict' | ||
|
||
const Cookie = require('./cookie'); | ||
const Cookie = require('./cookie') | ||
|
||
module.exports = class Session { | ||
constructor(sessionId, encryptedSessionId, expires, cookieOpts) { | ||
this.sessionId = sessionId; | ||
this.encryptedSessionId = encryptedSessionId; | ||
this.expires = expires; | ||
this.cookie = new Cookie(cookieOpts); | ||
constructor (sessionId, encryptedSessionId, expires, cookieOpts) { | ||
this.sessionId = sessionId | ||
this.encryptedSessionId = encryptedSessionId | ||
this.expires = expires | ||
this.cookie = new Cookie(cookieOpts) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.