From 42f731ec7c7f451bb7fde9650425d35e62294763 Mon Sep 17 00:00:00 2001 From: Jeff Flynt Date: Fri, 28 Jun 2024 19:45:10 -0500 Subject: [PATCH 1/4] Implement Refresh Token Flow --- lib/model/meteor-model.js | 21 +++++++++++++++++-- lib/model/model.js | 11 +++++++++- lib/oauth.js | 3 ++- lib/utils/isModelInterface.js | 4 +++- .../requiredRefreshTokenPostParams.js | 11 ++++++++++ 5 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 lib/validation/requiredRefreshTokenPostParams.js diff --git a/lib/model/meteor-model.js b/lib/model/meteor-model.js index 6b8b334..bb7ffbd 100644 --- a/lib/model/meteor-model.js +++ b/lib/model/meteor-model.js @@ -54,7 +54,15 @@ export const getClient = bind(function (clientId, secret) { clientId, secret: secret || undefined // secret can be undefined or null but should act as the same }) - return clientDoc || false + + if (clientDoc) { + return { + ...clientDoc, + id: clientDoc.clientId + } + } else { + return false + } }) /** @@ -139,5 +147,14 @@ export const saveRefreshToken = bind(function (token, clientId, expires, user) { * @private used by OAuthMeteorModel.prototype.getRefreshToken */ export const getRefreshToken = bind(function (refreshToken) { - return collections.RefreshTokens.findOne({ refreshToken }) + return collections.AccessTokens.findOne({ refreshToken }) +}) + +export const revokeToken = bind(function (token) { + const docCount = collections.AccessTokens.find({ refreshToken: token.refreshToken }).count(); + if (docCount === 0) { + return true; + } + + return collections.AccessTokens.remove({ refreshToken: token.refreshToken }) === docCount; }) diff --git a/lib/model/model.js b/lib/model/model.js index 55b99e1..083c842 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' /** @@ -174,6 +175,14 @@ class OAuthMeteorModel { this.log('grantTypeAllowed (clientId:', clientId, ', grantType:', grantType + ')') return ['authorization_code', 'refresh_token'].includes(grantType) } + + /** + * revokeToken(refreshToken) is required and should return true + */ + async revokeToken(refreshToken) { + this.log(`revokeToken (refreshToken: ${refreshToken})`) + return revokeToken(refreshToken) + } } export { OAuthMeteorModel } diff --git a/lib/oauth.js b/lib/oauth.js index 79e84b5..4f2f773 100644 --- a/lib/oauth.js +++ b/lib/oauth.js @@ -17,6 +17,7 @@ import { validateParams } from './validation/validateParams' import { requiredAuthorizeGetParams } from './validation/requiredAuthorizeGetParams' import { requiredAuthorizePostParams } from './validation/requiredAuthorizePostParams' import { requiredAccessTokenPostParams } from './validation/requiredAccessTokenPostParams' +import { requiredRefreshTokenPostParams } from "./validation/requiredRefreshTokenPostParams"; import { UserValidation } from './validation/UserValidation' import { OptionsSchema } from './validation/OptionsSchema' @@ -407,7 +408,7 @@ const initRoutes = (self, { accessTokenUrl = '/oauth/token', authorizeUrl = '/oa // - validate authorization code // - issue accessToken and refreshToken route('post', accessTokenUrl, async function (req, res /*, next */) { - if (!validateParams(req.body, requiredAccessTokenPostParams, self.debug)) { + if (!validateParams(req.body, req.body?.refresh_token ? requiredRefreshTokenPostParams : requiredAccessTokenPostParams, self.debug)) { return errorHandler(res, { status: 400, error: 'invalid_request', diff --git a/lib/utils/isModelInterface.js b/lib/utils/isModelInterface.js index 4db2a16..e963e2f 100644 --- a/lib/utils/isModelInterface.js +++ b/lib/utils/isModelInterface.js @@ -10,7 +10,8 @@ const modelNames = [ 'saveAuthorizationCode', 'saveRefreshToken', 'saveToken', - 'getAccessToken' + 'getAccessToken', + 'revokeToken' ] /** @@ -28,6 +29,7 @@ const modelNames = [ * - 'saveRefreshToken', * - 'saveToken', * - 'getAccessToken' + * - 'revokeToken' * @param model {Object} the model implementation * @return {boolean} true if valid, otherwise false */ diff --git a/lib/validation/requiredRefreshTokenPostParams.js b/lib/validation/requiredRefreshTokenPostParams.js new file mode 100644 index 0000000..e577f1a --- /dev/null +++ b/lib/validation/requiredRefreshTokenPostParams.js @@ -0,0 +1,11 @@ +import { Match } from "meteor/check"; +import { nonEmptyString } from "./nonEmptyString"; + +const isNonEmptyString = Match.Where(nonEmptyString); + +export const requiredRefreshTokenPostParams = { + grant_type: isNonEmptyString, + refresh_token: isNonEmptyString, + client_id: Match.Maybe(String), + client_secret: Match.Maybe(String), +}; From 47d14e31fb7d34f648d2daa33862194e800b26c1 Mon Sep 17 00:00:00 2001 From: Jeff Flynt Date: Tue, 2 Jul 2024 13:55:26 -0500 Subject: [PATCH 2/4] Fix linting issues --- lib/model/meteor-model.js | 8 ++++---- lib/model/model.js | 2 +- lib/oauth.js | 2 +- lib/validation/requiredRefreshTokenPostParams.js | 10 +++++----- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/model/meteor-model.js b/lib/model/meteor-model.js index bb7ffbd..fe5c2cc 100644 --- a/lib/model/meteor-model.js +++ b/lib/model/meteor-model.js @@ -109,7 +109,7 @@ export const saveAuthorizationCode = bind(function saveAuthCode (code, client, u redirectUri, scope: code.scope, client: { - id: client.client_id + id: client.clientId }, user: { id: user.id @@ -151,10 +151,10 @@ export const getRefreshToken = bind(function (refreshToken) { }) export const revokeToken = bind(function (token) { - const docCount = collections.AccessTokens.find({ refreshToken: token.refreshToken }).count(); + const docCount = collections.AccessTokens.find({ refreshToken: token.refreshToken }).count() if (docCount === 0) { - return true; + return true } - return collections.AccessTokens.remove({ refreshToken: token.refreshToken }) === docCount; + return collections.AccessTokens.remove({ refreshToken: token.refreshToken }) === docCount }) diff --git a/lib/model/model.js b/lib/model/model.js index 083c842..8231e22 100644 --- a/lib/model/model.js +++ b/lib/model/model.js @@ -179,7 +179,7 @@ class OAuthMeteorModel { /** * revokeToken(refreshToken) is required and should return true */ - async revokeToken(refreshToken) { + async revokeToken (refreshToken) { this.log(`revokeToken (refreshToken: ${refreshToken})`) return revokeToken(refreshToken) } diff --git a/lib/oauth.js b/lib/oauth.js index 4f2f773..0305a3b 100644 --- a/lib/oauth.js +++ b/lib/oauth.js @@ -17,7 +17,7 @@ import { validateParams } from './validation/validateParams' import { requiredAuthorizeGetParams } from './validation/requiredAuthorizeGetParams' import { requiredAuthorizePostParams } from './validation/requiredAuthorizePostParams' import { requiredAccessTokenPostParams } from './validation/requiredAccessTokenPostParams' -import { requiredRefreshTokenPostParams } from "./validation/requiredRefreshTokenPostParams"; +import { requiredRefreshTokenPostParams } from './validation/requiredRefreshTokenPostParams'; import { UserValidation } from './validation/UserValidation' import { OptionsSchema } from './validation/OptionsSchema' diff --git a/lib/validation/requiredRefreshTokenPostParams.js b/lib/validation/requiredRefreshTokenPostParams.js index e577f1a..59588fc 100644 --- a/lib/validation/requiredRefreshTokenPostParams.js +++ b/lib/validation/requiredRefreshTokenPostParams.js @@ -1,11 +1,11 @@ -import { Match } from "meteor/check"; -import { nonEmptyString } from "./nonEmptyString"; +import { Match } from 'meteor/check' +import { nonEmptyString } from './nonEmptyString' -const isNonEmptyString = Match.Where(nonEmptyString); +const isNonEmptyString = Match.Where(nonEmptyString) export const requiredRefreshTokenPostParams = { grant_type: isNonEmptyString, refresh_token: isNonEmptyString, client_id: Match.Maybe(String), - client_secret: Match.Maybe(String), -}; + client_secret: Match.Maybe(String) +} From 680c1c67dd27a8e9f20caf6b6a896490902f5b17 Mon Sep 17 00:00:00 2001 From: Jeff Flynt Date: Tue, 2 Jul 2024 15:15:40 -0500 Subject: [PATCH 3/4] Remove semicolon in oauth.js --- lib/oauth.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/oauth.js b/lib/oauth.js index 0305a3b..e5e7930 100644 --- a/lib/oauth.js +++ b/lib/oauth.js @@ -17,7 +17,7 @@ import { validateParams } from './validation/validateParams' import { requiredAuthorizeGetParams } from './validation/requiredAuthorizeGetParams' import { requiredAuthorizePostParams } from './validation/requiredAuthorizePostParams' import { requiredAccessTokenPostParams } from './validation/requiredAccessTokenPostParams' -import { requiredRefreshTokenPostParams } from './validation/requiredRefreshTokenPostParams'; +import { requiredRefreshTokenPostParams } from './validation/requiredRefreshTokenPostParams' import { UserValidation } from './validation/UserValidation' import { OptionsSchema } from './validation/OptionsSchema' From 4526117991277747af498cf127e67b61b6f37bf6 Mon Sep 17 00:00:00 2001 From: Jeff Flynt Date: Thu, 18 Jul 2024 12:37:20 -0500 Subject: [PATCH 4/4] Fix addition of id when retrieving client --- lib/model/meteor-model.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/lib/model/meteor-model.js b/lib/model/meteor-model.js index fe5c2cc..3cdadd9 100644 --- a/lib/model/meteor-model.js +++ b/lib/model/meteor-model.js @@ -54,15 +54,7 @@ export const getClient = bind(function (clientId, secret) { clientId, secret: secret || undefined // secret can be undefined or null but should act as the same }) - - if (clientDoc) { - return { - ...clientDoc, - id: clientDoc.clientId - } - } else { - return false - } + return clientDoc || false }) /**