diff --git a/package.json b/package.json index 367e08b8..86bee76d 100644 --- a/package.json +++ b/package.json @@ -68,8 +68,8 @@ }, "homepage": "https://github.com/intuit/oauth-jsclient", "dependencies": { - "csrf": "^3.0.4", "atob": "2.1.2", + "csrf": "^3.0.4", "es6-promise": "^4.2.5", "events": "^3.0.0", "idtoken-verifier": "^1.2.0", @@ -83,6 +83,7 @@ }, "devDependencies": { "body-parser": "^1.15.2", + "btoa": "^1.2.1", "chai": "^4.1.2", "chai-as-promised": "^7.1.1", "chance": "^1.1.3", diff --git a/src/OAuthClient.js b/src/OAuthClient.js index 65386e38..07fa3861 100644 --- a/src/OAuthClient.js +++ b/src/OAuthClient.js @@ -470,8 +470,8 @@ OAuthClient.prototype.validateIdToken = function validateIdToken(params = {}) { // Step 1 : First check if the issuer is as mentioned in "issuer" if (id_token_payload.iss !== 'https://oauth.platform.intuit.com/op/v1') return false; - // Step 2 : check if the aud field in idToken is same as application's clientId - if (id_token_payload.aud !== this.clientId) return false; + // Step 2 : check if the aud field in idToken contains application's clientId + if (!id_token_payload.aud.find(audience => (audience === this.clientId))) return false; // Step 3 : ensure the timestamp has not elapsed if (id_token_payload.exp < Date.now() / 1000) return false; @@ -507,8 +507,7 @@ OAuthClient.prototype.getKeyFromJWKsURI = function getKeyFromJWKsURI(id_token, k return (new Promise(((resolve) => { resolve(this.loadResponse(request)); }))).then((response) => { - if (response.status !== '200') throw new Error('Could not reach JWK endpoint'); - + if (Number(response.status) !== 200) throw new Error('Could not reach JWK endpoint'); // Find the key by KID const responseBody = JSON.parse(response.body); const key = responseBody.keys.find(el => (el.kid === kid)); diff --git a/test/OAuthClientTest.js b/test/OAuthClientTest.js index 3b2ec659..a373a901 100644 --- a/test/OAuthClientTest.js +++ b/test/OAuthClientTest.js @@ -11,6 +11,8 @@ const nock = require('nock'); const sinon = require('sinon'); const chai = require('chai'); const chaiAsPromised = require('chai-as-promised'); +const btoa = require('btoa'); +const jwt = require('jsonwebtoken'); // eslint-disable-next-line no-unused-vars const getPem = require('rsa-pem-from-mod-exp'); @@ -23,11 +25,14 @@ const expectedTokenResponse = require('./mocks/tokenResponse.json'); const expectedUserInfo = require('./mocks/userInfo.json'); const expectedMakeAPICall = require('./mocks/makeAPICallResponse.json'); const expectedjwkResponseCall = require('./mocks/jwkResponse.json'); -const expectedvalidateIdToken = require('./mocks/validateIdToken.json'); const expectedOpenIDToken = require('./mocks/openID-token.json'); // var expectedErrorResponse = require('./mocks/errorResponse.json'); const expectedMigrationResponse = require('./mocks/authResponse.json'); +require.cache[require.resolve('rsa-pem-from-mod-exp')] = { + exports: sinon.stub().returns(3), +}; + const oauthClient = new OAuthClientTest({ clientId: 'clientID', clientSecret: 'clientSecret', @@ -338,9 +343,6 @@ describe('Tests for OAuthClient', () => { }); describe('getPublicKey', () => { - require.cache[require.resolve('rsa-pem-from-mod-exp')] = { - exports: sinon.mock().returns(3), - }; const pem = oauthClient.getPublicKey(3, 4); expect(pem).to.be.equal(3); }); @@ -373,7 +375,7 @@ describe('Validate Id Token ', () => { before(() => { nock('https://oauth.platform.intuit.com').persist() .get('/op/v1/jwks') - .reply(200, expectedjwkResponseCall, { + .reply(200, expectedjwkResponseCall.body, { 'content-type': 'application/json;charset=UTF-8', 'content-length': '264', connection: 'close', @@ -383,26 +385,41 @@ describe('Validate Id Token ', () => { 'cache-control': 'no-cache, no-store', pragma: 'no-cache', }); + sinon.stub(jwt, 'verify').returns(true); }); + const mockIdTokenPayload = { + sub: 'b053d994-07d5-468d-b7ee-22e349d2e739', + aud: ['clientID'], + realmid: '1108033471', + auth_time: 1462554475, + iss: 'https://oauth.platform.intuit.com/op/v1', + exp: Date.now() + 60000, + iat: 1462557728, + }; + + const tokenParts = expectedOpenIDToken.id_token.split('.'); + const encodedMockIdTokenPayload = tokenParts[0].concat('.', btoa(JSON.stringify(mockIdTokenPayload))); + const mockToken = Object.assign({}, expectedOpenIDToken, { id_token: encodedMockIdTokenPayload }); + it('validate id token returns error if id_token missing', async () => { delete oauthClient.getToken().id_token; await expect(oauthClient.validateIdToken()).to.be.rejectedWith(Error); }); it('Validate Id Token', () => { - oauthClient.getToken().setToken(expectedOpenIDToken); + oauthClient.getToken().setToken(mockToken); oauthClient.validateIdToken() .then((response) => { - expect(response).to.be.equal(expectedvalidateIdToken); + expect(response).to.be.equal(true); }); }); it('Validate Id Token alternative', () => { - oauthClient.setToken(expectedOpenIDToken); + oauthClient.setToken(mockToken); oauthClient.validateIdToken() .then((response) => { - expect(response).to.be.equal(expectedOpenIDToken); + expect(response).to.be.equal(true); }); }); }); diff --git a/test/mocks/jwkResponse.json b/test/mocks/jwkResponse.json index be557039..b01451f9 100644 --- a/test/mocks/jwkResponse.json +++ b/test/mocks/jwkResponse.json @@ -1,3 +1,3 @@ { - "body":"{\"keys\": [{\"kty\":\"sample_value_kty\",\"e\":\"sample_value_e\",\"use\":\"sample_value_use\",\"kid\":\"sample_value_kid\",\"alg\":\"sample_value_alg\",\"n\":\"sample_value_n\"}]}" -} \ No newline at end of file + "body":"{\"keys\": [{\"kty\":\"sample_value_kty\",\"e\":\"sample_value_e\",\"use\":\"sample_value_use\",\"kid\":\"r4p5SbL2qaFehFzhj8gI\",\"alg\":\"sample_value_alg\",\"n\":\"sample_value_n\"}]}" +}