Skip to content

Commit 9357f4e

Browse files
authored
Merge pull request #12 from acasella/master
Implement token revocation
2 parents a1148cd + 7fd9ea8 commit 9357f4e

File tree

6 files changed

+70
-4
lines changed

6 files changed

+70
-4
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
66

7+
## [1.3.0](https://github.com/axa-group/oauth2-mock-server/compare/v1.2.0...v1.3.0) — 2019-06-03
8+
9+
### Added
10+
11+
- Add revocation endpoint
12+
713
## [1.2.0](https://github.com/axa-group/oauth2-mock-server/compare/v1.1.0...v1.2.0) — 2019-03-19
814

915
### Added

README.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ It also provides a convenient way, through event emitters, to programatically cu
8282
- The JWT access token
8383
- The token endpoint response body and status
8484
- The userinfo endpoint response body and status
85+
- The revoke endpoint response body and status
8586

8687
This is particularly useful when expecting the oidc service to behave in a specific way on one single test.
8788

@@ -108,6 +109,13 @@ service.issuer.once('beforeSigning', (token) => {
108109
const timestamp = Math.floor(Date.now() / 1000);
109110
token.payload.exp = timestamp + 400;
110111
});
112+
113+
//Simulates a custom token revocation body
114+
service.once('beforeRevoke', (revokeResponse) => {
115+
revokeResponse.body = {
116+
result: 'revoked'
117+
};
118+
});
111119
```
112120

113121
## Supported endpoints
@@ -130,15 +138,19 @@ Issues access tokens. Currently, this endpoint is limited to:
130138
- Authorization code grant
131139
- Refresh token grant
132140

133-
### GET /authorize
141+
### GET `/authorize`
134142

135143
It simulates the user authentication. It will automatically redirect to the callback endpoint sent as parameter.
136144
It currently supports only 'code' response_type.
137145

138-
### GET /userinfo
146+
### GET `/userinfo`
139147

140148
It provides extra userinfo claims.
141149

150+
### POST `/revoke`
151+
152+
It simulates a token revocation. This endpoint should always return 200 as stated by [RFC 7009](https://tools.ietf.org/html/rfc7009#section-2.2).
153+
142154
## Command-Line Interface
143155

144156
The server can be run from the command line. Run `oauth2-mock-server --help` for details on its utilization.

lib/oauth2-service.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const TOKEN_ENDPOINT_PATH = '/token';
3131
const JWKS_URI_PATH = '/jwks';
3232
const AUTHORIZE_PATH = '/authorize';
3333
const USERINFO_PATH = '/userinfo';
34+
const REVOKE_PATH = '/revoke';
3435

3536
const issuer = Symbol('issuer');
3637
const requestHandler = Symbol('requestHandler');
@@ -41,6 +42,7 @@ const jwksHandler = Symbol('jwksHandler');
4142
const tokenHandler = Symbol('tokenHandler');
4243
const authorizeHandler = Symbol('authorizeHandler');
4344
const userInfoHandler = Symbol('userInfoHandler');
45+
const revokeHandler = Symbol('revokeHandler');
4446

4547
/**
4648
* Provides a request handler for an OAuth 2 server.
@@ -97,6 +99,7 @@ class OAuth2Service extends EventEmitter {
9799
this[tokenHandler].bind(this));
98100
app.get(AUTHORIZE_PATH, OAuth2Service[authorizeHandler]);
99101
app.get(USERINFO_PATH, this[userInfoHandler].bind(this));
102+
app.post(REVOKE_PATH, this[revokeHandler].bind(this));
100103

101104
return app;
102105
}
@@ -114,6 +117,7 @@ class OAuth2Service extends EventEmitter {
114117
token_endpoint_auth_signing_alg_values_supported: ['RS256'],
115118
response_modes_supported: ['query'],
116119
id_token_signing_alg_values_supported: ['RS256'],
120+
revocation_endpoint: `${this.issuer.url}${REVOKE_PATH}`,
117121
};
118122

119123
return res.json(openidConfig);
@@ -224,5 +228,14 @@ class OAuth2Service extends EventEmitter {
224228
this.emit('beforeUserinfo', userInfoResponse);
225229
res.status(userInfoResponse.statusCode).json(userInfoResponse.body);
226230
}
231+
232+
[revokeHandler](req, res) {
233+
const revokeResponse = {
234+
body: null,
235+
statusCode: 200,
236+
};
237+
this.emit('beforeRevoke', revokeResponse);
238+
return res.status(revokeResponse.statusCode).json(revokeResponse.body);
239+
}
227240
}
228241
module.exports = OAuth2Service;

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "oauth2-mock-server",
3-
"version": "1.2.0",
3+
"version": "1.3.0",
44
"description": "OAuth 2 mock server",
55
"author": {
66
"name": "Jorge Poveda",

test/oauth2-service.test.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ describe('OAuth 2 service', () => {
3636
token_endpoint_auth_signing_alg_values_supported: ['RS256'],
3737
response_modes_supported: ['query'],
3838
id_token_signing_alg_values_supported: ['RS256'],
39+
revocation_endpoint: `${url}/revoke`,
3940
});
4041
});
4142

@@ -267,6 +268,40 @@ describe('OAuth 2 service', () => {
267268
error_message: 'token is expired',
268269
});
269270
});
271+
272+
it('should expose the revoke endpoint', async () => {
273+
const res = await request(service.requestHandler)
274+
.post('/revoke')
275+
.type('form')
276+
.set('authorization', `Basic ${Buffer.from('dummy_client_id:dummy_client_secret').toString('base64')}`)
277+
.send({
278+
token: 'authorization_code',
279+
token_type_hint: 'refresh_token',
280+
})
281+
.expect(200);
282+
283+
expect(res.body).toEqual(null);
284+
});
285+
286+
it('should allow customizing the revoke response through a beforeRevoke event', async () => {
287+
service.once('beforeRevoke', (revokeResponse) => {
288+
/* eslint-disable no-param-reassign */
289+
revokeResponse.body = '';
290+
revokeResponse.statusCode = 204;
291+
/* eslint-enable no-param-reassign */
292+
});
293+
const res = await request(service.requestHandler)
294+
.post('/revoke')
295+
.type('form')
296+
.set('authorization', `Basic ${Buffer.from('dummy_client_id:dummy_client_secret').toString('base64')}`)
297+
.send({
298+
token: 'authorization_code',
299+
token_type_hint: 'refresh_token',
300+
})
301+
.expect(204);
302+
303+
expect(res.text).toBeFalsy();
304+
});
270305
});
271306

272307
function tokenRequest(app) {

0 commit comments

Comments
 (0)