Skip to content

Commit

Permalink
Add genid option to generate custom session IDs
Browse files Browse the repository at this point in the history
closes #47
closes #49
  • Loading branch information
dougwilson committed Jun 18, 2014
1 parent 5fc7b09 commit a8bda70
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 1 deletion.
1 change: 1 addition & 0 deletions History.md
@@ -1,6 +1,7 @@
unreleased
==========

* Add `genid` option to generate custom session IDs
* Add `saveUninitialized` option to control saving uninitialized sessions
* Add `unset` option to control unsetting `req.session`
* deps: buffer-crc32@0.2.3
Expand Down
15 changes: 15 additions & 0 deletions README.md
Expand Up @@ -34,12 +34,27 @@ middleware _before_ `session()`.
- `secret` - session cookie is signed with this secret to prevent tampering.
- `cookie` - session cookie settings.
- (default: `{ path: '/', httpOnly: true, secure: false, maxAge: null }`)
- `genid` - function to call to generate a new session ID. (default: uses `uid2` library)
- `rolling` - forces a cookie set on every response. This resets the expiration date. (default: `false`)
- `resave` - forces session to be saved even when unmodified. (default: `true`)
- `proxy` - trust the reverse proxy when setting secure cookies (via "x-forwarded-proto" header). When set to `true`, the "x-forwarded-proto" header will be used. When set to `false`, all headers are ignored. When left unset, will use the "trust proxy" setting from express. (default: `undefined`)
- `saveUninitialized` - forces a session that is "uninitialized" to be saved to the store. A session is uninitialized when it is new but not modified. This is useful for implementing login sessions, reducing server storage usage, or complying with laws that require permission before setting a cookie. (default: `true`)
- `unset` - controls result of unsetting `req.session` (through `delete`, setting to `null`, etc.). This can be "keep" to keep the session in the store but ignore modifications or "destroy" to destroy the stored session. (default: `'keep'`)

#### options.genid

Generate a custom session ID for new sessions. 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.

**NOTE** be careful you 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'
}))
```

#### Cookie options

Expand Down
19 changes: 18 additions & 1 deletion index.js
Expand Up @@ -72,6 +72,12 @@ function session(options){
, storeReady = true
, rollingSessions = options.rolling || false;

var generateId = options.genid || generateSessionId;

if (typeof generateId !== 'function') {
throw new TypeError('genid option must be a function');
}

// TODO: switch default to false on next major
var resaveSession = options.resave === undefined
? true
Expand All @@ -96,7 +102,7 @@ function session(options){

// generates the new session
store.generate = function(req){
req.sessionID = uid(24);
req.sessionID = generateId(req);
req.session = new Session(req);
req.session.cookie = new Cookie(cookie);
};
Expand Down Expand Up @@ -276,6 +282,17 @@ function session(options){
};
};

/**
* Generate a session ID for a new session.
*
* @return {String}
* @api private
*/

function generateSessionId(sess) {
return uid(24);
}

/**
* Hash the given `sess` object omitting changes to `.cookie`.
*
Expand Down
49 changes: 49 additions & 0 deletions test/session.js
Expand Up @@ -186,6 +186,55 @@ describe('session()', function(){
})
})

describe('genid option', function(){
it('should reject non-function values', function(){
session.bind(null, { genid: 'bogus!' }).should.throw(/genid.*must/);
});

it('should provide default generator', function(done){
request(app)
.get('/')
.expect('set-cookie', /connect\.sid=s%3A([^\.]{24})\./i)
.expect(200, done);
});

it('should allow custom function', function(done){
var app = express()
.use(cookieParser())
.use(session({ genid: function(){ return 'a' }, secret: 'keyboard cat', cookie: { maxAge: min }}))
.use(respond);

request(app)
.get('/')
.expect('set-cookie', /connect\.sid=s%3Aa\./i)
.expect(200, done);
});

it('should encode unsafe chars', function(done){
var app = express()
.use(cookieParser())
.use(session({ genid: function(){ return '%' }, secret: 'keyboard cat', cookie: { maxAge: min }}))
.use(respond);

request(app)
.get('/')
.expect('set-cookie', /connect\.sid=s%3A%25\./i)
.expect(200, done);
});

it('should provide req argument', function(done){
var app = express()
.use(cookieParser())
.use(session({ genid: function(req){ return req.url }, secret: 'keyboard cat', cookie: { maxAge: min }}))
.use(respond);

request(app)
.get('/foo')
.expect('set-cookie', /connect\.sid=s%3A%2Ffoo\./i)
.expect(200, done);
});
});

describe('key option', function(){
it('should default to "connect.sid"', function(done){
request(app)
Expand Down

0 comments on commit a8bda70

Please sign in to comment.