Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@ console.log('listening on port 3000');

If your session store requires data or utilities from context, `opts.ContextStore` is alse supported. `ContextStore` must be a class which claims three instance methods demonstrated above. `new ContextStore(ctx)` will be executed on every request.

### Custom External Key

External key is used the cookie by default, but you can use `options.externalKey` to customize your own external key methods. `options.externalKey` with two methods:

- `get(key, opts, ctx)`: get the external key
- `set(key, value, opts, ctx)`: set the external key

### Session#isNew

Returns __true__ if the session is new.
Expand Down
7 changes: 7 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ function formatOpts(opts) {
assert(is.function(store.destroy), 'store.destroy must be function');
}

const externalKey = opts.externalKey;

if (externalKey) {
assert(is.function(externalKey.get), 'externalKey.get must be function');
assert(is.function(externalKey.set), 'externalKey.set must be function');
}

const ContextStore = opts.ContextStore;
if (ContextStore) {
assert(is.class(ContextStore), 'ContextStore must be a class');
Expand Down
17 changes: 13 additions & 4 deletions lib/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,14 @@ class ContextSession {
debug('init from external');
const ctx = this.ctx;
const opts = this.opts;

const externalKey = ctx.cookies.get(opts.key, opts);
debug('get external key from cookie %s', externalKey);
let externalKey;
if (opts.externalKey) {
externalKey = opts.externalKey.get(opts.key, opts, ctx);
debug('get external key from custom %s', externalKey);
} else {
externalKey = ctx.cookies.get(opts.key, opts);
debug('get external key from cookie %s', externalKey);
}

if (!externalKey) {
// create a new `externalKey`
Expand Down Expand Up @@ -264,7 +269,11 @@ class ContextSession {
changed,
rolling: opts.rolling,
});
this.ctx.cookies.set(key, externalKey, opts);
if (opts.externalKey) {
opts.externalKey.set(key, externalKey, opts, this.ctx);
} else {
this.ctx.cookies.set(key, externalKey, opts);
}
return;
}

Expand Down
64 changes: 64 additions & 0 deletions test/externalKey.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
'use strict';

const Koa = require('koa');
const request = require('supertest');
const assert = require('assert');
const session = require('..');
const store = require('./store');
const TOKEN_KEY = 'User-Token';

describe('Koa Session External Key', () => {
describe('when the external key set/get is invalid', () => {
it('should throw a error', () => {
try {
new App({
externalKey: {},
});
} catch (err) {
assert.equal(err.message, 'externalKey.get must be function');
}
});
});

describe('custom get/set external key', () => {
it('should still work', done => {
const app = App();

app.use(async function(ctx) {
if (ctx.method === 'POST') {
ctx.session.string = ';';
ctx.status = 204;
} else {
ctx.body = ctx.session.string;
}
});

const server = app.listen();

request(server)
.post('/')
.expect(204, (err, res) => {
if (err) return done(err);
const token = res.get(TOKEN_KEY);
request(server)
.get('/')
.set(TOKEN_KEY, token)
.expect(';', done);
});
});
});
});

function App(options) {
const app = new Koa();
app.keys = [ 'a', 'b' ];
options = options || {};
options.store = store;
options.key = TOKEN_KEY;
options.externalKey = options.externalKey || {
get: (key, opts, ctx) => ctx.get(key),
set: (key, value, opts, ctx) => ctx.set(key, value),
};
app.use(session(options, app));
return app;
}