From acced76bd771b2f9b11ad98dfdb2225f834da18c Mon Sep 17 00:00:00 2001 From: ravitmg Date: Fri, 19 Aug 2022 11:34:50 +0545 Subject: [PATCH 01/12] Refactor to extend routes functionality --- .versions | 2 +- lib/oauth.js | 422 +++++++++++++++++++++++++------------------------- lib/webapp.js | 3 +- package.js | 2 +- 4 files changed, 218 insertions(+), 211 deletions(-) diff --git a/.versions b/.versions index 85b1308..803665b 100644 --- a/.versions +++ b/.versions @@ -30,7 +30,7 @@ http@1.4.4 id-map@1.1.1 inter-process-messaging@0.1.1 jkuester:http@2.1.0 -leaonline:oauth2-server@4.2.1 +ravitmg:oauth2-server@4.2.1 lmieulet:meteor-coverage@3.2.0 lmieulet:meteor-legacy-coverage@0.1.0 lmieulet:meteor-packages-coverage@0.1.0 diff --git a/lib/oauth.js b/lib/oauth.js index 2eb65a4..1cb4f07 100644 --- a/lib/oauth.js +++ b/lib/oauth.js @@ -31,23 +31,6 @@ import OAuthserver from '@node-oauth/oauth2-server' const { Request, Response } = OAuthserver -/** - * Publishes all authorized clients for the current authenticated user. - * Does not touch any other user-related fields. - * Allows to inject a custom publication-name. - * @param pubName {string} - * @return {function():Mongo.Cursor} - * @private - */ -const publishAuthorizedClients = (pubName) => { - return Meteor.publish(pubName, function () { - if (!this.userId) { - return this.ready() - } - return Meteor.users.find({ _id: this.userId }, { fields: { 'oauth.authorizedClients': 1 } }) - }) -} - /** * The base class of this package. * Represents an oauth2-server with a default model setup for Meteor/Mongo. @@ -88,9 +71,10 @@ export class OAuth2Server { const oauthOptions = Object.assign({ model: this.model }, serverOptions) this.oauth = new OAuthserver(oauthOptions) - const authorizedPubName = (serverOptions && serverOptions.authorizedPublicationName) || 'authorizedOAuth' - publishAuthorizedClients(authorizedPubName) - initRoutes(this, routes) + this.validateClientRequest() + this.addUserToRequest() + this.generateAuthorizationCodeResponse() + this.generateAccessTokenResponse() return this } @@ -193,12 +177,195 @@ export class OAuth2Server { } } } -} -const initRoutes = (self, { accessTokenUrl = '/oauth/token', authorizeUrl = '/oauth/authorize', errorUrl = '/oauth/error', fallbackUrl = '/oauth/*' } = {}) => { - const debugMiddleware = getDebugMiddleWare(self) + // STEP 1: VALIDATE CLIENT REQUEST + // Note from https://www.oauth.com/oauth2-servers/authorization/the-authorization-response/ + // If there is something wrong with the syntax of the request, such as the redirect_uri or client_id is invalid, + // then it’s important not to redirect the user and instead you should show the error message directly. + // This is to avoid letting your authorization server be used as an open redirector. + validateClientRequest () { + const self = this + const { authorizeUrl } = this.config.routes + self.route('get', authorizeUrl, function (req, res, next) { + const validRequestParams = self.validateRequestParams(req, requiredAuthorizeGetParams) + if (!validRequestParams) return; + + const validResponseType = self.validateResponseType(req, res) + if (!validResponseType) return + + const client = self.getValidatedClient(req, res) + if (!client) return + + const redirectUri = self.getValidatedRedirectUri(req, res, client) + if (!redirectUri) return + + return next() + }) + } + + // STEP 2: ADD USER TO THE REQUEST + // validate all inputs again, since all inputs + // could have been manipulated within form + addUserToRequest () { + const self = this + const { authorizeUrl } = this.config.routes + self.route('post', authorizeUrl, function (req, res, next) { + const validRequestParams = self.validateRequestParams(req, requiredAuthorizeGetParams) + if (!validRequestParams) return; + + const client = self.getValidatedClient(req, res) + if (!client) return + + const validRedirectUri = self.getValidatedRedirectUri(req, res, client) + if (!validRedirectUri) return + + // token refers here to the Meteor.loginToken, + // which is assigned, once the user has been validly logged-in + // only valid tokens can be used to find a user + // in the Meteor.users collection + const user = Meteor.users.findOne({ + 'services.resume.loginTokens.hashedToken': Accounts._hashLoginToken(req.body.token) + }) + + // we fail already here if no user has been found + // since the oauth-node sever would repsond with a + // 503 error, while it should be a 400 + const validateUserCredentials = { user, client } + + if (!user || !UserValidation.isValid(self, validateUserCredentials)) { + return errorHandler(res, { + status: 400, + error: 'access_denied', + description: 'You are no valid user', + state: req.body.state, + debug: self.debug + }) + } + + const id = user._id + req.user = { id } // TODO add fields from scope + + if (req.body.allowed === 'false') { + Meteor.users.update(id, { $pull: { 'oauth.authorizedClients': client.clientId } }) + } + else { + Meteor.users.update(id, { $addToSet: { 'oauth.authorizedClients': client.clientId } }) + } + + // make this work on a post route + req.query.allowed = req.body.allowed + + return next() + }) + } + + generateAuthorizationCodeResponse () { + const self = this + const { authorizeUrl } = this.config.routes + // STEP 3: GENERATE AUTHORIZATION CODE RESPONSE + // - use the user form the prior middleware for the authentication handler + // - on allow, assign the client_id to the user's authorized clients + // - on deny, ...? + // - construct the redirect query and redirect to the redirect_uri + self.route('post', authorizeUrl, function (req, res /*, next */) { + const request = new Request(req) + const response = new Response(res) + const authorizeOptions = { + authenticateHandler: { + handle: function (request, response) { + return request.user + } + } + } + + return self.oauth.authorize(request, response, authorizeOptions) + .then(bind(function (code) { + const query = new URLSearchParams({ + code: code.authorizationCode, + user: req.user.id, + state: req.body.state + }) + + const finalRedirectUri = `${req.body.redirect_uri}?${query}` + + res.statusCode = 302 + res.setHeader('Location', finalRedirectUri) + res.end() + })) + .catch(function (err) { + errorHandler(res, { + originalError: err, + error: err.name, + description: err.message, + status: err.statusCode, + state: req.body.state, + debug: self.debug + }) + }) + }) + } - const validateResponseType = (req, res) => { + generateAccessTokenResponse () { + const self = this + const { accessTokenUrl } = this.config.routes + // STEP 4: GENERATE ACCESS TOKEN RESPONSE + // - validate params + // - validate authorization code + // - issue accessToken and refreshToken + self.route('post', accessTokenUrl, function (req, res, next) { + if (!validateParams(req.body, requiredAccessTokenPostParams, self.debug)) { + return errorHandler(res, { + status: 400, + error: 'invalid_request', + description: 'One or more request parameters are invalid', + state: req.body.state, + debug: self.debug + }) + } + + const request = new Request(req) + const response = new Response(res) + return self.oauth.token(request, response) + .then(function (token) { + res.writeHead(200, { + 'Content-Type': 'application/json', + 'Cache-Control': 'no-store', + Pragma: 'no-cache' + }) + const body = JSON.stringify({ + access_token: token.accessToken, + token_type: 'bearer', + expires_in: token.accessTokenExpiresAt, + refresh_token: token.refreshToken + }) + res.end(body) + }) + .catch(function (err) { + return errorHandler(res, { + error: 'unauthorized_client', + description: err.message, + state: req.body.state, + debug: self.debug, + status: err.statusCode + }) + }) + }) + } + + initFallbackRoutes () { + const self = this + const { fallbackUrl } = this.config.routes + self.route('use', fallbackUrl, function (req, res, next) { + return errorHandler(res, { + error: 'route not found', + status: 404, + debug: self.debug + }) + }) + } + + validateResponseType (req, res) { + const self = this const responseType = req.method.toLowerCase() === 'get' ? req.query.response_type : req.body.response_type @@ -214,7 +381,22 @@ const initRoutes = (self, { accessTokenUrl = '/oauth/token', authorizeUrl = '/oa return true } - const getValidatedClient = (req, res) => { + validateRequestParams (req, expectedParams) { + const self = this + if (!validateParams(req.query, expectedParams, self.debug)) { + return errorHandler(res, { + status: 400, + error: 'invalid_request', + description: 'One or more request parameters are invalid', + state: req.query.state, + debug: self.debug + }) + } + return true + } + + getValidatedClient (req, res) { + const self = this const clientId = req.method.toLowerCase() === 'get' ? req.query.client_id : req.body.client_id const secret = req.method.toLowerCase() === 'get' ? req.query.client_secret : req.body.client_secret const client = Promise.await(self.model.getClient(clientId, secret)) @@ -231,7 +413,8 @@ const initRoutes = (self, { accessTokenUrl = '/oauth/token', authorizeUrl = '/oa return client } - const getValidatedRedirectUri = (req, res, client) => { + getValidatedRedirectUri (req, res, client) { + const self = this const redirectUris = [].concat(client.redirectUris) const redirectUri = req.method.toLowerCase() === 'get' ? req.query.redirect_uri : req.body.redirect_uri if (redirectUris.indexOf(redirectUri) === -1) { @@ -246,7 +429,9 @@ const initRoutes = (self, { accessTokenUrl = '/oauth/token', authorizeUrl = '/oa return redirectUri } - const route = (method, url, handler) => { + route (method, url, handler) { + const self = this; + const debugMiddleware = getDebugMiddleWare(self) const targetFn = self.app[method] if (self.debug) { targetFn.call(self.app, url, debugMiddleware) @@ -273,185 +458,6 @@ const initRoutes = (self, { accessTokenUrl = '/oauth/token', authorizeUrl = '/oa } })) } - - // STEP 1: VALIDATE CLIENT REQUEST - // Note from https://www.oauth.com/oauth2-servers/authorization/the-authorization-response/ - // If there is something wrong with the syntax of the request, such as the redirect_uri or client_id is invalid, - // then it’s important not to redirect the user and instead you should show the error message directly. - // This is to avoid letting your authorization server be used as an open redirector. - route('get', authorizeUrl, function (req, res, next) { - if (!validateParams(req.query, requiredAuthorizeGetParams, self.debug)) { - return errorHandler(res, { - status: 400, - error: 'invalid_request', - description: 'One or more request parameters are invalid', - state: req.query.state, - debug: self.debug - }) - } - - const validResponseType = validateResponseType(req, res) - if (!validResponseType) return - - const client = getValidatedClient(req, res) - if (!client) return - - const redirectUri = getValidatedRedirectUri(req, res, client) - if (!redirectUri) return - - return next() - }) - - // STEP 2: ADD USER TO THE REQUEST - // validate all inputs again, since all inputs - // could have been manipulated within form - route('post', authorizeUrl, function (req, res, next) { - if (!validateParams(req.body, requiredAuthorizePostParams, self.debug)) { - return errorHandler(res, { - error: 'invalid_request', - description: 'One or more request parameters are invalid', - state: req.body.state, - debug: self.debug, - status: 400 - }) - } - - const client = getValidatedClient(req, res) - if (!client) return - - const validRedirectUri = getValidatedRedirectUri(req, res, client) - if (!validRedirectUri) return - - // token refers here to the Meteor.loginToken, - // which is assigned, once the user has been validly logged-in - // only valid tokens can be used to find a user - // in the Meteor.users collection - const user = Meteor.users.findOne({ - 'services.resume.loginTokens.hashedToken': Accounts._hashLoginToken(req.body.token) - }) - - // we fail already here if no user has been found - // since the oauth-node sever would repsond with a - // 503 error, while it should be a 400 - const validateUserCredentials = { user, client } - - if (!user || !UserValidation.isValid(self, validateUserCredentials)) { - return errorHandler(res, { - status: 400, - error: 'access_denied', - description: 'You are no valid user', - state: req.body.state, - debug: self.debug - }) - } - - const id = user._id - req.user = { id } // TODO add fields from scope - - if (req.body.allowed === 'false') { - Meteor.users.update(id, { $pull: { 'oauth.authorizedClients': client.clientId } }) - } - else { - Meteor.users.update(id, { $addToSet: { 'oauth.authorizedClients': client.clientId } }) - } - - // make this work on a post route - req.query.allowed = req.body.allowed - - return next() - }) - - // STEP 3: GENERATE AUTHORIZATION CODE RESPONSE - // - use the user form the prior middleware for the authentication handler - // - on allow, assign the client_id to the user's authorized clients - // - on deny, ...? - // - construct the redirect query and redirect to the redirect_uri - route('post', authorizeUrl, function (req, res /*, next */) { - const request = new Request(req) - const response = new Response(res) - const authorizeOptions = { - authenticateHandler: { - handle: function (request, response) { - return request.user - } - } - } - - return self.oauth.authorize(request, response, authorizeOptions) - .then(bind(function (code) { - const query = new URLSearchParams({ - code: code.authorizationCode, - user: req.user.id, - state: req.body.state - }) - - const finalRedirectUri = `${req.body.redirect_uri}?${query}` - - res.statusCode = 302 - res.setHeader('Location', finalRedirectUri) - res.end() - })) - .catch(function (err) { - errorHandler(res, { - originalError: err, - error: err.name, - description: err.message, - status: err.statusCode, - state: req.body.state, - debug: self.debug - }) - }) - }) - - // STEP 4: GENERATE ACCESS TOKEN RESPONSE - // - validate params - // - validate authorization code - // - issue accessToken and refreshToken - route('post', accessTokenUrl, function (req, res, next) { - if (!validateParams(req.body, requiredAccessTokenPostParams, self.debug)) { - return errorHandler(res, { - status: 400, - error: 'invalid_request', - description: 'One or more request parameters are invalid', - state: req.body.state, - debug: self.debug - }) - } - - const request = new Request(req) - const response = new Response(res) - - return self.oauth.token(request, response) - .then(function (token) { - res.writeHead(200, { - 'Content-Type': 'application/json', - 'Cache-Control': 'no-store', - Pragma: 'no-cache' - }) - const body = JSON.stringify({ - access_token: token.accessToken, - token_type: 'bearer', - expires_in: token.accessTokenExpiresAt, - refresh_token: token.refreshToken - }) - res.end(body) - }) - .catch(function (err) { - return errorHandler(res, { - error: 'unauthorized_client', - description: err.message, - state: req.body.state, - debug: self.debug, - status: err.statusCode - }) - }) - }) - - route('use', fallbackUrl, function (req, res, next) { - return errorHandler(res, { - error: 'route not found', - status: 404, - debug: self.debug - }) - }) } + +export { requiredAuthorizeGetParams, requiredAuthorizePostParams, requiredRefreshTokenPostParams, requiredAccessTokenPostParams, UserValidation, errorHandler } \ No newline at end of file diff --git a/lib/webapp.js b/lib/webapp.js index dd9c70b..84fad01 100644 --- a/lib/webapp.js +++ b/lib/webapp.js @@ -6,7 +6,8 @@ import bodyParser from 'body-parser' * @private */ const server = WebApp.connectHandlers -server.use(bodyParser.urlencoded({ extended: false })) +server.use(bodyParser.urlencoded({ extended: true })) +server.use(bodyParser.json()) /** * Wrapped `WebApp` with express-style get/post and default use routes. diff --git a/package.js b/package.js index ee56d44..1618cf1 100644 --- a/package.js +++ b/package.js @@ -1,6 +1,6 @@ /* eslint-env meteor */ Package.describe({ - name: 'leaonline:oauth2-server', + name: 'ravitmg:oauth2-server', version: '4.2.1', summary: 'Node OAuth2 Server (v4) with Meteor bindings', git: 'https://github.com/leaonline/oauth2-server.git' From b4ec71d979aed51541aec7fa0bce0c027080b06f Mon Sep 17 00:00:00 2001 From: ravitmg Date: Fri, 19 Aug 2022 11:36:52 +0545 Subject: [PATCH 02/12] Add required functions for refresh_token grant --- lib/model/meteor-model.js | 4 ++++ lib/model/model.js | 14 +++++++++++++- lib/oauth.js | 5 ++++- lib/validation/requiredRefreshTokenPostParams.js | 10 ++++++++++ 4 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 lib/validation/requiredRefreshTokenPostParams.js diff --git a/lib/model/meteor-model.js b/lib/model/meteor-model.js index 6b8b334..5be8faa 100644 --- a/lib/model/meteor-model.js +++ b/lib/model/meteor-model.js @@ -141,3 +141,7 @@ export const saveRefreshToken = bind(function (token, clientId, expires, user) { export const getRefreshToken = bind(function (refreshToken) { return collections.RefreshTokens.findOne({ refreshToken }) }) + +export const revokeToken = bind(function (token) { + return collections.RefreshTokens.remove({ refreshToken: token }) +}) diff --git a/lib/model/model.js b/lib/model/model.js index 55b99e1..fd9d125 100644 --- a/lib/model/model.js +++ b/lib/model/model.js @@ -10,7 +10,8 @@ import { saveAuthorizationCode, saveRefreshToken, saveToken, - getAccessToken + getAccessToken, + revokeToken } from './meteor-model' /** @@ -164,6 +165,17 @@ class OAuthMeteorModel { return getRefreshToken(refreshToken) } + async revokeToken (token) { + /* Delete the token from the database */ + this.log('revokeToken (token:', token + ')') + + if (!token || token === 'undefined') { + return false; + } + + return revokeToken(token) + } + /** * * @param clientId diff --git a/lib/oauth.js b/lib/oauth.js index 1cb4f07..9794f52 100644 --- a/lib/oauth.js +++ b/lib/oauth.js @@ -28,6 +28,7 @@ import { secureHandler } from './middleware/secureHandler' // oauth import OAuthserver from '@node-oauth/oauth2-server' +import { requiredRefreshTokenPostParams } from './validation/requiredRefreshTokenPostParams' const { Request, Response } = OAuthserver @@ -313,7 +314,9 @@ export class OAuth2Server { // - validate authorization code // - issue accessToken and refreshToken self.route('post', accessTokenUrl, function (req, res, next) { - if (!validateParams(req.body, requiredAccessTokenPostParams, self.debug)) { + if (!validateParams(req.body, requiredAccessTokenPostParams, self.debug) + && !validateParams(req.body, requiredRefreshTokenPostParams, self.debug) + ) { return errorHandler(res, { status: 400, error: 'invalid_request', diff --git a/lib/validation/requiredRefreshTokenPostParams.js b/lib/validation/requiredRefreshTokenPostParams.js new file mode 100644 index 0000000..54231fe --- /dev/null +++ b/lib/validation/requiredRefreshTokenPostParams.js @@ -0,0 +1,10 @@ + +import { Match } from 'meteor/check' +import { nonEmptyString } from './nonEmptyString' + +const isNonEmptyString = Match.Where(nonEmptyString) + +export const requiredRefreshTokenPostParams = { + refresh_token: isNonEmptyString, + grant_type: 'refresh_token' +} From c34617a929ea8ae50e76ba6abbec5a10ec27c31d Mon Sep 17 00:00:00 2001 From: ravitmg Date: Fri, 19 Aug 2022 12:03:05 +0545 Subject: [PATCH 03/12] update package name and github link --- .versions | 4 ++-- package.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.versions b/.versions index 803665b..8be72eb 100644 --- a/.versions +++ b/.versions @@ -30,11 +30,11 @@ http@1.4.4 id-map@1.1.1 inter-process-messaging@0.1.1 jkuester:http@2.1.0 -ravitmg:oauth2-server@4.2.1 +tmgrawv:oauth2-server@4.2.1 lmieulet:meteor-coverage@3.2.0 lmieulet:meteor-legacy-coverage@0.1.0 lmieulet:meteor-packages-coverage@0.1.0 -local-test:leaonline:oauth2-server@4.2.1 +local-test:tmgrawv:oauth2-server@4.2.1 localstorage@1.2.0 logging@1.3.1 meteor@1.10.0 diff --git a/package.js b/package.js index 1618cf1..7524d17 100644 --- a/package.js +++ b/package.js @@ -1,9 +1,9 @@ /* eslint-env meteor */ Package.describe({ - name: 'ravitmg:oauth2-server', + name: 'tmgrawv:oauth2-server', version: '4.2.1', summary: 'Node OAuth2 Server (v4) with Meteor bindings', - git: 'https://github.com/leaonline/oauth2-server.git' + git: 'https://github.com/ravitmg/oauth2-server.git' }) Package.onUse(function (api) { From 31c502f3d3e98d85c82801cdaa8ca4efa4202836 Mon Sep 17 00:00:00 2001 From: ravitmg Date: Mon, 22 Aug 2022 15:34:56 +0545 Subject: [PATCH 04/12] Fix authenticated post method fix --- lib/oauth.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/oauth.js b/lib/oauth.js index 9794f52..8ef2414 100644 --- a/lib/oauth.js +++ b/lib/oauth.js @@ -174,7 +174,9 @@ export class OAuth2Server { app.get(route, secureHandler(self, fn)) }, post (route, fn) { - return app.post(route, debugMiddleware, authHandler, fn) + app.post(route, debugMiddleware) + app.post(route, authHandler) + app.post(route, secureHandler(self, fn)) } } } From f32fbb677da9174960156bda700cb83c7cc65c5f Mon Sep 17 00:00:00 2001 From: ravitmg Date: Mon, 22 Aug 2022 15:37:39 +0545 Subject: [PATCH 05/12] Refresh token model fix --- lib/model/meteor-model.js | 10 +++++----- lib/model/model.js | 20 ++++++-------------- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/lib/model/meteor-model.js b/lib/model/meteor-model.js index 5be8faa..c52c016 100644 --- a/lib/model/meteor-model.js +++ b/lib/model/meteor-model.js @@ -126,12 +126,12 @@ export const revokeAuthorizationCode = bind(function revokeAuthorizationCode ({ * @private used by OAuthMeteorModel.prototype.saveRefreshToken */ -export const saveRefreshToken = bind(function (token, clientId, expires, user) { +export const saveRefreshToken = bind(function (tokenDoc, clientDoc, userDoc) { return collections.RefreshTokens.insert({ - refreshToken: token, - clientId, - userId: user.id, - expires + refreshToken: tokenDoc.refreshToken, + expires: tokenDoc.refreshTokenExpiresAt, + clientId: clientDoc.clientId, + userId: userDoc.id }) }) diff --git a/lib/model/model.js b/lib/model/model.js index fd9d125..00cd4a1 100644 --- a/lib/model/model.js +++ b/lib/model/model.js @@ -104,7 +104,12 @@ class OAuthMeteorModel { this.log('with token ', tokenDoc) this.log('with client ', clientDoc) this.log('with user ', userDoc) - return saveToken(tokenDoc, clientDoc, userDoc) + const accessTokenToc = await saveToken(tokenDoc, clientDoc, userDoc) + if (!clientDoc.grants.includes('refresh_token')) return accessTokenToc + + this.log('saveRefreshToken:') + const refreshTokenDoc = await saveRefreshToken(tokenDoc, clientDoc, userDoc) + return Object.assign({}, accessTokenToc, refreshTokenDoc) } /** @@ -139,19 +144,6 @@ class OAuthMeteorModel { return revokeAuthorizationCode(code) } - /** - * - * @param token - * @param clientId - * @param expires - * @param user - * @return {Promise<*>} - */ - async saveRefreshToken (token, clientId, expires, user) { - this.log('saveRefreshToken (token:', token, ', clientId:', clientId, ', user:', user, ', expires:', expires, ')') - return saveRefreshToken(token, clientId, expires, user) - } - /** getRefreshToken(token) should return an object with: refreshToken (String) From fcee76ddb0a76c175799446a0c4431038fe17fa8 Mon Sep 17 00:00:00 2001 From: ravitmg Date: Thu, 25 Aug 2022 15:29:21 +0545 Subject: [PATCH 06/12] modify model for refresh_token workflow --- README.md | 3 +-- lib/model/DefaultModelConfig.js | 10 ++++++++-- lib/model/meteor-model.js | 21 +++++---------------- lib/model/model.js | 11 ++++------- 4 files changed, 18 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index a09e8a1..501af6f 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ This package is a implementation of the package [@node-oauth/oauth2-server](https://github.com/node-oauth/node-oauth2-server) for Meteor. It can run without `express` (we use Meteor's builtin `WebApp`) and implements -the `authorization_code` workflow and works like the Facebook's OAuth popup. +the `authorization_code` and `refresh_token` workflow and works like the Facebook's OAuth popup. ## Changelog @@ -58,7 +58,6 @@ const oauth2server = new OAuth2Server({ }, model: { accessTokensCollectionName: 'oauth_access_tokens', - refreshTokensCollectionName: 'oauth_refresh_tokens', clientsCollectionName: 'oauth_clients', authCodesCollectionName: 'oauth_auth_codes', debug: true diff --git a/lib/model/DefaultModelConfig.js b/lib/model/DefaultModelConfig.js index d0763d0..ae8325c 100644 --- a/lib/model/DefaultModelConfig.js +++ b/lib/model/DefaultModelConfig.js @@ -1,10 +1,16 @@ /** * Default collection names for the model collections. - * @type {{accessTokensCollectionName: string, refreshTokensCollectionName: string, clientsCollectionName: string, authCodesCollectionName: string, debug: boolean}} + * @type { + * { + * accessTokensCollectionName: string, + * clientsCollectionName: string, + * authCodesCollectionName: string, + * debug: boolean + * } + * } */ export const DefaultModelConfig = { accessTokensCollectionName: 'oauth_access_tokens', - refreshTokensCollectionName: 'oauth_refresh_tokens', clientsCollectionName: 'oauth_clients', authCodesCollectionName: 'oauth_auth_codes', debug: false diff --git a/lib/model/meteor-model.js b/lib/model/meteor-model.js index c52c016..3013ce3 100644 --- a/lib/model/meteor-model.js +++ b/lib/model/meteor-model.js @@ -3,7 +3,6 @@ import { bind } from '../utils/bind' export const collections = { AccessTokens: undefined, - RefreshTokens: undefined, Clients: undefined, AuthCodes: undefined } @@ -122,26 +121,16 @@ export const revokeAuthorizationCode = bind(function revokeAuthorizationCode ({ return collections.AuthCodes.remove({ authorizationCode }) === docCount }) -/** - * @private used by OAuthMeteorModel.prototype.saveRefreshToken - */ - -export const saveRefreshToken = bind(function (tokenDoc, clientDoc, userDoc) { - return collections.RefreshTokens.insert({ - refreshToken: tokenDoc.refreshToken, - expires: tokenDoc.refreshTokenExpiresAt, - clientId: clientDoc.clientId, - userId: userDoc.id - }) -}) - /** * @private used by OAuthMeteorModel.prototype.getRefreshToken */ export const getRefreshToken = bind(function (refreshToken) { - return collections.RefreshTokens.findOne({ refreshToken }) + return collections.AccessTokens.findOne({ refreshToken }) }) +/** + * @private used by OauthMeteorModel.protoptype.revokeToken + */ export const revokeToken = bind(function (token) { - return collections.RefreshTokens.remove({ refreshToken: token }) + return collections.AccessTokens.update({ refreshToken: token.refreshToken }, { $unset: { refreshToken: null, refreshTokenExpiresAt: null } }) }) diff --git a/lib/model/model.js b/lib/model/model.js index 00cd4a1..b2d504c 100644 --- a/lib/model/model.js +++ b/lib/model/model.js @@ -24,7 +24,6 @@ class OAuthMeteorModel { this.debug = modelConfig.debug collections.AccessTokens = createCollection(modelConfig.accessTokensCollection, modelConfig.accessTokensCollectionName) - collections.RefreshTokens = createCollection(modelConfig.refreshTokensCollection, modelConfig.refreshTokensCollectionName) collections.AuthCodes = createCollection(modelConfig.authCodesCollection, modelConfig.authCodesCollectionName) collections.Clients = createCollection(modelConfig.clientsCollection, modelConfig.clientsCollectionName) } @@ -104,12 +103,7 @@ class OAuthMeteorModel { this.log('with token ', tokenDoc) this.log('with client ', clientDoc) this.log('with user ', userDoc) - const accessTokenToc = await saveToken(tokenDoc, clientDoc, userDoc) - if (!clientDoc.grants.includes('refresh_token')) return accessTokenToc - - this.log('saveRefreshToken:') - const refreshTokenDoc = await saveRefreshToken(tokenDoc, clientDoc, userDoc) - return Object.assign({}, accessTokenToc, refreshTokenDoc) + return saveToken(tokenDoc, clientDoc, userDoc) } /** @@ -157,6 +151,9 @@ class OAuthMeteorModel { return getRefreshToken(refreshToken) } + /** + * revokeToken (token) is required for refresh_token grant type and should return true + */ async revokeToken (token) { /* Delete the token from the database */ this.log('revokeToken (token:', token + ')') From 14903ed84455851b572758f74df0ced16e44527e Mon Sep 17 00:00:00 2001 From: ravitmg Date: Fri, 26 Aug 2022 18:50:36 +0545 Subject: [PATCH 07/12] fix validatereqparams and modelinterface --- lib/oauth.js | 6 +++--- lib/utils/isModelInterface.js | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/oauth.js b/lib/oauth.js index 8ef2414..2932809 100644 --- a/lib/oauth.js +++ b/lib/oauth.js @@ -190,7 +190,7 @@ export class OAuth2Server { const self = this const { authorizeUrl } = this.config.routes self.route('get', authorizeUrl, function (req, res, next) { - const validRequestParams = self.validateRequestParams(req, requiredAuthorizeGetParams) + const validRequestParams = self.validateRequestParams(req, res, requiredAuthorizeGetParams) if (!validRequestParams) return; const validResponseType = self.validateResponseType(req, res) @@ -213,7 +213,7 @@ export class OAuth2Server { const self = this const { authorizeUrl } = this.config.routes self.route('post', authorizeUrl, function (req, res, next) { - const validRequestParams = self.validateRequestParams(req, requiredAuthorizeGetParams) + const validRequestParams = self.validateRequestParams(req, res, requiredAuthorizeGetParams) if (!validRequestParams) return; const client = self.getValidatedClient(req, res) @@ -386,7 +386,7 @@ export class OAuth2Server { return true } - validateRequestParams (req, expectedParams) { + validateRequestParams (req, res, expectedParams) { const self = this if (!validateParams(req.query, expectedParams, self.debug)) { return errorHandler(res, { diff --git a/lib/utils/isModelInterface.js b/lib/utils/isModelInterface.js index 4db2a16..b10230e 100644 --- a/lib/utils/isModelInterface.js +++ b/lib/utils/isModelInterface.js @@ -8,7 +8,6 @@ const modelNames = [ 'getRefreshToken', 'revokeAuthorizationCode', 'saveAuthorizationCode', - 'saveRefreshToken', 'saveToken', 'getAccessToken' ] @@ -32,7 +31,8 @@ const modelNames = [ * @return {boolean} true if valid, otherwise false */ export const isModelInterface = model => { - return model && Object.keys(model).some(property => { - return modelNames.includes(property) && typeof model[property] === 'function' - }) + if (!model) return false; + const checkProperties = object => Object.getOwnPropertyNames(object).some(property => modelNames.includes(property) && typeof object[property] === 'function'); + if (model.constructor && model.constructor.name) return checkProperties(model.__proto__) + return checkProperties(model) } From 0016ad812733c1ec4698be76be0597b152101331 Mon Sep 17 00:00:00 2001 From: ravitmg Date: Wed, 14 Sep 2022 16:13:17 +0545 Subject: [PATCH 08/12] minor changes --- lib/middleware/getDebugMiddleware.js | 2 +- lib/oauth.js | 5 +++-- lib/utils/error.js | 2 +- lib/webapp.js | 1 - 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/middleware/getDebugMiddleware.js b/lib/middleware/getDebugMiddleware.js index b1f70ae..14883fc 100644 --- a/lib/middleware/getDebugMiddleware.js +++ b/lib/middleware/getDebugMiddleware.js @@ -9,7 +9,7 @@ import { debug } from '../utils/console' export const getDebugMiddleWare = instance => (req, res, next) => { if (instance.debug === true) { const baseUrl = req.originalUrl.split('?')[0] - debug(req.method, baseUrl, req.query || req.body) + debug(req.method, baseUrl, (req.query && Object.keys(req.query).length !== 0) ? req.query : req.body) } return next() } diff --git a/lib/oauth.js b/lib/oauth.js index 2932809..5d74291 100644 --- a/lib/oauth.js +++ b/lib/oauth.js @@ -36,6 +36,7 @@ const { Request, Response } = OAuthserver * The base class of this package. * Represents an oauth2-server with a default model setup for Meteor/Mongo. */ + export class OAuth2Server { /** * Creates a new OAuth2 server instance. @@ -374,7 +375,7 @@ export class OAuth2Server { const responseType = req.method.toLowerCase() === 'get' ? req.query.response_type : req.body.response_type - if (responseType !== 'code' && responseType !== 'token') { + if (responseType !== 'code') { return errorHandler(res, { status: 415, error: 'unsupported_response_type', @@ -465,4 +466,4 @@ export class OAuth2Server { } } -export { requiredAuthorizeGetParams, requiredAuthorizePostParams, requiredRefreshTokenPostParams, requiredAccessTokenPostParams, UserValidation, errorHandler } \ No newline at end of file +export { requiredAuthorizeGetParams, requiredAuthorizePostParams, requiredRefreshTokenPostParams, requiredAccessTokenPostParams, UserValidation, errorHandler, validateParams } \ No newline at end of file diff --git a/lib/utils/error.js b/lib/utils/error.js index dd5d6da..40583da 100644 --- a/lib/utils/error.js +++ b/lib/utils/error.js @@ -20,7 +20,7 @@ export const errorHandler = function (res, options) { res.writeHead(errCode, { 'Content-Type': 'application/json' }) // by default we log the error that will be used as response - error(`[error] ${errCode} - ${options.error} - ${options.description}`) + options.debug && error(`[error] ${errCode} - ${options.error} - ${options.description}`) if (options.debug && options.originalError) { error('[original error]:') diff --git a/lib/webapp.js b/lib/webapp.js index 84fad01..f79deee 100644 --- a/lib/webapp.js +++ b/lib/webapp.js @@ -44,7 +44,6 @@ const app = { if (req.headers['content-type'] !== 'application/x-www-form-urlencoded') { // Transforms requests which are POST and aren't "x-www-form-urlencoded" content type // and they pass the required information as query strings - info('Transforming a request to form-urlencoded with the query going to the body.') req.headers['content-type'] = 'application/x-www-form-urlencoded' req.body = Object.assign({}, req.body, req.query) } From 68d1db0baaae66587d331b61e809d441aee3be12 Mon Sep 17 00:00:00 2001 From: ravitmg Date: Thu, 15 Sep 2022 15:10:47 +0545 Subject: [PATCH 09/12] Fixes for grant_type refresh token --- lib/oauth.js | 49 +++++++++++++------ .../requiredRefreshTokenPostParams.js | 4 +- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/lib/oauth.js b/lib/oauth.js index 5d74291..f201cbc 100644 --- a/lib/oauth.js +++ b/lib/oauth.js @@ -36,7 +36,6 @@ const { Request, Response } = OAuthserver * The base class of this package. * Represents an oauth2-server with a default model setup for Meteor/Mongo. */ - export class OAuth2Server { /** * Creates a new OAuth2 server instance. @@ -317,18 +316,26 @@ export class OAuth2Server { // - validate authorization code // - issue accessToken and refreshToken self.route('post', accessTokenUrl, function (req, res, next) { - if (!validateParams(req.body, requiredAccessTokenPostParams, self.debug) - && !validateParams(req.body, requiredRefreshTokenPostParams, self.debug) - ) { - return errorHandler(res, { - status: 400, - error: 'invalid_request', - description: 'One or more request parameters are invalid', - state: req.body.state, - debug: self.debug - }) + if (![ 'authorization_code', 'refresh_token' ].includes(req.body.grant_type)) { + return errorHandler(res, { + status: 400, + error: 'invalid_grant', + description: 'The grant_type is not supported', + state: _req.body.state, + debug: self.debug, + }) } + if (req.body.grant_type === 'authorization_code') { + const validParams = self.validateRequestParams(req, res, requiredAccessTokenPostParams) + if (!validParams) return + } + + if (req.body.grant_type === 'refresh_token') { + const validParams = self.validateRequestParams(req, res, requiredRefreshTokenPostParams) + if (!validParams) return + } + const request = new Request(req) const response = new Response(res) return self.oauth.token(request, response) @@ -348,7 +355,7 @@ export class OAuth2Server { }) .catch(function (err) { return errorHandler(res, { - error: 'unauthorized_client', + error: err.name, description: err.message, state: req.body.state, debug: self.debug, @@ -389,7 +396,9 @@ export class OAuth2Server { validateRequestParams (req, res, expectedParams) { const self = this - if (!validateParams(req.query, expectedParams, self.debug)) { + const params = req.method.toLowerCase() === 'get' ? req.query : req.body + + if (!validateParams(params, expectedParams, self.debug)) { return errorHandler(res, { status: 400, error: 'invalid_request', @@ -465,5 +474,15 @@ export class OAuth2Server { })) } } - -export { requiredAuthorizeGetParams, requiredAuthorizePostParams, requiredRefreshTokenPostParams, requiredAccessTokenPostParams, UserValidation, errorHandler, validateParams } \ No newline at end of file +const validation = { + requiredAuthorizeGetParams, + requiredAuthorizePostParams, + requiredRefreshTokenPostParams, + requiredAccessTokenPostParams, + UserValidation +} +export { + validation, + errorHandler, + validateParams +} \ No newline at end of file diff --git a/lib/validation/requiredRefreshTokenPostParams.js b/lib/validation/requiredRefreshTokenPostParams.js index 54231fe..cfe5acd 100644 --- a/lib/validation/requiredRefreshTokenPostParams.js +++ b/lib/validation/requiredRefreshTokenPostParams.js @@ -6,5 +6,7 @@ const isNonEmptyString = Match.Where(nonEmptyString) export const requiredRefreshTokenPostParams = { refresh_token: isNonEmptyString, - grant_type: 'refresh_token' + client_id: isNonEmptyString, + client_secret: isNonEmptyString, + grant_type: 'refresh_token', } From 58473f1131e554a40b1fba31225368b1320f2dea Mon Sep 17 00:00:00 2001 From: ravitmg Date: Fri, 30 Sep 2022 13:30:02 +0545 Subject: [PATCH 10/12] remove unnecessary changes --- README.md | 3 ++- lib/middleware/getDebugMiddleware.js | 2 +- lib/model/DefaultModelConfig.js | 12 +++-------- lib/model/meteor-model.js | 19 +++++++++++------ lib/model/model.js | 31 ++++++++++++++-------------- lib/oauth.js | 4 ++-- lib/utils/isModelInterface.js | 8 +++---- 7 files changed, 40 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 501af6f..a09e8a1 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ This package is a implementation of the package [@node-oauth/oauth2-server](https://github.com/node-oauth/node-oauth2-server) for Meteor. It can run without `express` (we use Meteor's builtin `WebApp`) and implements -the `authorization_code` and `refresh_token` workflow and works like the Facebook's OAuth popup. +the `authorization_code` workflow and works like the Facebook's OAuth popup. ## Changelog @@ -58,6 +58,7 @@ const oauth2server = new OAuth2Server({ }, model: { accessTokensCollectionName: 'oauth_access_tokens', + refreshTokensCollectionName: 'oauth_refresh_tokens', clientsCollectionName: 'oauth_clients', authCodesCollectionName: 'oauth_auth_codes', debug: true diff --git a/lib/middleware/getDebugMiddleware.js b/lib/middleware/getDebugMiddleware.js index 14883fc..ba090f0 100644 --- a/lib/middleware/getDebugMiddleware.js +++ b/lib/middleware/getDebugMiddleware.js @@ -9,7 +9,7 @@ import { debug } from '../utils/console' export const getDebugMiddleWare = instance => (req, res, next) => { if (instance.debug === true) { const baseUrl = req.originalUrl.split('?')[0] - debug(req.method, baseUrl, (req.query && Object.keys(req.query).length !== 0) ? req.query : req.body) + debug(req.method, baseUrl, { query: req.query, body: req.body }) } return next() } diff --git a/lib/model/DefaultModelConfig.js b/lib/model/DefaultModelConfig.js index ae8325c..08ed87a 100644 --- a/lib/model/DefaultModelConfig.js +++ b/lib/model/DefaultModelConfig.js @@ -1,16 +1,10 @@ /** * Default collection names for the model collections. - * @type { - * { - * accessTokensCollectionName: string, - * clientsCollectionName: string, - * authCodesCollectionName: string, - * debug: boolean - * } - * } - */ + * @type {{accessTokensCollectionName: string, refreshTokensCollectionName: string, clientsCollectionName: string, authCodesCollectionName: string, debug: boolean}} +*/ export const DefaultModelConfig = { accessTokensCollectionName: 'oauth_access_tokens', + refreshTokensCollectionName: 'oauth_refresh_tokens', clientsCollectionName: 'oauth_clients', authCodesCollectionName: 'oauth_auth_codes', debug: false diff --git a/lib/model/meteor-model.js b/lib/model/meteor-model.js index 3013ce3..6b8b334 100644 --- a/lib/model/meteor-model.js +++ b/lib/model/meteor-model.js @@ -3,6 +3,7 @@ import { bind } from '../utils/bind' export const collections = { AccessTokens: undefined, + RefreshTokens: undefined, Clients: undefined, AuthCodes: undefined } @@ -122,15 +123,21 @@ export const revokeAuthorizationCode = bind(function revokeAuthorizationCode ({ }) /** - * @private used by OAuthMeteorModel.prototype.getRefreshToken + * @private used by OAuthMeteorModel.prototype.saveRefreshToken */ -export const getRefreshToken = bind(function (refreshToken) { - return collections.AccessTokens.findOne({ refreshToken }) + +export const saveRefreshToken = bind(function (token, clientId, expires, user) { + return collections.RefreshTokens.insert({ + refreshToken: token, + clientId, + userId: user.id, + expires + }) }) /** - * @private used by OauthMeteorModel.protoptype.revokeToken + * @private used by OAuthMeteorModel.prototype.getRefreshToken */ -export const revokeToken = bind(function (token) { - return collections.AccessTokens.update({ refreshToken: token.refreshToken }, { $unset: { refreshToken: null, refreshTokenExpiresAt: null } }) +export const getRefreshToken = bind(function (refreshToken) { + return collections.RefreshTokens.findOne({ refreshToken }) }) diff --git a/lib/model/model.js b/lib/model/model.js index b2d504c..55b99e1 100644 --- a/lib/model/model.js +++ b/lib/model/model.js @@ -10,8 +10,7 @@ import { saveAuthorizationCode, saveRefreshToken, saveToken, - getAccessToken, - revokeToken + getAccessToken } from './meteor-model' /** @@ -24,6 +23,7 @@ class OAuthMeteorModel { this.debug = modelConfig.debug collections.AccessTokens = createCollection(modelConfig.accessTokensCollection, modelConfig.accessTokensCollectionName) + collections.RefreshTokens = createCollection(modelConfig.refreshTokensCollection, modelConfig.refreshTokensCollectionName) collections.AuthCodes = createCollection(modelConfig.authCodesCollection, modelConfig.authCodesCollectionName) collections.Clients = createCollection(modelConfig.clientsCollection, modelConfig.clientsCollectionName) } @@ -138,6 +138,19 @@ class OAuthMeteorModel { return revokeAuthorizationCode(code) } + /** + * + * @param token + * @param clientId + * @param expires + * @param user + * @return {Promise<*>} + */ + async saveRefreshToken (token, clientId, expires, user) { + this.log('saveRefreshToken (token:', token, ', clientId:', clientId, ', user:', user, ', expires:', expires, ')') + return saveRefreshToken(token, clientId, expires, user) + } + /** getRefreshToken(token) should return an object with: refreshToken (String) @@ -151,20 +164,6 @@ class OAuthMeteorModel { return getRefreshToken(refreshToken) } - /** - * revokeToken (token) is required for refresh_token grant type and should return true - */ - async revokeToken (token) { - /* Delete the token from the database */ - this.log('revokeToken (token:', token + ')') - - if (!token || token === 'undefined') { - return false; - } - - return revokeToken(token) - } - /** * * @param clientId diff --git a/lib/oauth.js b/lib/oauth.js index f201cbc..f194271 100644 --- a/lib/oauth.js +++ b/lib/oauth.js @@ -45,7 +45,7 @@ export class OAuth2Server { * @param debug * @return {OAuth2Server} */ - constructor ({ serverOptions = {}, model, routes, debug } = {}) { + constructor ({ serverOptions = {}, model, routes, debug, checkModelInterface = true } = {}) { check(serverOptions, OptionsSchema.serverOptions) this.instanceId = Random.id() @@ -55,7 +55,7 @@ export class OAuth2Server { } // if we have passed our own model instance we directly assign it as model, - if (isModelInterface(model)) { + if (!checkModelInterface || isModelInterface(model)) { this.config.model = null this.model = model } diff --git a/lib/utils/isModelInterface.js b/lib/utils/isModelInterface.js index b10230e..4db2a16 100644 --- a/lib/utils/isModelInterface.js +++ b/lib/utils/isModelInterface.js @@ -8,6 +8,7 @@ const modelNames = [ 'getRefreshToken', 'revokeAuthorizationCode', 'saveAuthorizationCode', + 'saveRefreshToken', 'saveToken', 'getAccessToken' ] @@ -31,8 +32,7 @@ const modelNames = [ * @return {boolean} true if valid, otherwise false */ export const isModelInterface = model => { - if (!model) return false; - const checkProperties = object => Object.getOwnPropertyNames(object).some(property => modelNames.includes(property) && typeof object[property] === 'function'); - if (model.constructor && model.constructor.name) return checkProperties(model.__proto__) - return checkProperties(model) + return model && Object.keys(model).some(property => { + return modelNames.includes(property) && typeof model[property] === 'function' + }) } From 2f7184399fa562314ed449658a4882a8e3db2cbb Mon Sep 17 00:00:00 2001 From: ravitmg Date: Fri, 30 Sep 2022 13:50:08 +0545 Subject: [PATCH 11/12] revert package name --- .versions | 4 ++-- package.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.versions b/.versions index 8be72eb..85b1308 100644 --- a/.versions +++ b/.versions @@ -30,11 +30,11 @@ http@1.4.4 id-map@1.1.1 inter-process-messaging@0.1.1 jkuester:http@2.1.0 -tmgrawv:oauth2-server@4.2.1 +leaonline:oauth2-server@4.2.1 lmieulet:meteor-coverage@3.2.0 lmieulet:meteor-legacy-coverage@0.1.0 lmieulet:meteor-packages-coverage@0.1.0 -local-test:tmgrawv:oauth2-server@4.2.1 +local-test:leaonline:oauth2-server@4.2.1 localstorage@1.2.0 logging@1.3.1 meteor@1.10.0 diff --git a/package.js b/package.js index 7524d17..a360220 100644 --- a/package.js +++ b/package.js @@ -1,6 +1,6 @@ /* eslint-env meteor */ Package.describe({ - name: 'tmgrawv:oauth2-server', + name: 'leaonline:oauth2-server', version: '4.2.1', summary: 'Node OAuth2 Server (v4) with Meteor bindings', git: 'https://github.com/ravitmg/oauth2-server.git' From 5bbb9fafee8a47607b80e65d9472e7e097d807cb Mon Sep 17 00:00:00 2001 From: ravitmg Date: Thu, 3 Nov 2022 15:54:31 +0545 Subject: [PATCH 12/12] update to new version --- .versions | 20 ++++++++++---------- lib/oauth.js | 18 ++++++++++++++---- package.js | 4 ++-- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/.versions b/.versions index 85b1308..298743b 100644 --- a/.versions +++ b/.versions @@ -1,4 +1,4 @@ -accounts-base@2.2.3 +accounts-base@2.2.4 accounts-password@2.3.1 allow-deny@1.1.1 babel-compiler@7.9.0 @@ -12,10 +12,10 @@ check@1.3.1 coffeescript@1.0.17 dburles:mongo-collection-instances@0.1.3 ddp@1.4.0 -ddp-client@2.5.0 +ddp-client@2.6.0 ddp-common@1.4.0 ddp-rate-limiter@1.1.0 -ddp-server@2.5.0 +ddp-server@2.6.0 diff-sequence@1.1.1 dynamic-import@0.7.2 ecmascript@0.16.2 @@ -30,26 +30,25 @@ http@1.4.4 id-map@1.1.1 inter-process-messaging@0.1.1 jkuester:http@2.1.0 -leaonline:oauth2-server@4.2.1 lmieulet:meteor-coverage@3.2.0 lmieulet:meteor-legacy-coverage@0.1.0 lmieulet:meteor-packages-coverage@0.1.0 -local-test:leaonline:oauth2-server@4.2.1 +local-test:tmgrawv:oauth2-server@4.2.3 localstorage@1.2.0 logging@1.3.1 -meteor@1.10.0 +meteor@1.10.1 meteortesting:browser-tests@1.3.5 meteortesting:mocha@2.0.3 meteortesting:mocha-core@8.0.1 -minimongo@1.8.0 +minimongo@1.9.0 modern-browsers@0.1.8 -modules@0.18.0 +modules@0.19.0 modules-runtime@0.13.0 -mongo@1.15.0 +mongo@1.16.0 mongo-decimal@0.1.3 mongo-dev-server@1.1.0 mongo-id@1.0.8 -npm-mongo@4.3.1 +npm-mongo@4.9.0 ordered-dict@1.1.0 practicalmeteor:chai@1.9.2_3 promise@0.12.0 @@ -63,6 +62,7 @@ routepolicy@1.1.1 service-configuration@1.3.0 sha@1.0.9 socket-stream-client@0.5.0 +tmgrawv:oauth2-server@4.2.3 tracker@1.2.0 underscore@1.0.10 url@1.3.2 diff --git a/lib/oauth.js b/lib/oauth.js index f194271..cc0c5d7 100644 --- a/lib/oauth.js +++ b/lib/oauth.js @@ -479,10 +479,20 @@ const validation = { requiredAuthorizePostParams, requiredRefreshTokenPostParams, requiredAccessTokenPostParams, - UserValidation + UserValidation, + validateParams +} +const handlers = { + errorHandler +} + +const middlewares = { + secureHandler } + export { validation, - errorHandler, - validateParams -} \ No newline at end of file + handlers, + middlewares, + app +} \ No newline at end of file diff --git a/package.js b/package.js index a360220..c634ef3 100644 --- a/package.js +++ b/package.js @@ -1,7 +1,7 @@ /* eslint-env meteor */ Package.describe({ - name: 'leaonline:oauth2-server', - version: '4.2.1', + name: 'tmgrawv:oauth2-server', + version: '4.2.3', summary: 'Node OAuth2 Server (v4) with Meteor bindings', git: 'https://github.com/ravitmg/oauth2-server.git' })