Skip to content

Commit bcc59a9

Browse files
committed
Merge branch 'development' into fix-missing-body-allowed-check
2 parents 5b5de24 + 9fab017 commit bcc59a9

20 files changed

+1032
-196
lines changed

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
## Changelog
22

3+
## 4.1.1
4+
5+
### Added
6+
- Added TypeScript types
7+
### Changed
8+
- Removed extra files when someone npm installs.
9+
- Upgrades all code from ES5 to ES6, where possible.
10+
311
## 4.1.0
412
### Changed
513
* Bump dev dependencies to resolve vulnerabilities

docs/model/overview.rst

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Model functions used by the authorization code grant:
3737
- :ref:`Model#saveAuthorizationCode`
3838
- :ref:`Model#revokeAuthorizationCode`
3939
- :ref:`Model#validateScope`
40+
- :ref:`Model#validateRedirectUri`
4041

4142
--------
4243

docs/model/spec.rst

+45-4
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ An ``Object`` representing the access token and associated data.
214214

215215
function getAccessToken(accessToken) {
216216
// imaginary DB queries
217-
db.queryAccessToken({access_token: accessToken})
217+
return db.queryAccessToken({access_token: accessToken})
218218
.then(function(token) {
219219
return Promise.all([
220220
token,
@@ -288,7 +288,7 @@ An ``Object`` representing the refresh token and associated data.
288288

289289
function getRefreshToken(refreshToken) {
290290
// imaginary DB queries
291-
db.queryRefreshToken({refresh_token: refreshToken})
291+
return db.queryRefreshToken({refresh_token: refreshToken})
292292
.then(function(token) {
293293
return Promise.all([
294294
token,
@@ -364,7 +364,7 @@ An ``Object`` representing the authorization code and associated data.
364364

365365
function getAuthorizationCode(authorizationCode) {
366366
// imaginary DB queries
367-
db.queryAuthorizationCode({authorization_code: authorizationCode})
367+
return db.queryAuthorizationCode({authorization_code: authorizationCode})
368368
.then(function(code) {
369369
return Promise.all([
370370
code,
@@ -446,7 +446,7 @@ The return value (``client``) can carry additional properties that will be ignor
446446
if (clientSecret) {
447447
params.client_secret = clientSecret;
448448
}
449-
db.queryClient(params)
449+
return db.queryClient(params)
450450
.then(function(client) {
451451
return {
452452
id: client.id,
@@ -985,3 +985,44 @@ Returns ``true`` if the access token passes, ``false`` otherwise.
985985
return requestedScopes.every(s => authorizedScopes.indexOf(s) >= 0);
986986
}
987987

988+
--------
989+
990+
.. _Model#validateRedirectUri:
991+
992+
``validateRedirectUri(redirectUri, client, [callback])``
993+
================================================================
994+
995+
Invoked to check if the provided ``redirectUri`` is valid for a particular ``client``.
996+
997+
This model function is **optional**. If not implemented, the ``redirectUri`` should be included in the provided ``redirectUris`` of the client.
998+
999+
**Invoked during:**
1000+
1001+
- ``authorization_code`` grant
1002+
1003+
**Arguments:**
1004+
1005+
+-----------------+----------+---------------------------------------------------------------------+
1006+
| Name | Type | Description |
1007+
+=================+==========+=====================================================================+
1008+
| redirect_uri | String | The redirect URI to validate. |
1009+
+-----------------+----------+---------------------------------------------------------------------+
1010+
| client | Object | The associated client. |
1011+
+-----------------+----------+---------------------------------------------------------------------+
1012+
1013+
**Return value:**
1014+
1015+
Returns ``true`` if the ``redirectUri`` is valid, ``false`` otherwise.
1016+
1017+
**Remarks:**
1018+
When implementing this method you should take care of possible security risks related to ``redirectUri``.
1019+
.. _rfc6819: https://datatracker.ietf.org/doc/html/rfc6819
1020+
1021+
Section-5.2.3.5 is implemented by default.
1022+
.. _Section-5.2.3.5: https://datatracker.ietf.org/doc/html/rfc6819#section-5.2.3.5
1023+
1024+
::
1025+
1026+
function validateRedirectUri(redirectUri, client) {
1027+
return client.redirectUris.includes(redirectUri);
1028+
}

lib/grant-types/abstract-grant-type.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const InvalidArgumentError = require('../errors/invalid-argument-error');
88
const InvalidScopeError = require('../errors/invalid-scope-error');
99
const Promise = require('bluebird');
1010
const promisify = require('promisify-any').use(Promise);
11-
const is = require('../validator/is');
11+
const isFormat = require('@node-oauth/formats');
1212
const tokenUtil = require('../utils/token-util');
1313

1414
/**
@@ -83,7 +83,7 @@ AbstractGrantType.prototype.getRefreshTokenExpiresAt = function() {
8383
*/
8484

8585
AbstractGrantType.prototype.getScope = function(request) {
86-
if (!is.nqschar(request.body.scope)) {
86+
if (!isFormat.nqschar(request.body.scope)) {
8787
throw new InvalidArgumentError('Invalid parameter: `scope`');
8888
}
8989

lib/grant-types/authorization-code-grant-type.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const InvalidRequestError = require('../errors/invalid-request-error');
1111
const Promise = require('bluebird');
1212
const promisify = require('promisify-any').use(Promise);
1313
const ServerError = require('../errors/server-error');
14-
const is = require('../validator/is');
14+
const isFormat = require('@node-oauth/formats');
1515
const util = require('util');
1616

1717
/**
@@ -85,7 +85,7 @@ AuthorizationCodeGrantType.prototype.getAuthorizationCode = function(request, cl
8585
throw new InvalidRequestError('Missing parameter: `code`');
8686
}
8787

88-
if (!is.vschar(request.body.code)) {
88+
if (!isFormat.vschar(request.body.code)) {
8989
throw new InvalidRequestError('Invalid parameter: `code`');
9090
}
9191
return promisify(this.model.getAuthorizationCode, 1).call(this.model, request.body.code)
@@ -114,7 +114,7 @@ AuthorizationCodeGrantType.prototype.getAuthorizationCode = function(request, cl
114114
throw new InvalidGrantError('Invalid grant: authorization code has expired');
115115
}
116116

117-
if (code.redirectUri && !is.uri(code.redirectUri)) {
117+
if (code.redirectUri && !isFormat.uri(code.redirectUri)) {
118118
throw new InvalidGrantError('Invalid grant: `redirect_uri` is not a valid URI');
119119
}
120120

@@ -140,7 +140,7 @@ AuthorizationCodeGrantType.prototype.validateRedirectUri = function(request, cod
140140

141141
const redirectUri = request.body.redirect_uri || request.query.redirect_uri;
142142

143-
if (!is.uri(redirectUri)) {
143+
if (!isFormat.uri(redirectUri)) {
144144
throw new InvalidRequestError('Invalid request: `redirect_uri` is not a valid URI');
145145
}
146146

lib/grant-types/password-grant-type.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const InvalidGrantError = require('../errors/invalid-grant-error');
1010
const InvalidRequestError = require('../errors/invalid-request-error');
1111
const Promise = require('bluebird');
1212
const promisify = require('promisify-any').use(Promise);
13-
const is = require('../validator/is');
13+
const isFormat = require('@node-oauth/formats');
1414
const util = require('util');
1515

1616
/**
@@ -80,11 +80,11 @@ PasswordGrantType.prototype.getUser = function(request) {
8080
throw new InvalidRequestError('Missing parameter: `password`');
8181
}
8282

83-
if (!is.uchar(request.body.username)) {
83+
if (!isFormat.uchar(request.body.username)) {
8484
throw new InvalidRequestError('Invalid parameter: `username`');
8585
}
8686

87-
if (!is.uchar(request.body.password)) {
87+
if (!isFormat.uchar(request.body.password)) {
8888
throw new InvalidRequestError('Invalid parameter: `password`');
8989
}
9090

lib/grant-types/refresh-token-grant-type.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const InvalidRequestError = require('../errors/invalid-request-error');
1111
const Promise = require('bluebird');
1212
const promisify = require('promisify-any').use(Promise);
1313
const ServerError = require('../errors/server-error');
14-
const is = require('../validator/is');
14+
const isFormat = require('@node-oauth/formats');
1515
const util = require('util');
1616

1717
/**
@@ -82,7 +82,7 @@ RefreshTokenGrantType.prototype.getRefreshToken = function(request, client) {
8282
throw new InvalidRequestError('Missing parameter: `refresh_token`');
8383
}
8484

85-
if (!is.vschar(request.body.refresh_token)) {
85+
if (!isFormat.vschar(request.body.refresh_token)) {
8686
throw new InvalidRequestError('Invalid parameter: `refresh_token`');
8787
}
8888

lib/handlers/authorize-handler.js

+37-15
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const Request = require('../request');
1818
const Response = require('../response');
1919
const ServerError = require('../errors/server-error');
2020
const UnauthorizedClientError = require('../errors/unauthorized-client-error');
21-
const is = require('../validator/is');
21+
const isFormat = require('@node-oauth/formats');
2222
const tokenUtil = require('../utils/token-util');
2323
const url = require('url');
2424

@@ -96,6 +96,12 @@ AuthorizeHandler.prototype.handle = function(request, response) {
9696
let ResponseType;
9797

9898
return Promise.bind(this)
99+
.then(function() {
100+
state = this.getState(request);
101+
if(request.query.allowed === 'false') {
102+
throw new AccessDeniedError('Access denied: user denied access to application');
103+
}
104+
})
99105
.then(function() {
100106
const requestedScope = this.getScope(request);
101107

@@ -107,7 +113,6 @@ AuthorizeHandler.prototype.handle = function(request, response) {
107113
return this.generateAuthorizationCode(client, user, scope);
108114
})
109115
.then(function(authorizationCode) {
110-
state = this.getState(request);
111116
ResponseType = this.getResponseType(request);
112117

113118
return this.saveAuthorizationCode(authorizationCode, expiresAt, scope, client, uri, user);
@@ -160,19 +165,20 @@ AuthorizeHandler.prototype.getAuthorizationCodeLifetime = function() {
160165
*/
161166

162167
AuthorizeHandler.prototype.getClient = function(request) {
168+
const self = this;
163169
const clientId = request.body.client_id || request.query.client_id;
164170

165171
if (!clientId) {
166172
throw new InvalidRequestError('Missing parameter: `client_id`');
167173
}
168174

169-
if (!is.vschar(clientId)) {
175+
if (!isFormat.vschar(clientId)) {
170176
throw new InvalidRequestError('Invalid parameter: `client_id`');
171177
}
172178

173179
const redirectUri = request.body.redirect_uri || request.query.redirect_uri;
174180

175-
if (redirectUri && !is.uri(redirectUri)) {
181+
if (redirectUri && !isFormat.uri(redirectUri)) {
176182
throw new InvalidRequestError('Invalid request: `redirect_uri` is not a valid URI');
177183
}
178184
return promisify(this.model.getClient, 2).call(this.model, clientId, null)
@@ -193,10 +199,17 @@ AuthorizeHandler.prototype.getClient = function(request) {
193199
throw new InvalidClientError('Invalid client: missing client `redirectUri`');
194200
}
195201

196-
if (redirectUri && !client.redirectUris.includes(redirectUri)) {
197-
throw new InvalidClientError('Invalid client: `redirect_uri` does not match client value');
202+
if (redirectUri) {
203+
return self.validateRedirectUri(redirectUri, client)
204+
.then(function(valid) {
205+
if (!valid) {
206+
throw new InvalidClientError('Invalid client: `redirect_uri` does not match client value');
207+
}
208+
return client;
209+
});
210+
} else {
211+
return client;
198212
}
199-
return client;
200213
});
201214
};
202215

@@ -225,7 +238,7 @@ AuthorizeHandler.prototype.validateScope = function(user, client, scope) {
225238
AuthorizeHandler.prototype.getScope = function(request) {
226239
const scope = request.body.scope || request.query.scope;
227240

228-
if (!is.nqschar(scope)) {
241+
if (!isFormat.nqschar(scope)) {
229242
throw new InvalidScopeError('Invalid parameter: `scope`');
230243
}
231244

@@ -238,13 +251,14 @@ AuthorizeHandler.prototype.getScope = function(request) {
238251

239252
AuthorizeHandler.prototype.getState = function(request) {
240253
const state = request.body.state || request.query.state;
241-
242-
if (!this.allowEmptyState && !state) {
243-
throw new InvalidRequestError('Missing parameter: `state`');
244-
}
245-
246-
if (!is.vschar(state)) {
247-
throw new InvalidRequestError('Invalid parameter: `state`');
254+
const stateExists = state && state.length > 0;
255+
const stateIsValid = stateExists
256+
? isFormat.vschar(state)
257+
: this.allowEmptyState;
258+
259+
if (!stateIsValid) {
260+
const message = (!stateExists) ? 'Missing' : 'Invalid';
261+
throw new InvalidRequestError(`${message} parameter: \`state\``);
248262
}
249263

250264
return state;
@@ -289,6 +303,14 @@ AuthorizeHandler.prototype.saveAuthorizationCode = function(authorizationCode, e
289303
return promisify(this.model.saveAuthorizationCode, 3).call(this.model, code, client, user);
290304
};
291305

306+
307+
AuthorizeHandler.prototype.validateRedirectUri = function(redirectUri, client) {
308+
if (this.model.validateRedirectUri) {
309+
return promisify(this.model.validateRedirectUri, 2).call(this.model, redirectUri, client);
310+
}
311+
312+
return Promise.resolve(client.redirectUris.includes(redirectUri));
313+
};
292314
/**
293315
* Get response type.
294316
*/

lib/handlers/token-handler.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const TokenModel = require('../models/token-model');
1818
const UnauthorizedClientError = require('../errors/unauthorized-client-error');
1919
const UnsupportedGrantTypeError = require('../errors/unsupported-grant-type-error');
2020
const auth = require('basic-auth');
21-
const is = require('../validator/is');
21+
const isFormat = require('@node-oauth/formats');
2222

2323
/**
2424
* Grant types.
@@ -123,11 +123,11 @@ TokenHandler.prototype.getClient = function(request, response) {
123123
throw new InvalidRequestError('Missing parameter: `client_secret`');
124124
}
125125

126-
if (!is.vschar(credentials.clientId)) {
126+
if (!isFormat.vschar(credentials.clientId)) {
127127
throw new InvalidRequestError('Invalid parameter: `client_id`');
128128
}
129129

130-
if (credentials.clientSecret && !is.vschar(credentials.clientSecret)) {
130+
if (credentials.clientSecret && !isFormat.vschar(credentials.clientSecret)) {
131131
throw new InvalidRequestError('Invalid parameter: `client_secret`');
132132
}
133133

@@ -203,7 +203,7 @@ TokenHandler.prototype.handleGrantType = function(request, client) {
203203
throw new InvalidRequestError('Missing parameter: `grant_type`');
204204
}
205205

206-
if (!is.nchar(grantType) && !is.uri(grantType)) {
206+
if (!isFormat.nchar(grantType) && !isFormat.uri(grantType)) {
207207
throw new InvalidRequestError('Invalid parameter: `grant_type`');
208208
}
209209

0 commit comments

Comments
 (0)