add app.keys support

This commit is contained in:
Jonathan Ong 2013-11-15 10:03:40 -08:00 committed by TJ Holowaychuk
parent 52307eeb72
commit 0362c8e457
4 changed files with 137 additions and 3 deletions

View file

@ -59,6 +59,26 @@ http.createServer(app.callback()).listen(3001);
Add the given middleware function to this application. See [Middleware](#middleware) for Add the given middleware function to this application. See [Middleware](#middleware) for
more information. more information.
### app.keys=
Set signed cookie keys.
These are passed to [KeyGrip](https://github.com/jed/keygrip),
however you may also pass your own `KeyGrip` instance. For
example the following are acceptable:
```js
app.keys = ['im a newer secret', 'i like turtle'];
app.keys = new KeyGrip(['im a newer secret', 'i like turtle'], 'sha256');
```
These keys may be rotated and are used when signing cookies
with the `{ signed: true }` option:
```js
this.cookies.set('name', 'tobi', { signed: true });
```
## Handling Requests ## Handling Requests
Koa requests are manipulated using a `Context` object containing both a Koa `Request` and `Response` object. For more information on these view: Koa requests are manipulated using a `Context` object containing both a Koa `Request` and `Response` object. For more information on these view:

View file

@ -10,6 +10,7 @@ var context = require('./context');
var request = require('./request'); var request = require('./request');
var response = require('./response'); var response = require('./response');
var Cookies = require('cookies'); var Cookies = require('cookies');
var Keygrip = require('keygrip');
var Stream = require('stream'); var Stream = require('stream');
var http = require('http'); var http = require('http');
var co = require('co'); var co = require('co');
@ -96,11 +97,42 @@ app.callback = function(){
return function(req, res, next){ return function(req, res, next){
var ctx = self.createContext(req, res); var ctx = self.createContext(req, res);
co.call(ctx, gen)(next || ctx.onerror); co.call(ctx, gen)(next || ctx.onerror);
} }
}; };
/**
* Set signed cookie keys.
*
* These are passed to [KeyGrip](https://github.com/jed/keygrip),
* however you may also pass your own `KeyGrip` instance. For
* example the following are acceptable:
*
* app.keys = ['im a newer secret', 'i like turtle'];
* app.keys = new KeyGrip(['im a newer secret', 'i like turtle'], 'sha256');
*
* @param {Array|KeyGrip} keys
* @api public
*/
app.__defineSetter__('keys', function(keys){
var ok = Array.isArray(keys) || keys instanceof Keygrip;
if (!ok) throw new TypeError('app.keys must be an array or Keygrip');
if (!(keys instanceof Keygrip)) keys = new Keygrip(keys);
this._keys = keys;
});
/**
* Get `Keygrip` instance.
*
* @return {Keygrip}
* @api public
*/
app.__defineGetter__('keys', function(){
return this._keys;
});
/** /**
* Initialize a new context. * Initialize a new context.
* *
@ -116,8 +148,8 @@ app.createContext = function(req, res){
context.res = request.res = response.res = res; context.res = request.res = response.res = res;
request.ctx = response.ctx = context; request.ctx = response.ctx = context;
context.onerror = context.onerror.bind(context); context.onerror = context.onerror.bind(context);
context.cookies = new Cookies(req, res);
context.originalUrl = request.originalUrl = req.url; context.originalUrl = request.originalUrl = req.url;
context.cookies = new Cookies(req, res, this.keys);
return context; return context;
} }

View file

@ -27,7 +27,8 @@
"fresh": "~0.2.0", "fresh": "~0.2.0",
"negotiator": "~0.3.0", "negotiator": "~0.3.0",
"koa-compose": "~2.0.0", "koa-compose": "~2.0.0",
"cookies": "~0.3.6" "cookies": "~0.3.6",
"keygrip": "~0.2.4"
}, },
"devDependencies": { "devDependencies": {
"bytes": "~0.2.1", "bytes": "~0.2.1",

81
test/context/cookies.js Normal file
View file

@ -0,0 +1,81 @@
var koa = require('../..')
var request = require('supertest');
describe('ctx.cookies.set()', function(){
it('should set an unsigned cookie', function(done){
var app = koa();
app.use(function *(next){
this.cookies.set('name', 'jon');
this.status = 204;
})
var server = app.listen();
request(server)
.get('/')
.expect(204)
.end(function(err, res){
if (err) return done(err);
res.headers['set-cookie'].some(function(cookie){
return /^name=/.test(cookie);
}).should.be.ok;
done();
})
})
describe('with .signed', function(){
describe('when no .keys are set', function(){
it('should error', function(done){
var app = koa();
app.use(function *(next){
try {
this.cookies.set('foo', 'bar', { signed: true });
} catch (err) {
this.body = err.message;
}
});
request(app.listen())
.get('/')
.expect('Cannot call method \'sign\' of undefined', done);
})
})
it('should send a signed cookie', function(done){
var app = koa();
app.keys = ['a', 'b'];
app.use(function *(next){
this.cookies.set('name', 'jon', { signed: true });
this.status = 204;
})
var server = app.listen();
request(server)
.get('/')
.expect(204)
.end(function(err, res){
if (err) return done(err);
var cookies = res.headers['set-cookie'];
cookies.some(function(cookie){
return /^name=/.test(cookie);
}).should.be.ok;
cookies.some(function(cookie){
return /^name\.sig=/.test(cookie);
}).should.be.ok;
done();
})
})
})
})