diff --git a/lib/actions/authorization/decode_request.js b/lib/actions/authorization/decode_request.js index 36e027b1b..fd4cf6444 100644 --- a/lib/actions/authorization/decode_request.js +++ b/lib/actions/authorization/decode_request.js @@ -83,6 +83,7 @@ module.exports = (provider, PARAM_LIST) => { issuer: payload.iss ? client.clientId : undefined, audience: payload.aud ? provider.issuer : undefined, clockTolerance: conf('clockTolerance'), + ignoreAzp: true, }; await JWT.verify(params.request, client.keystore, opts); wasSignedOrEncrypted = true; diff --git a/lib/helpers/jwt.js b/lib/helpers/jwt.js index a6bf8d623..c089b8afb 100644 --- a/lib/helpers/jwt.js +++ b/lib/helpers/jwt.js @@ -13,10 +13,17 @@ const { stringify, parse } = JSON; const format = 'compact'; const typ = 'JWT'; -function verifyAudience({ aud }, expected) { - const target = Array.isArray(aud) ? aud : [aud]; - const match = target.some(actual => actual === expected); - assert(match, `jwt audience missing ${expected}`); +function verifyAudience({ aud, azp }, expected, checkAzp) { + if (Array.isArray(aud)) { + const match = aud.some(actual => actual === expected); + assert(match, `jwt audience missing ${expected}`); + if (checkAzp) { + assert(azp, 'jwt missing azp claim'); + assert.strictEqual(azp, expected, 'invalid jwt azp'); + } + } else { + assert.strictEqual(aud, expected, `jwt audience missing ${expected}`); + } } class JWT { @@ -57,7 +64,8 @@ class JWT { } static assertPayload(payload, { - clockTolerance = 0, audience, ignoreExpiration, ignoreIssued, ignoreNotBefore, issuer, jti, + clockTolerance = 0, audience, ignoreExpiration, + ignoreAzp, ignoreIssued, ignoreNotBefore, issuer, jti, } = {}) { const timestamp = epochTime(); @@ -86,6 +94,7 @@ class JWT { verifyAudience( payload, audience, + !ignoreAzp, ); } diff --git a/lib/models/id_token.js b/lib/models/id_token.js index 6ac7d260f..1b8c7ca00 100644 --- a/lib/models/id_token.js +++ b/lib/models/id_token.js @@ -150,6 +150,7 @@ module.exports = function getIdToken(provider) { const alg = client.idTokenSignedResponseAlg; const opts = { ignoreExpiration: true, + audience: client.clientId, issuer: provider.issuer, clockTolerance: instance(provider).configuration('clockTolerance'), }; @@ -161,11 +162,13 @@ module.exports = function getIdToken(provider) { keyOrStore = client.keystore; } - if (keyOrStore !== undefined) return JWT.verify(jwt, keyOrStore, opts); + if (keyOrStore !== undefined) { + return JWT.verify(jwt, keyOrStore, opts); + } - const decode = JWT.decode(jwt); - JWT.assertPayload(decode.payload, opts); - return decode; + const decoded = JWT.decode(jwt); + JWT.assertPayload(decoded.payload, opts); + return decoded; } }; }; diff --git a/lib/shared/token_jwt_auth.js b/lib/shared/token_jwt_auth.js index de9e05859..657d02422 100644 --- a/lib/shared/token_jwt_auth.js +++ b/lib/shared/token_jwt_auth.js @@ -52,6 +52,7 @@ module.exports = function getTokenJwtAuth(provider, endpoint) { audience: endpointUri, issuer: ctx.oidc.client.clientId, clockTolerance: instance(provider).configuration('clockTolerance'), + ignoreAzp: true, }); } catch (err) { throw new InvalidClientAuth(err.message); diff --git a/test/jwt/jsonwebtoken.test.js b/test/jwt/jsonwebtoken.test.js index c2ee267fe..d7f4ada58 100644 --- a/test/jwt/jsonwebtoken.test.js +++ b/test/jwt/jsonwebtoken.test.js @@ -260,9 +260,10 @@ describe('JSON Web Token (JWT) RFC7519 implementation', () => { const key = keystore.get({ kty: 'oct' }); return JWT.sign({ data: true }, key, 'HS256', { audience: ['client', 'momma'], + authorizedParty: 'client', }) .then(jwt => JWT.verify(jwt, key, { - audience: 'momma', + audience: 'client', })); }); @@ -272,7 +273,7 @@ describe('JSON Web Token (JWT) RFC7519 implementation', () => { audience: 'client', }) .then(jwt => JWT.verify(jwt, key, { - audience: ['pappa'], + audience: 'pappa', })) .then((valid) => { expect(valid).not.to.be.ok; diff --git a/test/storage/jwt.test.js b/test/storage/jwt.test.js index 661ce15cb..89e04c102 100644 --- a/test/storage/jwt.test.js +++ b/test/storage/jwt.test.js @@ -27,7 +27,7 @@ if (FORMAT === 'jwt') { const redirectUri = 'https://rp.example.com/cb'; const codeChallenge = 'codeChallenge'; const codeChallengeMethod = 'codeChallengeMethod'; - const aud = [clientId, 'foo']; + const aud = ['foo', 'bar']; const gty = 'foo'; const error = 'access_denied'; const errorDescription = 'resource owner denied access';