Skip to content

Commit

Permalink
fix: assert refresh_token grant ID Token sub to equal previous
Browse files Browse the repository at this point in the history
  • Loading branch information
panva committed Mar 21, 2020
1 parent 35929d7 commit 23f3f9f
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 0 deletions.
11 changes: 11 additions & 0 deletions lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -988,6 +988,17 @@ module.exports = (issuer, aadIssValidation = false) => class Client extends Base
if (tokenset.id_token) {
await this.decryptIdToken(tokenset);
await this.validateIdToken(tokenset, null, 'token', null);

if (refreshToken instanceof TokenSet && refreshToken.id_token) {
const expectedSub = refreshToken.claims().sub;
const actualSub = tokenset.claims().sub;
if (actualSub !== expectedSub) {
throw new RPError({
printf: ['sub mismatch, expected %s, got: %s', expectedSub, actualSub],
jwt: tokenset.id_token,
});
}
}
}

return tokenset;
Expand Down
61 changes: 61 additions & 0 deletions test/client/client_instance.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -873,11 +873,13 @@ describe('Client', () => {
describe('#refresh', function () {
before(function () {
const issuer = new Issuer({
issuer: 'https://op.example.com',
token_endpoint: 'https://op.example.com/token',
});
this.client = new issuer.Client({
client_id: 'identifier',
client_secret: 'secure',
id_token_signed_response_alg: 'HS256',
});
});

Expand Down Expand Up @@ -931,6 +933,65 @@ describe('Client', () => {
});
});

it('passes ID Token validations when ID Token is returned', function () {
nock('https://op.example.com')
.post('/token') // to make sure filteringRequestBody works
.reply(200, {
access_token: 'present',
refresh_token: 'refreshValue',
id_token: jose.JWT.sign({
sub: 'foo',
}, this.client.client_secret, {
issuer: this.client.issuer.issuer,
audience: this.client.client_id,
expiresIn: '5m',
}),
});

return this.client.refresh(new TokenSet({
access_token: 'present',
refresh_token: 'refreshValue',
id_token: jose.JWT.sign({
sub: 'foo',
}, this.client.client_secret, {
issuer: this.client.issuer.issuer,
audience: this.client.client_id,
expiresIn: '6m',
}),
}));
});

it('rejects when returned ID Token sub does not match the one passed in', function () {
nock('https://op.example.com')
.post('/token') // to make sure filteringRequestBody works
.reply(200, {
access_token: 'present',
refresh_token: 'refreshValue',
id_token: jose.JWT.sign({
sub: 'bar',
}, this.client.client_secret, {
issuer: this.client.issuer.issuer,
audience: this.client.client_id,
expiresIn: '5m',
}),
});

return this.client.refresh(new TokenSet({
access_token: 'present',
refresh_token: 'refreshValue',
id_token: jose.JWT.sign({
sub: 'foo',
}, this.client.client_secret, {
issuer: this.client.issuer.issuer,
audience: this.client.client_id,
expiresIn: '5m',
}),
}))
.then(fail, (error) => {
expect(error).to.have.property('message', 'sub mismatch, expected foo, got: bar');
});
});

it('rejects when passed a TokenSet not containing refresh_token', function () {
return this.client.refresh(new TokenSet({
access_token: 'present',
Expand Down

0 comments on commit 23f3f9f

Please sign in to comment.