Skip to content

Commit

Permalink
Add complete callback to decision middleware.
Browse files Browse the repository at this point in the history
  • Loading branch information
jaredhanson committed Feb 28, 2017
1 parent 77d19ac commit 83b4893
Show file tree
Hide file tree
Showing 2 changed files with 215 additions and 2 deletions.
10 changes: 8 additions & 2 deletions lib/middleware/decision.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,9 @@ var utils = require('../utils')
* @return {Function}
* @api protected
*/
module.exports = function(server, options, parse) {
module.exports = function(server, options, parse, complete) {
if (typeof options == 'function') {
complete = parse;
parse = options;
options = undefined;
}
Expand Down Expand Up @@ -98,6 +99,11 @@ module.exports = function(server, options, parse) {
if (!req.body[cancelField]) { req.oauth2.res.allow = true; }
else { req.oauth2.res.allow = false; }
}

function completing(cb) {
if (!complete) { return cb(); }
complete(req, req.oauth2, cb);
}

// proxy end() to delete the transaction
var end = res.end;
Expand All @@ -113,7 +119,7 @@ module.exports = function(server, options, parse) {
};
req.oauth2._endProxied = true;

server._respond(req.oauth2, res, function(err) {
server._respond(req.oauth2, res, completing, function(err) {
if (err) { return next(err); }
return next(new AuthorizationError('Unsupported response type: ' + req.oauth2.req.type, 'unsupported_response_type'));
});
Expand Down
207 changes: 207 additions & 0 deletions test/middleware/decision.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,213 @@ describe('decision', function() {
});
});
});

