Skip to content

Commit

Permalink
fix: use insufficient_scope instead of invalid_scope at userinfo_endp…
Browse files Browse the repository at this point in the history
…oint
  • Loading branch information
panva committed Oct 12, 2021
1 parent 2d3cae0 commit ba8a8f0
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 7 deletions.
12 changes: 6 additions & 6 deletions lib/actions/userinfo.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { InvalidDpopProof, InvalidToken, InvalidScope } = require('../helpers/errors');
const { InvalidDpopProof, InvalidToken, InsufficientScope } = require('../helpers/errors');
const difference = require('../helpers/_/difference');
const setWWWAuthenticate = require('../helpers/set_www_authenticate');
const bodyParser = require('../shared/conditional_body');
Expand Down Expand Up @@ -74,8 +74,9 @@ module.exports = [

ctx.oidc.entity('AccessToken', accessToken);

if (!accessToken.scope || !accessToken.scope.split(' ').includes('openid')) {
throw new InvalidToken('access token missing openid scope');
const { scopes } = accessToken;
if (!scopes.size || !scopes.has('openid')) {
throw new InsufficientScope('access token missing openid scope', 'openid');
}

if (accessToken['x5t#S256']) {
Expand Down Expand Up @@ -115,11 +116,10 @@ module.exports = [

async function validateScope(ctx, next) {
if (ctx.oidc.params.scope) {
const accessTokenScopes = ctx.oidc.accessToken.scope.split(' ');
const missing = difference(ctx.oidc.params.scope.split(' '), accessTokenScopes);
const missing = difference(ctx.oidc.params.scope.split(' '), [...ctx.oidc.accessToken.scopes]);

if (missing.length !== 0) {
throw new InvalidScope('access token missing requested scope', missing.join(' '));
throw new InsufficientScope('access token missing requested scope', missing.join(' '));
}
}
await next();
Expand Down
9 changes: 9 additions & 0 deletions lib/helpers/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ class InvalidScope extends OIDCProviderError {
}
}

class InsufficientScope extends OIDCProviderError {
constructor(description, scope, detail) {
super(403, 'insufficient_scope');
Error.captureStackTrace(this, this.constructor);
Object.assign(this, { scope, error_description: description, error_detail: detail });
}
}

class InvalidRequest extends OIDCProviderError {
constructor(description, code = 400, detail) {
super(code, 'invalid_request');
Expand Down Expand Up @@ -168,6 +176,7 @@ module.exports.InvalidGrant = InvalidGrant;
module.exports.InvalidRedirectUri = InvalidRedirectUri;
module.exports.InvalidRequest = InvalidRequest;
module.exports.InvalidScope = InvalidScope;
module.exports.InsufficientScope = InsufficientScope;
module.exports.InvalidToken = InvalidToken;
module.exports.OIDCProviderError = OIDCProviderError;
module.exports.SessionNotFound = SessionNotFound;
Expand Down
17 changes: 16 additions & 1 deletion test/userinfo/userinfo.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,23 @@ describe('userinfo /me', () => {
.expect(this.failWith(400, 'invalid_request', 'no access token provided'));
});

it('validates the openid scope is present', async function () {
const at = await new this.provider.AccessToken({
client: await this.provider.Client.find('client'),
}).save();
sinon.stub(this.provider.Client, 'find').callsFake(async () => undefined);
return this.agent.get('/me')
.auth(at, { type: 'bearer' })
.expect(() => {
this.provider.Client.find.restore();
})
.expect(this.failWith(403, 'insufficient_scope', 'access token missing openid scope', 'openid'));
});

it('validates a client is still valid for a found token', async function () {
const at = await new this.provider.AccessToken({
client: await this.provider.Client.find('client'),
scope: 'openid',
}).save();
sinon.stub(this.provider.Client, 'find').callsFake(async () => undefined);
return this.agent.get('/me')
Expand All @@ -93,6 +107,7 @@ describe('userinfo /me', () => {
it('validates an account still valid for a found token', async function () {
const at = await new this.provider.AccessToken({
client: await this.provider.Client.find('client'),
scope: 'openid',
accountId: 'notfound',
}).save();
return this.agent.get('/me')
Expand All @@ -119,6 +134,6 @@ describe('userinfo /me', () => {
scope: 'openid profile',
})
.auth(this.access_token, { type: 'bearer' })
.expect(this.failWith(400, 'invalid_scope', 'access token missing requested scope', 'profile'));
.expect(this.failWith(403, 'insufficient_scope', 'access token missing requested scope', 'profile'));
});
});

0 comments on commit ba8a8f0

Please sign in to comment.