Giter Site home page Giter Site logo

generic-session's Introduction

generic-session

NPM version build status Coveralls David deps node version npm download Gittip

Generic session middleware for koa, easy use with custom stores such as redis or mongo, supports defer session getter.

This middleware will only set a cookie when a session is manually set. Each time the session is modified (and only when the session is modified), it will reset the cookie and session.

You can use the rolling sessions that will reset the cookie and session for every request which touch the session. Save behavior can be overridden per request.

For async/await and Node v6.9.0+ support use v2.x of this package, for older use v1.x

Usage

Example

var session = require('koa-generic-session');
var redisStore = require('koa-redis');
var koa = require('koa');

var app = new koa(); // for koa v1 use `var app = koa();`
app.keys = ['keys', 'keykeys'];
app.use(session({
  store: redisStore()
}));

app.use(function *() {
  switch (this.path) {
  case '/get':
    get.call(this);
    break;
  case '/remove':
    remove.call(this);
    break;
  case '/regenerate':
    yield regenerate.call(this);
    break;
  }
});

function get() {
  var session = this.session;
  session.count = session.count || 0;
  session.count++;
  this.body = session.count;
}

function remove() {
  this.session = null;
  this.body = 0;
}

function *regenerate() {
  get.call(this);
  yield this.regenerateSession();
  get.call(this);
}

app.listen(8080);
  • After adding session middleware, you can use this.session to set or get the sessions.
  • Getting session ID via this.sessionId.
  • Setting this.session = null; will destroy this session.
  • Altering this.session.cookie changes the cookie options of this user. Also you can use the cookie options in session the store. Use for example cookie.maxAge as the session store's ttl.
  • Calling this.regenerateSession will destroy any existing session and generate a new, empty one in its place. The new session will have a different ID.
  • Calling this.saveSession will save an existing session (this method was added for koa-redirect-loop)
  • Setting this.sessionSave = true will force saving the session regardless of any other options or conditions.
  • Setting this.sessionSave = false will prevent saving the session regardless of any other options or conditions.

Options

  • key: cookie name defaulting to koa.sid.

  • prefix: session prefix for store, defaulting to koa:sess:.

  • ttl: ttl is for sessionStore's expiration time (not to be confused with cookie expiration which is controlled by cookie.maxAge), can be a number or a function that returns a number (for dynamic TTL), default to null (means get ttl from cookie.maxAge or cookie.expires).

  • rolling: rolling session, always reset the cookie and sessions, defaults to false.

  • genSid: default sid was generated by uid2, you can pass a function to replace it (supports promises/async functions).

  • defer: defers get session, only generate a session when you use it through var session = yield this.session;, defaults to false.

  • allowEmpty: allow generation of empty sessions.

  • errorHandler(err, type, ctx): Store.get and Store.set will throw in some situation, use errorHandle to handle these errors by yourself. Default will throw.

  • reconnectTimeout: When store is disconnected, don't throw store unavailable error immediately, wait reconnectTimeout to reconnect, default is 10s.

  • sessionIdStore: object with get, set, reset methods for passing session id throw requests.

  • valid: valid(ctx, session), valid session value before use it.

  • beforeSave: beforeSave(ctx, session), hook before save session.

  • store: session store instance. It can be any Object that has the methods set, get, destroy like MemoryStore.

  • cookie: session cookie settings, defaulting to:

    {
      path: '/',
      httpOnly: true,
      maxAge: 24 * 60 * 60 * 1000 //one day in ms,
      overwrite: true,
      signed: true
    }

    For a full list of cookie options see expressjs/cookies.

    if you setcookie.maxAge to null, meaning no "expires" parameter is set so the cookie becomes a browser-session cookie. When the user closes the browser the cookie (and session) will be removed.

    Notice that ttl is different from cookie.maxAge, ttl set the expire time of sessionStore. So if you set cookie.maxAge = null, and ttl=ms('1d'), the session will expired after one day, but the cookie will destroy when the user closes the browser. And mostly you can just ignore options.ttl, koa-generic-session will parse cookie.maxAge as the tll.

    If your application requires dynamic expiration, control cookie.maxAge using ctx.session.cookie.maxAge = dynamicMaxAge, when you need ttl to differ from cookie.maxAge (a common example is browser-session cookies having cookie.maxAge = null, but you want them to not live indefinitely in the store) specify a function for ttl:

    {
      ttl: (session) => {
        // Expire browser-session cookies from the store after 1 day
        if (session.cookie?.maxAge === null) {
          return 1000 * 60 * 60 * 24;
        }
      }
    }