describe('with parsing function and complete function', function() {
var server;

before(function() {
server = new Server();
server.grant('code', 'response', function(txn, res, complete, next) {
if (txn.transactionID !== 'abc123') { return next(new Error('incorrect transaction argument')); }
if (txn.res.scope !== 'no-email') { return next(new Error('incorrect transaction argument')); }

if (txn.res.allow === false) { return res.redirect(txn.redirectURI + '?error=access_denied'); }
complete(function(err) {
if (err) { return next(err); }
return res.redirect(txn.redirectURI + '?code=a1b1c1');
});
});
});

describe('based on transaction', function() {
var request, response, middleware;

before(function() {
middleware = decision(server, function(req, done) {
done(null, { scope: req.query.scope });
}, function(req, txn, done) {
if (txn.client.id !== 'c5678') { return done(new Error('incorrect client argument')); }
if (txn.user.id !== 'u1234') { return done(new Error('incorrect user argument')); }

req.__federated_to__ = {};
req.__federated_to__.client = txn.client;
return done(null);
});
})

before(function(done) {
chai.connect.use('express', middleware)
.req(function(req) {
request = req;
req.query = {};
req.query.scope = 'no-email';
req.body = {};
req.session = {};
req.session['authorize'] = {};
req.session['authorize']['abc123'] = { protocol: 'oauth2' };
req.user = { id: 'u1234', username: 'bob' };
req.oauth2 = {};
req.oauth2.transactionID = 'abc123';
req.oauth2.client = { id: 'c5678', name: 'Example' };
req.oauth2.redirectURI = 'http://example.com/auth/callback';
req.oauth2.req = { type: 'code', scope: 'email' };
})
.end(function(res) {
response = res;
done();
})
.dispatch();
});

it('should set user on transaction', function() {
expect(request.oauth2.user).to.be.an('object');
expect(request.oauth2.user.id).to.equal('u1234');
expect(request.oauth2.user.username).to.equal('bob');
});

it('should set response on transaction', function() {
expect(request.oauth2.res).to.be.an('object');
expect(request.oauth2.res.allow).to.be.true;
expect(request.oauth2.res.scope).to.equal('no-email');
});

it('should complete transaction', function() {
expect(request.__federated_to__).to.be.an('object');
expect(request.__federated_to__.client).to.deep.equal(request.oauth2.client);
expect(request.__federated_to__.client.id).to.equal('c5678');
});

it('should respond', function() {
expect(response.statusCode).to.equal(302);
expect(response.getHeader('Location')).to.equal('http://example.com/auth/callback?code=a1b1c1');
});

it('should remove transaction from session', function() {
expect(request.session['authorize']['abc123']).to.be.undefined;
});
});

describe('without complete callback', function() {
var request, response, middleware;

before(function() {
middleware = decision(server, function(req, done) {
done(null, { scope: req.query.scope });
});
})

before(function(done) {
chai.connect.use('express', middleware)
.req(function(req) {
request = req;
req.query = {};
req.query.scope = 'no-email';
req.body = {};
req.session = {};
req.session['authorize'] = {};
req.session['authorize']['abc123'] = { protocol: 'oauth2' };
req.user = { id: 'u1234', username: 'bob' };
req.oauth2 = {};
req.oauth2.transactionID = 'abc123';
req.oauth2.client = { id: 'c5678', name: 'Example' };
req.oauth2.redirectURI = 'http://example.com/auth/callback';
req.oauth2.req = { type: 'code', scope: 'email' };
})
.end(function(res) {
response = res;
done();
})
.dispatch();
});

it('should set user on transaction', function() {
expect(request.oauth2.user).to.be.an('object');
expect(request.oauth2.user.id).to.equal('u1234');
expect(request.oauth2.user.username).to.equal('bob');
});

it('should set response on transaction', function() {
expect(request.oauth2.res).to.be.an('object');
expect(request.oauth2.res.allow).to.be.true;
expect(request.oauth2.res.scope).to.equal('no-email');
});

it('should respond', function() {
expect(response.statusCode).to.equal(302);
expect(response.getHeader('Location')).to.equal('http://example.com/auth/callback?code=a1b1c1');
});

it('should remove transaction from session', function() {
expect(request.session['authorize']['abc123']).to.be.undefined;
});
});

describe('encountering an error completing transaction', function() {
var request, response, err, middleware;

before(function() {
middleware = decision(server, function(req, done) {
done(null, { scope: req.query.scope });
}, function(req, txn, done) {
return done(new Error('failed to complete transaction'));
});
})

before(function(done) {
chai.connect.use('express', middleware)
.req(function(req) {
request = req;
req.query = {};
req.query.scope = 'no-email';
req.body = {};
req.session = {};
req.session['authorize'] = {};
req.session['authorize']['abc123'] = { protocol: 'oauth2' };
req.user = { id: 'u1234', username: 'bob' };
req.oauth2 = {};
req.oauth2.transactionID = 'abc123';
req.oauth2.client = { id: 'c5678', name: 'Example' };
req.oauth2.redirectURI = 'http://example.com/auth/callback';
req.oauth2.req = { type: 'code', scope: 'email' };
})
.res(function(res) {
response = res;
})
.next(function(e) {
err = e;
done();
})
.end(function(){})
.dispatch();
});

it('should error', function() {
expect(err).to.be.an.instanceOf(Error);
expect(err.message).to.equal('failed to complete transaction');
});

it('should set user on transaction', function() {
expect(request.oauth2.user).to.be.an('object');
expect(request.oauth2.user.id).to.equal('u1234');
expect(request.oauth2.user.username).to.equal('bob');
});

it('should set response on transaction', function() {
expect(request.oauth2.res).to.be.an('object');
expect(request.oauth2.res.allow).to.be.true;
expect(request.oauth2.res.scope).to.equal('no-email');
});

it('should leave transaction in session', function() {
expect(request.session['authorize']['abc123']).to.be.an('object');
});

it('should remove transaction from session after calling end', function() {
response.end();
expect(request.session['authorize']['abc123']).to.be.undefined;
});
});
});

describe('with cancel field option', function() {

Expand Down

0 comments on commit 83b4893

Please sign in to comment.