diff --git a/Readme.md b/Readme.md index b141f5641..772e27856 100644 --- a/Readme.md +++ b/Readme.md @@ -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 @@ -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. @@ -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 diff --git a/lib/authCodeGrant.js b/lib/authCodeGrant.js index 616bfbdeb..cf16dee97 100644 --- a/lib/authCodeGrant.js +++ b/lib/authCodeGrant.js @@ -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; @@ -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; } @@ -66,58 +66,55 @@ 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 @@ -125,72 +122,71 @@ function checkClient (done) { 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(); } diff --git a/lib/authorise.js b/lib/authorise.js index 2b8296019..14c7a62d7 100644 --- a/lib/authorise.js +++ b/lib/authorise.js @@ -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; @@ -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.')); + } } diff --git a/lib/error.js b/lib/error.js deleted file mode 100644 index 04d181ee8..000000000 --- a/lib/error.js +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright 2013-present NightWorld. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -var util = require('util'); - -module.exports = OAuth2Error; - -/** - * Error - * - * @param {Number} code Numeric error code - * @param {String} error Error descripton - * @param {String} description Full error description - */ -function OAuth2Error (error, description, err) { - if (!(this instanceof OAuth2Error)) - return new OAuth2Error(error, description, err); - - Error.call(this); - - this.name = this.constructor.name; - if (err instanceof Error) { - this.message = err.message; - this.stack = err.stack; - } else { - this.message = description; - Error.captureStackTrace(this, this.constructor); - } - - switch (error) { - case 'invalid_client': - this.headers = { - 'WWW-Authenticate': 'Basic realm="Service"' - }; - /* falls through */ - case 'invalid_grant': - case 'invalid_request': - this.code = 400; - break; - case 'invalid_token': - this.code = 401; - break; - case 'server_error': - this.code = 503; - break; - default: - this.code = 500; - } - - this.error = error; - this.error_description = description || error; -} - -util.inherits(OAuth2Error, Error); diff --git a/lib/grant.js b/lib/grant.js index 2ebbcd817..6ad6c3046 100644 --- a/lib/grant.js +++ b/lib/grant.js @@ -15,9 +15,9 @@ */ var auth = require('basic-auth'), - error = require('./error'), - runner = require('./runner'), - token = require('./token'); + error = require('node-restify-errors'), + runner = require('./runner'), + token = require('./token'); module.exports = Grant; @@ -59,36 +59,33 @@ function Grant (config, req, res, next) { /** * Basic request validation and extraction of grant_type and client creds * - * @param {Function} done + * @param {Function} next * @this OAuth */ -function extractCredentials (done) { +function extractCredentials (next) { // Only POST via application/x-www-form-urlencoded is acceptable if (this.req.method !== 'POST' || - !this.req.is('application/x-www-form-urlencoded')) { - return done(error('invalid_request', - 'Method must be POST with application/x-www-form-urlencoded encoding')); + !this.req.is('application/x-www-form-urlencoded')) { + return next(new error.BadMethodError('Method must be POST with application/x-www-form-urlencoded encoding')); } // Grant type this.grantType = this.req.body && this.req.body.grant_type; if (!this.grantType || !this.grantType.match(this.config.regex.grantType)) { - return done(error('invalid_request', - 'Invalid or missing grant_type parameter')); + return next(new error.BadMethodError('Invalid or missing grant_type parameter')); } // Extract credentials // http://tools.ietf.org/html/rfc6749#section-3.2.1 this.client = credsFromBasic(this.req) || credsFromBody(this.req); if (!this.client.clientId || - !this.client.clientId.match(this.config.regex.clientId)) { - return done(error('invalid_client', - 'Invalid or missing client_id parameter')); + !this.client.clientId.match(this.config.regex.clientId)) { + return next(new error.InvalidCredentialsError('Invalid or missing client_id parameter')); } else if (!this.client.clientSecret) { - return done(error('invalid_client', 'Missing client_secret parameter')); + return next(new error.InvalidCredentialsError('Missing client_secret parameter')); } - done(); + next(); } /** @@ -127,248 +124,241 @@ function credsFromBody (req) { /** * Check extracted client against model * - * @param {Function} done + * @param {Function} next * @this OAuth */ -function checkClient (done) { +function checkClient (next) { this.model.getClient(this.client.clientId, this.client.clientSecret, - function (err, client) { - if (err) return done(error('server_error', false, err)); + function (err, client) { + if (err) return next(new error.InternalError(err)); - if (!client) { - return done(error('invalid_client', 'Client credentials are invalid')); - } + if (!client) { + return next(new error.InvalidCredentialsError('Client credentials are invalid')); + } - done(); - }); + next(); + }); } /** * Delegate to the relvant grant function based on grant_type * - * @param {Function} done + * @param {Function} next * @this OAuth */ -function checkGrantType (done) { +function checkGrantType (next) { if (this.grantType.match(/^http(s|):\/\//) && this.model.extendedGrant) { - return useExtendedGrant.call(this, done); + return useExtendedGrant.call(this, next); } switch (this.grantType) { case 'authorization_code': - return useAuthCodeGrant.call(this, done); + return useAuthCodeGrant.call(this, next); case 'password': - return usePasswordGrant.call(this, done); + return usePasswordGrant.call(this, next); case 'refresh_token': - return useRefreshTokenGrant.call(this, done); + return useRefreshTokenGrant.call(this, next); case 'client_credentials': - return useClientCredentialsGrant.call(this, done); + return useClientCredentialsGrant.call(this, next); default: - done(error('invalid_request', - 'Invalid grant_type parameter or parameter missing')); + next(new error.MissingParameterError('Invalid grant_type parameter or parameter missing')); } } /** * Grant for authorization_code grant type * - * @param {Function} done + * @param {Function} next */ -function useAuthCodeGrant (done) { +function useAuthCodeGrant (next) { var code = this.req.body.code; if (!code) { - return done(error('invalid_request', 'No "code" parameter')); + return next(new error.MissingParameterError('No "code" parameter')); } var self = this; this.model.getAuthCode(code, function (err, authCode) { - if (err) return done(error('server_error', false, err)); + if (err) return next(new error('server_error', false, err)); if (!authCode || authCode.clientId !== self.client.clientId) { - return done(error('invalid_grant', 'Invalid code')); + return next(new error.InvalidContentError('Invalid code')); } else if (authCode.expires < self.now) { - return done(error('invalid_grant', 'Code has expired')); + return next(new error.InvalidContentError('Code has expired')); } self.user = authCode.user || { id: authCode.userId }; if (!self.user.id) { - return done(error('server_error', false, - 'No user/userId parameter returned from getauthCode')); + return next(new error.InternalError('No user/userId parameter returned from getauthCode')); } - done(); + next(); }); } /** * Grant for password grant type * - * @param {Function} done + * @param {Function} next */ -function usePasswordGrant (done) { +function usePasswordGrant (next) { // User credentials var uname = this.req.body.username, - pword = this.req.body.password; + pword = this.req.body.password; if (!uname || !pword) { - return done(error('invalid_client', - 'Missing parameters. "username" and "password" are required')); + return next(new error.InvalidCredentialsError('Missing parameters. "username" and "password" are required')); } var self = this; return this.model.getUser(uname, pword, function (err, user) { - if (err) return done(error('server_error', false, err)); + if (err) return next(new error.InternalError(err)); if (!user) { - return done(error('invalid_grant', 'User credentials are invalid')); + return next(new error.InvalidCredentialsError('User credentials are invalid')); } self.user = user; - done(); + next(); }); } /** * Grant for refresh_token grant type * - * @param {Function} done + * @param {Function} next */ -function useRefreshTokenGrant (done) { +function useRefreshTokenGrant (next) { var token = this.req.body.refresh_token; if (!token) { - return done(error('invalid_request', 'No "refresh_token" parameter')); + return next(new error.BadMethodError('No "refresh_token" parameter')); } var self = this; this.model.getRefreshToken(token, function (err, refreshToken) { - if (err) return done(error('server_error', false, err)); + if (err) return next(new error.InternalError(err)); if (!refreshToken || refreshToken.clientId !== self.client.clientId) { - return done(error('invalid_grant', 'Invalid refresh token')); + return next(new error.BadMethodError('Invalid refresh token')); } else if (refreshToken.expires !== null && - refreshToken.expires < self.now) { - return done(error('invalid_grant', 'Refresh token has expired')); + refreshToken.expires < self.now) { + return next(new error.BadMethodError('Refresh token has expired')); } if (!refreshToken.user && !refreshToken.userId) { - return done(error('server_error', false, - 'No user/userId parameter returned from getRefreshToken')); + return next(new error.InternalError('No user/userId parameter returned from getRefreshToken')); } self.user = refreshToken.user || { id: refreshToken.userId }; if (self.model.revokeRefreshToken) { return self.model.revokeRefreshToken(token, function (err) { - if (err) return done(error('server_error', false, err)); - done(); + if (err) return next(new error.InternalError(err)); + next(); }); } - done(); + next(); }); } /** * Grant for client_credentials grant type * - * @param {Function} done + * @param {Function} next */ -function useClientCredentialsGrant (done) { +function useClientCredentialsGrant (next) { // Client credentials var clientId = this.client.clientId, - clientSecret = this.client.clientSecret; + clientSecret = this.client.clientSecret; if (!clientId || !clientSecret) { - return done(error('invalid_client', - 'Missing parameters. "client_id" and "client_secret" are required')); + return next(new error.InvalidCredentialsError('Missing parameters. "client_id" and "client_secret" are required')); } var self = this; return this.model.getUserFromClient(clientId, clientSecret, - function (err, user) { - if (err) return done(error('server_error', false, err)); - if (!user) { - return done(error('invalid_grant', 'Client credentials are invalid')); - } - - self.user = user; - done(); - }); + function (err, user) { + if (err) return next(new error.InternalError(err)); + if (!user) { + return next(new error.InvalidCredentialsError('Client credentials are invalid')); + } + + self.user = user; + next(); + }); } /** * Grant for extended (http://*) grant type * - * @param {Function} done + * @param {Function} next */ -function useExtendedGrant (done) { +function useExtendedGrant (next) { var self = this; this.model.extendedGrant(this.grantType, this.req, - function (err, supported, user) { - if (err) { - return done(error(err.error || 'server_error', - err.description || err.message, err)); - } - - if (!supported) { - return done(error('invalid_request', - 'Invalid grant_type parameter or parameter missing')); - } else if (!user || user.id === undefined) { - return done(error('invalid_request', 'Invalid request.')); - } - - self.user = user; - done(); - }); + function (err, supported, user) { + if (err) { + return next(new error(err.error || 'server_error', + err.description || err.message, err)); + } + + if (!supported) { + return next(new error.BadMethodError('Invalid grant_type parameter or parameter missing')); + } else if (!user || user.id === undefined) { + return next(new error.BadMethodError('Invalid request.')); + } + + self.user = user; + next(); + }); } /** * Check the grant type is allowed for this client * - * @param {Function} done + * @param {Function} next * @this OAuth */ -function checkGrantTypeAllowed (done) { +function checkGrantTypeAllowed (next) { this.model.grantTypeAllowed(this.client.clientId, this.grantType, - function (err, allowed) { - if (err) return done(error('server_error', false, err)); + function (err, allowed) { + if (err) return next(new error.InternalError(err)); - if (!allowed) { - return done(error('invalid_client', - 'The grant type is unauthorised for this client_id')); - } + if (!allowed) { + return next(new error.InvalidCredentialsError('The grant type is unauthorised for this client_id')); + } - done(); - }); + next(); + }); } /** * Generate an access token * - * @param {Function} done + * @param {Function} next * @this OAuth */ -function generateAccessToken (done) { +function generateAccessToken (next) { var self = this; token(this, 'accessToken', function (err, token) { self.accessToken = token; - done(err); + next(err); }); } /** * Save access token with model * - * @param {Function} done + * @param {Function} next * @this OAuth */ -function saveAccessToken (done) { +function saveAccessToken (next) { var accessToken = this.accessToken; - // Object idicates a reissue + // Object indicates a reissue if (typeof accessToken === 'object' && accessToken.accessToken) { this.accessToken = accessToken.accessToken; - return done(); + return next(); } var expires = null; @@ -378,43 +368,43 @@ function saveAccessToken (done) { } this.model.saveAccessToken(accessToken, 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 next(new error.InternalError(err)); + next(); + }); } /** * Generate a refresh token * - * @param {Function} done + * @param {Function} next * @this OAuth */ -function generateRefreshToken (done) { - if (this.config.grants.indexOf('refresh_token') === -1) return done(); +function generateRefreshToken (next) { + if (this.config.grants.indexOf('refresh_token') === -1) return next(); var self = this; token(this, 'refreshToken', function (err, token) { self.refreshToken = token; - done(err); + next(err); }); } /** * Save refresh token with model * - * @param {Function} done + * @param {Function} next * @this OAuth */ -function saveRefreshToken (done) { +function saveRefreshToken (next) { var refreshToken = this.refreshToken; - if (!refreshToken) return done(); + if (!refreshToken) return next(); - // Object idicates a reissue + // Object indicates a reissue if (typeof refreshToken === 'object' && refreshToken.refreshToken) { this.refreshToken = refreshToken.refreshToken; - return done(); + return next(); } var expires = null; @@ -424,19 +414,19 @@ function saveRefreshToken (done) { } this.model.saveRefreshToken(refreshToken, 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 next(new error.InternalError(err)); + next(); + }); } /** * Create an access token and save it with the model * - * @param {Function} done + * @param {Function} next * @this OAuth */ -function sendResponse (done) { +function sendResponse (next) { var response = { token_type: 'bearer', access_token: this.accessToken @@ -448,8 +438,8 @@ function sendResponse (done) { if (this.refreshToken) response.refresh_token = this.refreshToken; - this.res.jsonp(response); + this.res.send(response); if (this.config.continueAfterResponse) - done(); + next(); } diff --git a/lib/oauth2server.js b/lib/oauth2server.js index fc6b9c1dc..b5dd5d799 100644 --- a/lib/oauth2server.js +++ b/lib/oauth2server.js @@ -14,10 +14,9 @@ * limitations under the License. */ -var error = require('./error'), - AuthCodeGrant = require('./authCodeGrant'), - Authorise = require('./authorise'), - Grant = require('./grant'); +var AuthCodeGrant = require('./authCodeGrant'), + Authorise = require('./authorise'), + Grant = require('./grant'); module.exports = OAuth2Server; @@ -38,15 +37,15 @@ function OAuth2Server (config) { this.grants = config.grants || []; this.debug = config.debug || function () {}; if (typeof this.debug !== 'function') { - this.debug = console.log; + this.debug = console.log; } this.passthroughErrors = config.passthroughErrors; this.continueAfterResponse = config.continueAfterResponse; this.accessTokenLifetime = config.accessTokenLifetime !== undefined ? - config.accessTokenLifetime : 3600; + config.accessTokenLifetime : 3600; this.refreshTokenLifetime = config.refreshTokenLifetime !== undefined ? - config.refreshTokenLifetime : 1209600; + config.refreshTokenLifetime : 1209600; this.authCodeLifetime = config.authCodeLifetime || 30; this.regex = { @@ -100,37 +99,11 @@ OAuth2Server.prototype.authCodeGrant = function (check) { var self = this; return function (req, res, next) { + console.log('authCodeGrant'); new AuthCodeGrant(self, req, res, next, check); }; }; -/** - * OAuth Error Middleware - * - * Returns middleware that will catch OAuth errors and ensure an OAuth - * complaint response - * - * @return {Function} middleware - */ -OAuth2Server.prototype.errorHandler = function () { - var self = this; - - return function (err, req, res, next) { - if (!(err instanceof error) || self.passthroughErrors) return next(err); - - delete err.name; - delete err.message; - - self.debug(err.stack || err); - delete err.stack; - - if (err.headers) res.set(err.headers); - delete err.headers; - - res.send(err.code, err); - }; -}; - /** * Lockdown * @@ -144,24 +117,30 @@ OAuth2Server.prototype.errorHandler = function () { * e.g.: * * ` - * app.all('/oauth/token', app.oauth.grant); + * server.post('/oauth/token', app.oauth.grant); * - * app.get('/secrets', function (req, res) { + * server.get('/secrets', function (req, res, next) { * res.send('secrets'); + * next(); * }); * - * app.get('/public', app.oauth.bypass, function (req, res) { - * res.send('publci'); + * server.get('/public', server.oauth.bypass, function (req, res, next) { + * res.send('public'); + * next(); * }); * - * app.oauth.lockdown(app); + * server.oauth.lockdown(server); * ` * - * @param {Object} app Express app + * @param {Object} server Restify server */ -OAuth2Server.prototype.lockdown = function (app) { +OAuth2Server.prototype.lockdown = function (server) { var self = this; + var lockdownRestify = function (mount) { + + }; + var lockdownExpress3 = function (stack) { // Check if it's a grant route var pos = stack.indexOf(self.grant); @@ -211,13 +190,7 @@ OAuth2Server.prototype.lockdown = function (app) { } }; - if (app.routes) { - for (var method in app.routes) { - app.routes[method].callbacks.forEach(lockdownExpress3); - } - } else { - app._router.stack.forEach(lockdownExpress4); - } + server.router.mounts.forEach(lockdownRestify); }; /** diff --git a/lib/token.js b/lib/token.js index 77423422a..af422b089 100644 --- a/lib/token.js +++ b/lib/token.js @@ -15,7 +15,7 @@ */ var crypto = require('crypto'), - error = require('./error'); + error = require('node-restify-errors'); module.exports = Token; @@ -29,7 +29,7 @@ module.exports = Token; function Token (config, type, callback) { if (config.model.generateToken) { config.model.generateToken(type, config.req, function (err, token) { - if (err) return callback(error('server_error', false, err)); + if (err) return callback(new error.InternalError(err)); if (!token) return generateRandomToken(callback); callback(false, token); }); @@ -45,7 +45,7 @@ function Token (config, type, callback) { */ var generateRandomToken = function (callback) { crypto.randomBytes(256, function (ex, buffer) { - if (ex) return callback(error('server_error')); + if (ex) return callback(new error.InternalError()); var token = crypto .createHash('sha1') diff --git a/package.json b/package.json index f82742fe1..61d55e5e0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "oauth2-server", - "description": "Complete, compliant and well tested module for implementing an OAuth2 Server/Provider with express in node.js", + "name": "oauth2-server-restify", + "description": "Complete, compliant and well tested module for implementing an OAuth2 Server/Provider with restify in node.js", "version": "2.3.0", "keywords": [ "oauth", @@ -12,13 +12,14 @@ }, "contributors": [ { - "name": "Thom Seddon", - "email": "thom@seddonmedia.co.uk" + "name": "Marcos Sanz Latorre", + "email": "marcos.sanz@13genius.com" } ], "main": "lib/oauth2server.js", "dependencies": { - "basic-auth": "~0.0.1" + "basic-auth": "~0.0.1", + "node-restify-errors": "~0.1.0" }, "devDependencies": { "body-parser": "~1.3.1", @@ -41,6 +42,6 @@ }, "repository": { "type": "git", - "url": "https://github.com/thomseddon/node-oauth2-server.git" + "url": "https://github.com/marsanla/node-oauth2-server-restify.git" } }