Hooks

  • valid(): valid session value before use it
  • beforeSave(): hook before save session

Session Store

You can use any other store to replace the default MemoryStore, it just needs to follow this api:

  • get(sid): get session object by sid
  • set(sid, sess, ttl): set session object for sid, with a ttl (in ms)
  • destroy(sid): destroy session for sid

the api needs to return a Promise, Thunk or generator.

And use these events to report the store's status.

  • connect
  • disconnect

Stores Presented

Graceful shutdown

Since this middleware comes with an auto-reconnect feature, it's very likely you can't gracefully shutdown after closing the client as generic-session will try to recover the connection, in those cases you can disable reconnect feature (

store.on('disconnect', () => {
if (storeStatus !== 'available') return
storeStatus = 'pending'
waitStore = new Promise((resolve, reject) => {
setTimeout(() => {
if (storeStatus === 'pending') storeStatus = 'unavailable'
reject(new Error('session store is unavailable'))
}, reconnectTimeout)
store.once('connect', resolve)
})
) desactivating the client emitter (do this only when stopping the server)

Example with ioredis

  // ...disconnecting from db
  redisClient.emit = () => true;
  await redisClient.quit();
  // ...stopping the server

Licences

(The MIT License)

Copyright (c) 2013 - 2016 dead-horse and other contributors

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.

generic-session's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

generic-session's Issues

Should we include the cookie object in the session data we store?

I'm the person who made the PostgreSQL implementation for this library, and I'm just interested if I'm supposed to be storing the cookie object in the session data. At the moment I am, and it's working fine, but it might be somewhat redundant? So in the database table that my module generates, I have this:

{
   "cookie":{
      "httpOnly":true,
      "path":"/",
      "overwrite":true,
      "signed":true,
      "maxage":86400000
   },
   "logged_in":true,
   "user_id":"6",
   "username":"TMiguelT",
   "email":"[email protected]"
}

Obviously the keys like logged_in and user_id are my own session properties, but the cookie object is included as well. This is because I simply serialize the whole session object, which contains cookie, but I'm not sure if I should filter it out. Thoughts?

session store is unavaliable

What is this mean, i just copy your code and run it.

var session = require('koa-generic-session');
var redisStore = require('koa-redis');
var koa = require('koa');

var app = koa();
app.keys = ['keys', 'keykeys'];
app.use(session({
  store: redisStore()
}));
...
Error: session store is unavaliable
      at null._onTimeout (/Users/jiangxingshang/Documents/workspace/WebStorm/Shop/node_modules/koa-generic-session/lib/session.js:105:16)
      at Timer.listOnTimeout (timers.js:110:15)

SessionId generation

Hello,

I am trying to write a bit of server-side tracking to my koa application. Turns out, koa-generic-session middleware doesn't generate sessionId and store it inside koa app context before yielding to next middleware. As a result, I don't have a way to uniquely identify a user until second request is made.

I don't understand whether this is intentional or I'm missing something very obvious.

typos in documentation

typos in words:
"unavaliable"
"cloese"

Very unclear sentence:
"Also you can use the cookie options in session the store"

Documentation regarding options.key and the cookie name not clear

The documentation regarding the use of options.key and the generation of the cookie name could be more clear and it would be nice if it used the same kind of fallback to options.cookie.name as options.ttl does.

I had the following scenario and assumed that this would work:

app.use(session({
    cookie: {
        name: 'mytnt-addressbook',
        maxAge: 1000 * 60 * 60,
        path: '/',
        httpOnly: true,
        rewrite: true,
        signed: true
    },
    store: redisStorage({
        host: settings.redis.host
    })
}));

After some debugging I found out that if you only supply the cookie.name without the key property it will fail and recreate a session for every request. The cookie name is being picked up by the cookie.name property but the retrieval and storage of the session relies on the key property which defaults to 'koa.sid'.

I read the part about the ttl property being based on the cookie max age if you do not supply the ttl property. I would suggest doing the same for the key. If key is not supplied and cookie.name is set, fall back to that, otherwise fall back to the default 'koa.sid'.

I'm willing to do a PR to change this and update the read me accordingly.

Can the real koa-session please stand up?

It's a little difficult to tell off-hand what the differences are between koa-sess and koa-session. Perhaps it would be worth leaving a note in the README for casual users...

(It looks to me like koa-sess is replacing the existing koa-session since it's been updated much more recently? Is it compatible?)

Session never destroyed

Hi,
I'm using generic-session with koa-redis on a side project and can't figure out how to destroy the session. I mean, I have a /logout route that is suppose to destroy the current session with the following code:

router.get('/logout', function *() {
  console.log('logout');
  this.session = null;
  this.status = 200;
});

As mentioned in the README.md, this.session = null should destroy the session but in fact nothing happened. I put some console.log in the destroy method of koa-redis and I noticed this function was never called.

FYI, modules versions are:

  • koa: 1.1.2
  • koa-generic-session: 1.10.0
  • koa-redis: 1.0.1
    And session are created by koa-passport (1.x.x)

I try to make test in koa-redis and koa-generic-session and everything works as expected, 100% passed.

Does not work with node v6.5

Hi,

I already opened up a bug in koa-convert, but I figured its worth opening one here as I am unsure of which lib isnt playing nicely with node v6.5.

I have a webapp that I built using the koa v2 framework. I use koa-convert, koa-generic-session and koa-redis to store user sessions in my redis instance. Since upgrading to node v6.5, my webapp fails with the following stacktrace:

TypeError: Cannot read property 'done' of undefined
      at next (/Users/dev/work/node_modules/co/index.js:98:14)
      at onFulfilled (/Users/dev/work/node_modules/co/index.js:69:7)
      at /Users/Stephen/dev/work/node_modules/co/index.js:54:5
      at Object.co (/Users/dev/work/node_modules/co/index.js:50:10)
      at next (/Users/dev/work/node_modules/co/index.js:99:29)
      at onFulfilled (/Users/dev/work/node_modules/co/index.js:69:7)

It worked fine with node v6.4. The stacktrace doesnt go past co, but through trial and error, I deleted a lot of third party libs and middleware and narrowed it down to this block of code:

app.use(convert(session({
  key: 'session',
  errorHandler(err, type, ctx) {
    console.error(err, type)
  },
  store: redisStore({ url: config.redis.url })
})))

which can be reduced to app.use(convert(session())) and still trigger the same bug.

I do not use the convert lib anywhere else in the app so I cannot quickly test to see if the issue is specifically in the convert or session lib.

refactor

the codebase is like a shit now.

no set-cookie on redirects

Common use-case: Redirect from GET * to GET /login over POST /login back to GET /login.

var koa = require('koa');
var session = require('koa-sess');

var app = koa();

app.use(session());

app.use(function* (next) {
  if (this.path !== '/') return yield next;

  if (!this.session.ok) {
    return this.redirect('/login');
  }

  this.body = 'Hello';
});

app.use(function *(next) {
  if (this.path !== '/login') return yield next;

  this.session.ok = true;

  this.redirect('/');
});

app.listen(3000);

Session hijacking?

Just out of interest, is there anything in this module that could prevent session hijacking? So if someone randomly generates session ID cookies, could they eventually find a real session and use it?

The header contains invalid characters

Edit: Found the problem! #96 (comment)

When running:

this.session.count = 12

This exception gets thrown:

  koa-generic-session set error: The header content contains invalid characters
      at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:351:13)
      at Cookies.set (..project\node_modules\cookies\lib\cookies.js:100:13)
      at Object.options.sessionIdStore.set (..project\node_modules\koa-generic-session\lib\session.js:93:20)
      at Object.saveNow (..project\node_modules\koa-generic-session\lib\session.js:283:26)
      at next (native)
      at onFulfilled (..project\node_modules\co\index.js:65:19)
      at process._tickCallback (node.js:369:9)

These are the modules that I used:

koa v1.2.0
koa-generic-session v1.11.1
koa-router v5.4.0

Can be reproduced with the following application:

'use strict'

const koa = require('koa')

const router  = require('koa-router')()
const session = require('koa-generic-session')

const app = koa()

app.keys = ['key']
app.use(session())

router.get('/', function* () {
    this.session.count = 12
    this.body = 'test'
});

app.use(router.routes())

app.listen(3000)
console.log('listening on port 3000')

rolling sessions

if options.rolling is set, every time request, reset cookie, touch session.

Koa 2

Any plans to update for koa2 ?

important error suppressed

When used with koa-generic-session-mongo:

koa-generic-session:session set session error:  +5ms The dotted field 'xx.ddds.com' in 'user.ssoUser.adAccounts.alipay.ali.com' is not valid for storage.

Such kind of errors should not be suppressed by default like this:

//update session
try {
  yield store.set(this.sessionId, session);
  this.cookies.set(key, this.sessionId, session.cookie);
  debug('saved');
} catch (err) {
  debug('set session error: ', err.message);
}

Bug: user gets kicked off

Let's take a site with 15 mins session cookie timeout as an example.

Here's what happens:

  1. Bob gets a session.
  2. Bot interacts with the site without modifying the session. He does not get any cookie renewal because of that.
  3. After 15 minutes, Bob's cookie expires and he is kicked off.

Using rolling: true seems to prevent this (but resubmits whole session to db each time, not optimized).

But... the behavior is kind of strange, it seems natural that we don't want to kick out Bob while he's browsing the site. Maybe make session time-out on inactivity period by default?

defer session's generate

after app.use(session), every request will generate a session, but maybe not every request need one.

so try to defer session's generate, only when request use the session, generate it.

var user = (yield this.session).user;

this may break old var user = this.session.user, but it worth a try.

How to use with passport?

I'm using passport for authentication and want to use generic-session with it to store session data inside Redis.
I just don't have a clue of how to make generic-session work with passport.
Can you point me in the right direction?

I found this repo which does this but it doesn't really seem to use the sessions and I am not sure whether it is correct: https://github.com/dozoisch/koa-react-full-example

关于session id 唯一性问题

有个疑问,我看了下源码,现在的session id生成是通过 uid2模块,这个模块是通过这个方法 crypto.pseudoRandomBytes来生成的,这个伪随机数生成算法能保证session id唯一么。如果node服务跑在多个进程甚至多个机器上,是否会有重复的风险。

maxage与maxAge问题

compatMaxage 处理了maxAge和maxage兼容,却删除了maxAge。

 function compatMaxage(opts) {
   if (opts) {
    opts.maxage = opts.maxage === undefined
      ? opts.maxAge
      : opts.maxage;
    delete opts.maxAge;
  }
}

在同时使用koa-sess-mongo-store的源码中获取的是maxAge而不是maxage。这导致一些问题。

MongoStore.prototype.set = function *(sid, sess) {
    var maxAge = sess.cookie.maxAge;
    sess.sid = sid;
    sess.ttl = new Date((this.ttl || ('number' == typeof maxAge
      ? maxAge : oneDay)) + Date.now());
    return yield this.col.update({sid: sid}, sess, {upsert: true});
};

虽然可以使用beforeSave,在存入mongo之前改一下字段。

var sessionConfig = {
    store: new MongoStore(),
    beforeSave: function(ctx, sess){
        ctx.session.cookie.maxAge = sess.cookie.maxage; //兼容 maxAge
    }
};

但总感觉兼容性不好,想请教大家怎么解决这个问题??????

Handle to isNew

need a method for isNew Session. leme know if am missing anything

Why save `cookie` settings inside session object?

I'm using this.session to store data.
And there is some cookie object with the cookie settings.
Why is it placed there?
My opinion is that it's not needed there and should be removed from there.
I don't care what are the cookie options, I don't need them.
They take space.
With millions of sessions that can exhaust ram or disk space.

Session-ID very insecure?

Unless I am missing something the default session-ids generation is very insecure and there is no hint to the library user in the documentation of what to do about it.

In particular, hashing the cookies will always generate the same value so unless users are putting something unique into the cookies before login, every visitor will have the same session id. This, in fact, is what I am seeing in a brand new project - all visitors ids are the same whether they are logged in or not because we aren't setting any other cookies.

Consider implementing something stronger by default and making it clear in the documentation how to override the generation function.

Need ability to conditionally refresh session

If options.rolling = true the session is refreshed on every request that passes through the session middleware, whereas otherwise it's only refreshed if the data has changed.

In the former case, there is no way to suppress the refresh, which in my case means an authenticated REST call that is regularly polled for new data keeps the user's session alive indefinitely regardless of timeout.

In the latter case a conditionally rolling session can externally induced by making trivial modifications to the session, but this is an ugly hack and also breaks the suppression of saving empty sessions.

There should be at least one API method allowing a way to overrule the decisions made during bootstrap for specific requests (by passing through a route with another middleware, by call in a route handler, etc.).

Viable options include:

  • exposing a paremeter that flags whether refreshSession will be even called on this request (perhaps a little heavy-handed)
  • exposing an paremeterless method at request time that can be called to immedately invoke refreshSession
  • exposing a method that when called suppresses session saving (perhaps configurable by options to choose between not invoking refreshSession at all or matching behavior of options.rolling = false).
  • exposing a single method that accepts request-time configuration which will override bootstrap-time configuration and allows specifying any reasonable session wrap-up behavior:
    • equivalent to options.rolling = true (definitely refresh unless new+empty)
    • equivalent to options.rolling = false (only refresh if changed)
    • no (don't refresh, period - maybe not actually reasonable)
  • let options.rolling accept a callback which can then dynamically resolve to boolean handling external conditions

I must produce my own solution in the short term which will likely involve forking or wrapping with a cheap hack, but if a more appropriate approach can be agreed upon I may later have time to supply a reference implementation.

generic session is not compatible with --harmony when no session operated

version 1.11.3
node v6.5.0
test case:

var session = require('koa-generic-session');
var redisStore = require('koa-redis');
var koa = require('koa');
var app = koa();
app.keys = ['keys', 'keykeys'];
app.use(session({
store: redisStore()
}));
app.use(function *() {
switch (this.path) {
case '/get':
this.body = {"hello": "world"}
}
});
app.listen(8080);

and run the script with
node --harmony

and visit
http://127.0.0.1:8080/get

Are the store implementations supposed to handle TTL?

I've implemented my particular store (TMiguelT/koa-pg-session) such that I return the session data even if the session's TTL has passed. Am I meant to return null if TTL has passed, or is this supposed to be handled by the generic-session module?

I ran a basic test where I see if data is persisted after the TTL with the MemoryStore module, and nothing was returned (working correctly) versus my own postgres module, which still returned data, so I assume it's my responsibility, is this true?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.