Skip to content

Commit

Permalink
Update to restify
Browse files Browse the repository at this point in the history
  • Loading branch information
marsanla committed Oct 30, 2014
1 parent bfd4650 commit c295461
Show file tree
Hide file tree
Showing 8 changed files with 242 additions and 348 deletions.
27 changes: 13 additions & 14 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Node OAuth2 Server [![Build Status](https://travis-ci.org/thomseddon/node-oauth2-server.png?branch=2.0)](https://travis-ci.org/thomseddon/node-oauth2-server)
# Node OAuth2 Server

Complete, compliant and well tested module for implementing an OAuth2 Server/Provider with [express](http://expressjs.com/) in [node.js](http://nodejs.org/)
Complete, compliant and well tested module for implementing an OAuth2 Server/Provider with [resitfy](http://mcavage.me/node-restify/) in [node.js](http://nodejs.org/)

## Installation

Expand All @@ -10,32 +10,31 @@ npm install oauth2-server

## Quick Start

The module provides two middlewares, one for authorization and routing, another for error handling, use them as you would any other middleware:
The module provides one middleware for authorization and routing, use it as you would any other middleware:

```js
var express = require('express'),
bodyParser = require('body-parser'),
oauthserver = require('oauth2-server');
var restify = require('restify'),
oauthserver = require('oauth2-server-restify');

var app = express();
var server = restify.createServer();

app.use(bodyParser()); // REQUIRED
server.use(restify.bodyParser({})); // REQUIRED

app.oauth = oauthserver({
server.oauth = oauthserver({
model: {}, // See below for specification
grants: ['password'],
debug: true
});

app.all('/oauth/token', app.oauth.grant());
server.post('/oauth/token', server.oauth.grant());

app.get('/', app.oauth.authorise(), function (req, res) {
server.get('/', server.oauth.authorise(), function (req, res, next) {
res.send('Secret area');
next();
});

app.use(app.oauth.errorHandler());

app.listen(3000);
server.listen(3000);
```

After running with node, visting http://127.0.0.1:3000 should present you with a json response saying your access token could not be found.
Expand Down Expand Up @@ -311,7 +310,7 @@ See: https://github.com/thomseddon/node-oauth2-server/blob/master/Changelog.md

## Credits

Copyright (c) 2013 Thom Seddon
Copyright (c) 2013 Thom Seddon & Marcos Sanz

## License

Expand Down
76 changes: 36 additions & 40 deletions lib/authCodeGrant.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
* limitations under the License.
*/

var error = require('./error'),
runner = require('./runner'),
token = require('./token');
var error = require('node-restify-errors'),
runner = require('./runner'),
token = require('./token');

module.exports = AuthCodeGrant;

Expand Down Expand Up @@ -54,7 +54,7 @@ function AuthCodeGrant(config, req, res, next, check) {
if (err && res.oauthRedirect) {
// Custom redirect error handler
res.redirect(self.client.redirectUri + '?error=' + err.error +
'&error_description=' + err.error_description + '&code=' + err.code);
'&error_description=' + err.error_description + '&code=' + err.code);

return self.config.continueAfterResponse ? next() : null;
}
Expand All @@ -66,131 +66,127 @@ function AuthCodeGrant(config, req, res, next, check) {
/**
* Check Request Params
*
* @param {Function} done
* @param {Function} next
* @this OAuth
*/
function checkParams (done) {
function checkParams (next) {
var body = this.req.body;
var query = this.req.query;
if (!body && !query) return done(error('invalid_request'));
if (!body && !query) return next(new error.BadMethodError());

// Response type
this.responseType = body.response_type || query.response_type;
if (this.responseType !== 'code') {
return done(error('invalid_request',
'Invalid response_type parameter (must be "code")'));
return next(new error.BadMethodError('Invalid response_type parameter (must be "code")'));
}

// Client
this.clientId = body.client_id || query.client_id;
if (!this.clientId) {
return done(error('invalid_request',
'Invalid or missing client_id parameter'));
return next(new error.BadMethodError('Invalid or missing client_id parameter'));
}

// Redirect URI
this.redirectUri = body.redirect_uri || query.redirect_uri;
if (!this.redirectUri) {
return done(error('invalid_request',
'Invalid or missing redirect_uri parameter'));
return next(new error.BadMethodError('Invalid or missing redirect_uri parameter'));
}

done();
next();
}

/**
* Check client against model
*
* @param {Function} done
* @param {Function} next
* @this OAuth
*/
function checkClient (done) {
function checkClient (next) {
var self = this;
this.model.getClient(this.clientId, null, function (err, client) {
if (err) return done(error('server_error', false, err));
if (err) return next(new error.InternalError(err));

if (!client) {
return done(error('invalid_client', 'Invalid client credentials'));
return next(new error.InvalidCredentialsError('Invalid client credentials'));
} else if (Array.isArray(client.redirectUri)) {
if (client.redirectUri.indexOf(self.redirectUri) === -1) {
return done(error('invalid_request', 'redirect_uri does not match'));
return next(new error.BadMethodError('redirect_uri does not match'));
}
client.redirectUri = self.redirectUri;
} else if (client.redirectUri !== self.redirectUri) {
return done(error('invalid_request', 'redirect_uri does not match'));
return next(new error.BadMethodError('redirect_uri does not match'));
}

// The request contains valid params so any errors after this point
// are redirected to the redirect_uri
self.res.oauthRedirect = true;
self.client = client;

done();
next();
});
}

/**
* Check client against model
*
* @param {Function} done
* @param {Function} next
* @this OAuth
*/
function checkUserApproved (done) {
function checkUserApproved (next) {
var self = this;
this.check(this.req, function (err, allowed, user) {
if (err) return done(error('server_error', false, err));
if (err) return new next(error.InternalError(err));

if (!allowed) {
return done(error('access_denied',
'The user denied access to your application'));
return next(new error.NotAuthorizedError('The user denied access to your application'));
}

self.user = user;
done();
next();
});
}

/**
* Check client against model
*
* @param {Function} done
* @param {Function} next
* @this OAuth
*/
function generateCode (done) {
function generateCode (next) {
var self = this;
token(this, 'authorization_code', function (err, code) {
self.authCode = code;
done(err);
next(err);
});
}

/**
* Check client against model
*
* @param {Function} done
* @param {Function} next
* @this OAuth
*/
function saveAuthCode (done) {
function saveAuthCode (next) {
var expires = new Date();
expires.setSeconds(expires.getSeconds() + this.config.authCodeLifetime);

this.model.saveAuthCode(this.authCode, this.client.clientId, expires,
this.user, function (err) {
if (err) return done(error('server_error', false, err));
done();
});
this.user, function (err) {
if (err) return new next(error.InternalError(err));
next();
});
}

/**
* Check client against model
*
* @param {Function} done
* @param {Function} next
* @this OAuth
*/
function redirect (done) {
function redirect (next) {
this.res.redirect(this.client.redirectUri + '?code=' + this.authCode +
(this.req.query.state ? '&state=' + this.req.query.state : ''));
(this.req.query.state ? '&state=' + this.req.query.state : ''));

if (this.config.continueAfterResponse)
return done();
return next();
}
78 changes: 40 additions & 38 deletions lib/authorise.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
* limitations under the License.
*/

var error = require('./error'),
runner = require('./runner');
var error = require('node-restify-errors'),
runner = require('./runner');

module.exports = Authorise;

Expand Down Expand Up @@ -50,81 +50,83 @@ function Authorise (config, req, next) {
*
* Extract token from request according to RFC6750
*
* @param {Function} done
* @param {Function} next
* @this OAuth
*/
function getBearerToken (done) {
var headerToken = this.req.get('Authorization'),
getToken = this.req.query.access_token,
postToken = this.req.body ? this.req.body.access_token : undefined;
function getBearerToken (next) {
var headerToken = this.req.authorization,
getToken = this.req.query.access_token,
postToken = this.req.body ? this.req.body.access_token : undefined;

// Check exactly one method was used
var methodsUsed = (headerToken !== undefined) + (getToken !== undefined) +
(postToken !== undefined);
(postToken !== undefined);

if (methodsUsed > 1) {
return done(error('invalid_request',
'Only one method may be used to authenticate at a time (Auth header, ' +
'GET or POST).'));
return next(new error.BadMethodError('Only one method may be used to authenticate at a time (Auth header, GET or POST).'));
} else if (methodsUsed === 0) {
return done(error('invalid_request', 'The access token was not found'));
return next(new error.InvalidCredentialsError('The access token was not found'));
}

// Header: http://tools.ietf.org/html/rfc6750#section-2.1
if (headerToken) {
var matches = headerToken.match(/Bearer\s(\S+)/);
if (headerToken && headerToken.length > 0) {
var matches = (headerToken.scheme === 'Bearer') ? headerToken.scheme : null;

if (!matches) {
return done(error('invalid_request', 'Malformed auth header'));
return next(new error.InvalidHeaderError('Malformed auth header'));
}

headerToken = matches[1];
} else {
headerToken = undefined;
}

// POST: http://tools.ietf.org/html/rfc6750#section-2.2
if (postToken) {
if (this.req.method === 'GET') {
return done(error('invalid_request',
'Method cannot be GET When putting the token in the body.'));
return next(new error.BadMethodError('Method cannot be GET When putting the token in the body.'));
}

if (!this.req.is('application/x-www-form-urlencoded')) {
return done(error('invalid_request', 'When putting the token in the ' +
'body, content type must be application/x-www-form-urlencoded.'));
return next(new error.BadMethodError('When putting the token in the ' +
'body, content type must be application/x-www-form-urlencoded.'));
}
}

this.bearerToken = headerToken || postToken || getToken;
done();
next();
}

/**
* Check token
*
* Check it against model, ensure it's not expired
* @param {Function} done
* @param {Function} next
* @this OAuth
*/
function checkToken (done) {
function checkToken (next) {
var self = this;
this.model.getAccessToken(this.bearerToken, function (err, token) {
if (err) return done(error('server_error', false, err));

if (!token) {
return done(error('invalid_token',
'The access token provided is invalid.'));
}
if (this.model && this.model.length > 0) {
this.model.getAccessToken(this.bearerToken, function (err, token) {
if (err) return next(new error.InternalError(err));

if (token.expires !== null &&
(!token.expires || token.expires < new Date())) {
return done(error('invalid_token',
'The access token provided has expired.'));
}
if (!token) {
return next(new error.InvalidCredentialsError('The access token provided is invalid.'));
}

// Expose params
self.req.oauth = { bearerToken: token };
self.req.user = token.user ? token.user : { id: token.userId };
if (token.expires !== null &&
(!token.expires || token.expires < new Date())) {
return next(new error.InvalidCredentialsError('The access token provided has expired.'));
}

done();
});
// Expose params
self.req.oauth = {bearerToken: token};
self.req.user = token.user ? token.user : {id: token.userId};

next();
});
} else {
next(new error.InvalidCredentialsError('The access token can not be find.'));
}
}
Loading

0 comments on commit c295461

Please sign in to comment.