Skip to content

Commit

Permalink
Implement Refresh Token Flow
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeff Flynt committed Jun 29, 2024
1 parent c200242 commit 42f731e
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 5 deletions.
21 changes: 19 additions & 2 deletions lib/model/meteor-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
})

/**
Expand Down Expand Up @@ -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;
})
11 changes: 10 additions & 1 deletion lib/model/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
saveAuthorizationCode,
saveRefreshToken,
saveToken,
getAccessToken
getAccessToken,
revokeToken
} from './meteor-model'

/**
Expand Down Expand Up @@ -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 }
3 changes: 2 additions & 1 deletion lib/oauth.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -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',
Expand Down
4 changes: 3 additions & 1 deletion lib/utils/isModelInterface.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ const modelNames = [
'saveAuthorizationCode',
'saveRefreshToken',
'saveToken',
'getAccessToken'
'getAccessToken',
'revokeToken'
]

/**
Expand All @@ -28,6 +29,7 @@ const modelNames = [
* - 'saveRefreshToken',
* - 'saveToken',
* - 'getAccessToken'
* - 'revokeToken'
* @param model {Object} the model implementation
* @return {boolean} true if valid, otherwise false
*/
Expand Down
11 changes: 11 additions & 0 deletions lib/validation/requiredRefreshTokenPostParams.js
Original file line number Diff line number Diff line change
@@ -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),
};

0 comments on commit 42f731e

Please sign in to comment.