Skip to content

Commit f5c027d

Browse files
committed
fix: explicitly support array of strings for AuthenticateHandler.options
In #267 some changes were made for compliance reasons where scopes should be treated as strings for our OAuth interfaces but as arrays of strings for internal purposes. As part of that change, we accidentally made the `scope` prop for the `authenticate` method only support strings. This change remediates any potential backwards compatibility changes from v5.0 to v5.1
1 parent bc16601 commit f5c027d

File tree

3 files changed

+102
-6
lines changed

3 files changed

+102
-6
lines changed

index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ declare namespace OAuth2Server {
168168
/**
169169
* The scope(s) to authenticate.
170170
*/
171-
scope?: string | undefined;
171+
scope?: string[];
172172

173173
/**
174174
* Set the X-Accepted-OAuth-Scopes HTTP header on response objects.

lib/handlers/authenticate-handler.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class AuthenticateHandler {
4747
this.addAuthorizedScopesHeader = options.addAuthorizedScopesHeader;
4848
this.allowBearerTokensInQueryString = options.allowBearerTokensInQueryString;
4949
this.model = options.model;
50-
this.scope = parseScope(options.scope);
50+
this.scope = Array.isArray(options.scope) ? options.scope : parseScope(options.scope);
5151
}
5252

5353
/**

test/integration/handlers/authenticate-handler_test.js

Lines changed: 100 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,35 @@ describe('AuthenticateHandler integration', function() {
242242
});
243243

244244
it('should return an access token', function() {
245+
const accessToken = {
246+
user: {},
247+
accessTokenExpiresAt: new Date(new Date().getTime() + 10000)
248+
};
249+
const model = {
250+
getAccessToken: function() {
251+
return accessToken;
252+
},
253+
verifyScope: function() {
254+
return true;
255+
}
256+
};
257+
const handler = new AuthenticateHandler({ addAcceptedScopesHeader: true, addAuthorizedScopesHeader: true, model: model, scope: ['foo'] });
258+
const request = new Request({
259+
body: {},
260+
headers: { 'Authorization': 'Bearer foo' },
261+
method: {},
262+
query: {}
263+
});
264+
const response = new Response({ body: {}, headers: {} });
265+
266+
return handler.handle(request, response)
267+
.then(function(data) {
268+
data.should.equal(accessToken);
269+
})
270+
.catch(should.fail);
271+
});
272+
273+
it('should return an access token (deprecated)', function() {
245274
const accessToken = {
246275
user: {},
247276
accessTokenExpiresAt: new Date(new Date().getTime() + 10000)
@@ -515,7 +544,7 @@ describe('AuthenticateHandler integration', function() {
515544
});
516545

517546
describe('verifyScope()', function() {
518-
it('should throw an error if `scope` is insufficient', function() {
547+
it('should throw an error if `scope` is insufficient (deprecated)', function() {
519548
const model = {
520549
getAccessToken: function() {},
521550
verifyScope: function() {
@@ -532,7 +561,48 @@ describe('AuthenticateHandler integration', function() {
532561
});
533562
});
534563

564+
it('should throw an error if `scope` is insufficient', function() {
565+
const model = {
566+
getAccessToken: function() {},
567+
verifyScope: function() {
568+
return false;
569+
}
570+
};
571+
const handler = new AuthenticateHandler({ addAcceptedScopesHeader: true, addAuthorizedScopesHeader: true, model: model, scope: ['foo'] });
572+
573+
return handler.verifyScope(['foo'])
574+
.then(should.fail)
575+
.catch(function(e) {
576+
e.should.be.an.instanceOf(InsufficientScopeError);
577+
e.message.should.equal('Insufficient scope: authorized scope is insufficient');
578+
});
579+
});
580+
581+
it('should support promises (deprecated)', function() {
582+
const model = {
583+
getAccessToken: function() {},
584+
verifyScope: function() {
585+
return true;
586+
}
587+
};
588+
const handler = new AuthenticateHandler({ addAcceptedScopesHeader: true, addAuthorizedScopesHeader: true, model: model, scope: 'foo' });
589+
590+
handler.verifyScope(['foo']).should.be.an.instanceOf(Promise);
591+
});
592+
535593
it('should support promises', function() {
594+
const model = {
595+
getAccessToken: function() {},
596+
verifyScope: function() {
597+
return true;
598+
}
599+
};
600+
const handler = new AuthenticateHandler({ addAcceptedScopesHeader: true, addAuthorizedScopesHeader: true, model: model, scope: ['foo'] });
601+
602+
handler.verifyScope(['foo']).should.be.an.instanceOf(Promise);
603+
});
604+
605+
it('should support non-promises (deprecated)', function() {
536606
const model = {
537607
getAccessToken: function() {},
538608
verifyScope: function() {
@@ -551,7 +621,7 @@ describe('AuthenticateHandler integration', function() {
551621
return true;
552622
}
553623
};
554-
const handler = new AuthenticateHandler({ addAcceptedScopesHeader: true, addAuthorizedScopesHeader: true, model: model, scope: 'foo' });
624+
const handler = new AuthenticateHandler({ addAcceptedScopesHeader: true, addAuthorizedScopesHeader: true, model: model, scope: ['foo'] });
555625

556626
handler.verifyScope(['foo']).should.be.an.instanceOf(Promise);
557627
});
@@ -571,7 +641,7 @@ describe('AuthenticateHandler integration', function() {
571641
response.headers.should.not.have.property('x-accepted-oauth-scopes');
572642
});
573643

574-
it('should set the `X-Accepted-OAuth-Scopes` header if `scope` is specified', function() {
644+
it('should set the `X-Accepted-OAuth-Scopes` header if `scope` is specified (deprecated)', function() {
575645
const model = {
576646
getAccessToken: function() {},
577647
verifyScope: function() {}
@@ -584,6 +654,19 @@ describe('AuthenticateHandler integration', function() {
584654
response.get('X-Accepted-OAuth-Scopes').should.equal('foo bar');
585655
});
586656

657+
it('should set the `X-Accepted-OAuth-Scopes` header if `scope` is specified', function() {
658+
const model = {
659+
getAccessToken: function() {},
660+
verifyScope: function() {}
661+
};
662+
const handler = new AuthenticateHandler({ addAcceptedScopesHeader: true, addAuthorizedScopesHeader: false, model: model, scope: ['foo', 'bar'] });
663+
const response = new Response({ body: {}, headers: {} });
664+
665+
handler.updateResponse(response, { scope: ['foo', 'biz'] });
666+
667+
response.get('X-Accepted-OAuth-Scopes').should.equal('foo bar');
668+
});
669+
587670
it('should not set the `X-Authorized-OAuth-Scopes` header if `scope` is not specified', function() {
588671
const model = {
589672
getAccessToken: function() {},
@@ -597,7 +680,7 @@ describe('AuthenticateHandler integration', function() {
597680
response.headers.should.not.have.property('x-oauth-scopes');
598681
});
599682

600-
it('should set the `X-Authorized-OAuth-Scopes` header', function() {
683+
it('should set the `X-Authorized-OAuth-Scopes` header (deprecated)', function() {
601684
const model = {
602685
getAccessToken: function() {},
603686
verifyScope: function() {}
@@ -609,5 +692,18 @@ describe('AuthenticateHandler integration', function() {
609692

610693
response.get('X-OAuth-Scopes').should.equal('foo biz');
611694
});
695+
696+
it('should set the `X-Authorized-OAuth-Scopes` header', function() {
697+
const model = {
698+
getAccessToken: function() {},
699+
verifyScope: function() {}
700+
};
701+
const handler = new AuthenticateHandler({ addAcceptedScopesHeader: false, addAuthorizedScopesHeader: true, model: model, scope: ['foo', 'bar'] });
702+
const response = new Response({ body: {}, headers: {} });
703+
704+
handler.updateResponse(response, { scope: ['foo', 'biz'] });
705+
706+
response.get('X-OAuth-Scopes').should.equal('foo biz');
707+
});
612708
});
613709
});

0 commit comments

Comments
 (0)