Skip to content

Commit 054f094

Browse files
jorisduguejoris
andauthored
feat(chore): implement revoke token and revoke all token (#222)
* feat(chore): update deps and implement gitlab provider * feat(docs): implement gitlab configuration in preset * feat(chore): implement revokeToken * feat(tests): implement missing test in interface * feat(chore,tests): implement revokeAllToken * feat(chore): implement revokeAllToken --------- Co-authored-by: joris <[email protected]>
1 parent 08381e6 commit 054f094

File tree

5 files changed

+545
-210
lines changed

5 files changed

+545
-210
lines changed

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ Assuming we have registered multiple OAuth providers like this:
244244

245245
## Utilities
246246

247-
This fastify plugin adds 3 utility decorators to your fastify instance using the same **namespace**:
247+
This fastify plugin adds 5 utility decorators to your fastify instance using the same **namespace**:
248248

249249
- `getAccessTokenFromAuthorizationCodeFlow(request, callback)`: A function that uses the Authorization code flow to fetch an OAuth2 token using the data in the last request of the flow. If the callback is not passed it will return a promise. The callback call or promise resolution returns an [AccessToken](https://github.com/lelylan/simple-oauth2/blob/master/API.md#accesstoken) object, which has an `AccessToken.token` property with the following keys:
250250
- `access_token`
@@ -267,6 +267,18 @@ fastify.get('/external', { /* Hooks can be used here */ }, async (req, reply) =>
267267
});
268268
```
269269

270+
- `revokeToken(Token, tokenType, params, callback)`: A function to revoke the current access_token or refresh_token on the authorization server. If the callback is not passed it will return a promise. The callback call or promise resolution returns `void`
271+
```js
272+
fastify.googleOAuth2.revokeToken(currentAccessToken, 'access_token', undefined, (err) => {
273+
// Handle the reply here
274+
});
275+
```
276+
- `revokeAllToken(Token, params, callback)`: A function to revoke the current access_token and refresh_token on the authorization server. If the callback is not passed it will return a promise. The callback call or promise resolution returns `void`
277+
```js
278+
fastify.googleOAuth2.revokeAllToken(currentAccessToken, undefined, (err) => {
279+
// Handle the reply here
280+
});
281+
```
270282
E.g. For `name: 'customOauth2'`, the helpers `getAccessTokenFromAuthorizationCodeFlow` and `getNewAccessTokenUsingRefreshToken` will become accessible like this:
271283

272284
- `fastify.customOauth2.getAccessTokenFromAuthorizationCodeFlow`

index.js

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
'use strict'
22

3-
const crypto = require('crypto')
3+
const { randomBytes } = require('crypto')
44

55
const fp = require('fastify-plugin')
66
const { AuthorizationCode } = require('simple-oauth2')
77
const kGenerateCallbackUriParams = Symbol.for('fastify-oauth2.generate-callback-uri-params')
88

9-
const promisify = require('util').promisify
10-
const callbackify = require('util').callbackify
9+
const { promisify, callbackify } = require('util')
1110

1211
function defaultGenerateStateFunction () {
13-
return crypto.randomBytes(16).toString('base64url')
12+
return randomBytes(16).toString('base64url')
1413
}
1514

1615
function defaultCheckStateFunction (request, callback) {
@@ -27,6 +26,12 @@ function defaultGenerateCallbackUriParams (callbackUriParams) {
2726
return callbackUriParams
2827
}
2928

29+
/**
30+
* @param {FastifyInstance} fastify
31+
* @param {Partial<FastifyOAuth2Options>} options
32+
* @param {Function} next
33+
* @return {*}
34+
*/
3035
function fastifyOauth2 (fastify, options, next) {
3136
if (typeof options.name !== 'string') {
3237
return next(new Error('options.name should be a string'))
@@ -123,6 +128,7 @@ function fastifyOauth2 (fastify, options, next) {
123128
cbk(fastify[name], code, callback)
124129
})
125130
}
131+
126132
const getAccessTokenFromAuthorizationCodeFlowPromisified = promisify(getAccessTokenFromAuthorizationCodeFlowCallbacked)
127133

128134
function getAccessTokenFromAuthorizationCodeFlow (request, callback) {
@@ -136,6 +142,7 @@ function fastifyOauth2 (fastify, options, next) {
136142
const accessToken = fastify[name].oauth2.createToken(refreshToken)
137143
callbackify(accessToken.refresh.bind(accessToken, params))(callback)
138144
}
145+
139146
const getNewAccessTokenUsingRefreshTokenPromisified = promisify(getNewAccessTokenUsingRefreshTokenCallbacked)
140147

141148
function getNewAccessTokenUsingRefreshToken (refreshToken, params, callback) {
@@ -144,6 +151,35 @@ function fastifyOauth2 (fastify, options, next) {
144151
}
145152
getNewAccessTokenUsingRefreshTokenCallbacked(refreshToken, params, callback)
146153
}
154+
155+
function revokeTokenCallbacked (token, tokenType, params, callback) {
156+
const accessToken = fastify[name].oauth2.createToken(token)
157+
callbackify(accessToken.revoke.bind(accessToken, tokenType, params))(callback)
158+
}
159+
160+
const revokeTokenPromisified = promisify(revokeTokenCallbacked)
161+
162+
function revokeToken (token, tokenType, params, callback) {
163+
if (!callback) {
164+
return revokeTokenPromisified(token, tokenType, params)
165+
}
166+
revokeTokenCallbacked(token, tokenType, params, callback)
167+
}
168+
169+
function revokeAllTokenCallbacked (token, params, callback) {
170+
const accessToken = fastify[name].oauth2.createToken(token)
171+
callbackify(accessToken.revokeAll.bind(accessToken, token, params))(callback)
172+
}
173+
174+
const revokeAllTokenPromisified = promisify(revokeAllTokenCallbacked)
175+
176+
function revokeAllToken (token, params, callback) {
177+
if (!callback) {
178+
return revokeAllTokenPromisified(token, params)
179+
}
180+
revokeAllTokenCallbacked(token, params, callback)
181+
}
182+
147183
const oauth2 = new AuthorizationCode(credentials)
148184

149185
if (startRedirectPath) {
@@ -155,7 +191,9 @@ function fastifyOauth2 (fastify, options, next) {
155191
oauth2,
156192
getAccessTokenFromAuthorizationCodeFlow,
157193
getNewAccessTokenUsingRefreshToken,
158-
generateAuthorizationUri
194+
generateAuthorizationUri,
195+
revokeToken,
196+
revokeAllToken
159197
})
160198
} catch (e) {
161199
next(e)
@@ -170,14 +208,15 @@ fastifyOauth2.APPLE_CONFIGURATION = {
170208
authorizePath: '/auth/authorize',
171209
tokenHost: 'https://appleid.apple.com',
172210
tokenPath: '/auth/token',
211+
revokePath: '/auth/revoke',
173212
// kGenerateCallbackUriParams is used for dedicated behavior for each OAuth2.0 provider
174213
// It can update the callbackUriParams based on requestObject, scope and state
175214
//
176215
// Symbol used in here because we would not like the user to modify this behavior and
177216
// do not want to mess up with property name collision
178217
[kGenerateCallbackUriParams]: function (callbackUriParams, requestObject, scope, state) {
179218
const stringifyScope = Array.isArray(scope) ? scope.join(' ') : scope
180-
// This behavior is not documented on Apple Developer Docs but it display through runtime error.
219+
// This behavior is not documented on Apple Developer Docs, but it displays through runtime error.
181220
// Related Docs: https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js/incorporating_sign_in_with_apple_into_other_platforms
182221
// Related Issue: https://github.com/fastify/fastify-oauth2/issues/116
183222
//
@@ -206,14 +245,16 @@ fastifyOauth2.GITLAB_CONFIGURATION = {
206245
authorizeHost: 'https://gitlab.com',
207246
authorizePath: '/oauth/authorize',
208247
tokenHost: 'https://gitlab.com',
209-
tokenPath: '/oauth/token'
248+
tokenPath: '/oauth/token',
249+
revokePath: '/oauth/revoke'
210250
}
211251

212252
fastifyOauth2.LINKEDIN_CONFIGURATION = {
213253
authorizeHost: 'https://www.linkedin.com',
214254
authorizePath: '/oauth/v2/authorization',
215255
tokenHost: 'https://www.linkedin.com',
216-
tokenPath: '/oauth/v2/accessToken'
256+
tokenPath: '/oauth/v2/accessToken',
257+
revokePath: '/oauth/v2/revoke'
217258
}
218259

219260
fastifyOauth2.GOOGLE_CONFIGURATION = {
@@ -248,14 +289,16 @@ fastifyOauth2.DISCORD_CONFIGURATION = {
248289
authorizeHost: 'https://discord.com',
249290
authorizePath: '/api/oauth2/authorize',
250291
tokenHost: 'https://discord.com',
251-
tokenPath: '/api/oauth2/token'
292+
tokenPath: '/api/oauth2/token',
293+
revokePath: '/api/oauth2/token/revoke'
252294
}
253295

254296
fastifyOauth2.TWITCH_CONFIGURATION = {
255297
authorizeHost: 'https://id.twitch.tv',
256298
authorizePath: '/oauth2/authorize',
257299
tokenHost: 'https://id.twitch.tv',
258-
tokenPath: '/oauth2/token'
300+
tokenPath: '/oauth2/token',
301+
revokePath: '/oauth2/revoke'
259302
}
260303

261304
fastifyOauth2.VATSIM_CONFIGURATION = {

0 commit comments

Comments
 (0)