From ba0858b9e63c99c13afeb9a01c61dff475d75d99 Mon Sep 17 00:00:00 2001 From: Poovamraj T T Date: Wed, 8 Nov 2023 22:25:19 +0100 Subject: [PATCH] Remove unused ID token verification (#799) --- package.json | 4 - src/jwt/__tests__/base64.spec.js | 31 -- src/jwt/__tests__/jwks.json | 12 - src/jwt/__tests__/jwt.spec.js | 662 ------------------------------- src/jwt/__tests__/pubkey.pem | 9 - src/jwt/base64.ts | 32 -- src/jwt/index.ts | 3 - src/jwt/rsa-verifier.ts | 78 ---- src/jwt/signatureVerifier.ts | 116 ------ src/jwt/validator.ts | 235 ----------- yarn.lock | 34 +- 11 files changed, 14 insertions(+), 1202 deletions(-) delete mode 100644 src/jwt/__tests__/base64.spec.js delete mode 100644 src/jwt/__tests__/jwks.json delete mode 100644 src/jwt/__tests__/jwt.spec.js delete mode 100644 src/jwt/__tests__/pubkey.pem delete mode 100644 src/jwt/base64.ts delete mode 100644 src/jwt/index.ts delete mode 100644 src/jwt/rsa-verifier.ts delete mode 100644 src/jwt/signatureVerifier.ts delete mode 100644 src/jwt/validator.ts diff --git a/package.json b/package.json index ce2f8f81..46ffdefe 100644 --- a/package.json +++ b/package.json @@ -84,8 +84,6 @@ "@react-native-community/eslint-config": "^3.0.2", "@release-it/conventional-changelog": "^5.0.0", "@types/base-64": "^1.0.0", - "@types/crypto-js": "^4.0.0", - "@types/jsbn": "^1.1.0", "@types/react-test-renderer": "^18.0.0", "@types/jest": "^29.5.0", "@types/react": "~17.0.21", @@ -119,8 +117,6 @@ }, "dependencies": { "base-64": "^0.1.0", - "crypto-js": "^4.0.0", - "jsbn": "^1.1.0", "jwt-decode": "^3.1.2", "prettier-quick": "^0.0.5", "prop-types": "^15.8.1", diff --git a/src/jwt/__tests__/base64.spec.js b/src/jwt/__tests__/base64.spec.js deleted file mode 100644 index af409bef..00000000 --- a/src/jwt/__tests__/base64.spec.js +++ /dev/null @@ -1,31 +0,0 @@ -import * as base64 from '../base64'; - -describe('helpers base64 url', function() { - describe('padding', function() { - it('does not add to multiple of 4', function() { - expect(base64.padding('')).toBe(''); - expect(base64.padding('abcd')).toBe('abcd'); - }); - it('adds to non multiple of 4', function() { - expect(base64.padding('a')).toBe('a==='); - expect(base64.padding('ab')).toBe('ab=='); - expect(base64.padding('abc')).toBe('abc='); - expect(base64.padding('abced')).toBe('abced==='); - }); - it('does not change already padded value', function() { - const padded = base64.padding('abc'); - expect(padded).toBe('abc='); - const again = base64.padding(padded); - expect(again).toBe('abc='); - }); - }); - - describe('decoding to hex', function() { - it('should convert base64 input into hex output', function() { - expect(base64.decodeToHEX('AQAB')).toBe('010001'); - expect(base64.decodeToHEX('uGbXWiK3dQTyCbX5')).toBe( - 'b866d75a22b77504f209b5f9', - ); - }); - }); -}); diff --git a/src/jwt/__tests__/jwks.json b/src/jwt/__tests__/jwks.json deleted file mode 100644 index 42e1df18..00000000 --- a/src/jwt/__tests__/jwks.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "keys": [ - { - "use": "sig", - "alg": "RS256", - "kty": "RSA", - "e": "AQAB", - "kid": "1234", - "n": "uGbXWiK3dQTyCbX5xdE4yCuYp0AF2d15Qq1JSXT_lx8CEcXb9RbDddl8jGDv-spi5qPa8qEHiK7FwV2KpRE983wGPnYsAm9BxLFb4YrLYcDFOIGULuk2FtrPS512Qea1bXASuvYXEpQNpGbnTGVsWXI9C-yjHztqyL2h8P6mlThPY9E9ue2fCqdgixfTFIF9Dm4SLHbphUS2iw7w1JgT69s7of9-I9l5lsJ9cozf1rxrXX4V1u_SotUuNB3Fp8oB4C1fLBEhSlMcUJirz1E8AziMCxS-VrRPDM-zfvpIJg3JljAh3PJHDiLu902v9w-Iplu1WyoB2aPfitxEhRN0Yw" - } - ] -} diff --git a/src/jwt/__tests__/jwt.spec.js b/src/jwt/__tests__/jwt.spec.js deleted file mode 100644 index b5fbdeae..00000000 --- a/src/jwt/__tests__/jwt.spec.js +++ /dev/null @@ -1,662 +0,0 @@ -import verifyToken from '../index'; -import * as signatureVerifier from '../signatureVerifier'; -const jwtDecoder = require('jwt-decode'); -import * as fs from 'fs'; -import * as path from 'path'; -import fetchMock from 'fetch-mock'; - -describe('id token verification tests', () => { - describe('token signature verification', () => { - beforeEach(() => { - fetchMock.restore(); - }); - - it('resolves when no idToken present', async () => { - await expect(verify(undefined)).resolves.toBeUndefined(); - }); - - it('fails when token not signed with RS256 or HS256', async () => { - const badAlgToken = - 'eyJhbGciOiJub25lIn0.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC05OTkiXSwiZXhwIjoxODEzMS42MjAzNTMxNTk3MjQsImlhdCI6LTE4MTMxLjYyMDM1MzE1OTcyNCwibm9uY2UiOiJhMWIyYzNkNGU1IiwiYXpwIjoidG9rZW5zLXRlc3QtMTIzIiwiYXV0aF90aW1lIjoxNTY2NTcxOTk4LjUxM30.'; - - await expect(verify(badAlgToken)).rejects.toHaveProperty( - 'name', - 'a0.idtoken.invalid_algorithm', - ); - }); - - it('fails when unable to decode token', async () => { - const testJwt = "won't work"; - - await expect(verify(testJwt)).rejects.toHaveProperty( - 'name', - 'a0.idtoken.token_decoding_error', - ); - }); - - it('fails when discovery endpoint returns error', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiIsImtpZCI6IjEyMzQifQ.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTcwMjAyOTMxLCJpYXQiOjE1NzAwMzAxMzEsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NzAxMTY1MzAuNzk2fQ.Xad-J3PtImY3z--Gvj-H61tH18mCGQUUBkcug-CB5ehkjd56PXrA-AJHZK7OLryB_uj6sFKVn-V8Wr6t3KW7_Fd2n-__Ca2h6PtgIrjceZlHAQY4SgAk9tPmeeTOhs6KyXDeW0Ot0j3CP9p7nWxgCGMu_H5J5ZgJSVUVlffVpaIMEGiFZ_r71PLPtuTL3GsDwtICG_5xuqoR2YBLSpNuuc46t15i94E3JC1UXGryRfxVbeHg3x5DF9nf6eVkMHRdi-CdNQn2iD0G9OmxxELh-40pecbyUxLv4NfTHmbxOdvWRK00N8sgkElnPnoWXb5pacxLShFsBTJdXIsyqF_onA'; - - fetchMock.get( - `${BASE_EXPECTATIONS.issuer}.well-known/openid-configuration`, - 500, - ); - - const result = verify(testJwt); - - expect(result).rejects.toHaveProperty( - 'name', - 'a0.idtoken.key_retrieval_error', - ); - expect(result).rejects.toHaveProperty( - 'message', - 'Could not find a public key for Key ID (kid) "1234"', - ); - }); - - it('fails when jwk set does not contain the expected key id', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiIsImtpZCI6IjEyMzQifQ.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTcwMjAyOTMxLCJpYXQiOjE1NzAwMzAxMzEsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NzAxMTY1MzAuNzk2fQ.Xad-J3PtImY3z--Gvj-H61tH18mCGQUUBkcug-CB5ehkjd56PXrA-AJHZK7OLryB_uj6sFKVn-V8Wr6t3KW7_Fd2n-__Ca2h6PtgIrjceZlHAQY4SgAk9tPmeeTOhs6KyXDeW0Ot0j3CP9p7nWxgCGMu_H5J5ZgJSVUVlffVpaIMEGiFZ_r71PLPtuTL3GsDwtICG_5xuqoR2YBLSpNuuc46t15i94E3JC1UXGryRfxVbeHg3x5DF9nf6eVkMHRdi-CdNQn2iD0G9OmxxELh-40pecbyUxLv4NfTHmbxOdvWRK00N8sgkElnPnoWXb5pacxLShFsBTJdXIsyqF_onA'; - - const jwks = getExpectedJwks(); - jwks.keys[0].kid = '4321'; - - setupFetchMock({jwks}); - const result = verify(testJwt); - - expect(result).rejects.toHaveProperty( - 'name', - 'a0.idtoken.key_retrieval_error', - ); - expect(result).rejects.toHaveProperty( - 'message', - 'Could not find a public key for Key ID (kid) "1234"', - ); - }); - - it('fails when public key is invalid and cannot be reconstructed', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiIsImtpZCI6IjEyMzQifQ.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTcwMjAzMjgxLCJpYXQiOjE1NzAwMzA0ODEsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NzAxMTY4ODAuNjk0fQ.ZNPsQq_U8NGyi5WFNgvuT0QlxfGFS9w6YIHWiF4dnwz_Zf3mv3gh4wybDR8vaLCE8ONTXvT9V_rW6oqNHSvEwa0nvPy2Vi3gVAvSfusoiYhkuQG_6SuqbeOrNJ1cejGzqw_iv2s6yEyN3B9wp0TCuIKL5jLPttaRi6ouGCbYeReANecaLOVZstrO4GhlY0NwtT4j5Dn1tDYavWxi1DZBisxBvMEFA6N0aQa51gJm6RYtUjBTo50j1xG5b7TIF4edjjT85FYQgrwEzA7Ss3HpnrYXEEvHn4nCsc585T3GKQuF21Nli-qGgQ3MywPOOqqiCSvL254Cp88Gt3xDS1hnqg'; - const jwks = getExpectedJwks(); - jwks.keys[0].n = 'bad-modulus'; - - setupFetchMock({jwks}); - - const result = verify(testJwt); - expect(result).rejects.toHaveProperty( - 'name', - 'a0.idtoken.invalid_signature', - ); - expect(result).rejects.toHaveProperty( - 'message', - 'Invalid ID token signature', - ); - }); - - it('fails when signature is invalid', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiIsImtpZCI6IjEyMzQifQ.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTcwMjAzMjgxLCJpYXQiOjE1NzAwMzA0ODEsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NzAxMTY4ODAuNjk0fQ.invalid-signature'; - - setupFetchMock(); - - await expect(verify(testJwt)).rejects.toHaveProperty( - 'name', - 'a0.idtoken.invalid_signature', - ); - }); - - it('passes verification and does not verify signature when signed with HS256', async () => { - const testJwt = - 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.9G6ScTYnBDLzI_qGNTXLi33J604tE4oagNVR7vnfaL0'; - - setupFetchMock(); - - await verify(testJwt); - - expect(fetchMock.called()).toBe(false); - }); - - it('passes verification with valid token signed with RS256', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiIsImtpZCI6IjEyMzQifQ.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.ObH7oG3NsGaxWnB8rzbLOgAD2I0fr9dyZC81YUrbju3RwC3lRAxqJkbesiSdGKry9OamIhKYwUGpPK0wrBaRJo8UjDjICkhM6lGP23plysemxhDnFK1qjj-NaUaW1yKg14v2lVpQl7glW9LIhFDhpqIf4bILA2wt9-z8Uvi31ETZvGb8PDY2bEvjXR-69-yLuoTNT2skP9loKfz6hHDMQCTWrGA61BMMjkZBLo9UotD9BzN8V7bLrFFT25v6q9N83mWaGLsHntzPIl3EYPOwX0NbE0lXKar59TUqtaTB3uNFHbGjIYi8wuuIp4PV9arpE3YrjWOOmrMurD1KpIyQrQ'; - - setupFetchMock(); - - await expect(verify(testJwt)).resolves.toBeUndefined(); - }); - }); - - describe('token claims verification', () => { - // default clock skew in seconds - const DEFAULT_LEEWAY = 60; - let sigMock; - - beforeEach(() => { - sigMock = jest.spyOn(signatureVerifier, 'verifySignature'); - }); - - afterEach(() => { - sigMock.mockRestore(); - }); - - it('fails when "iss" is missing', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.B4PGlucyy-fJ4v5NNK2hntvjAf5m8dJf84WttwVnzV0ZlfPbYUSJm7Vc1ys7iMqXAQzAl2I8bDf2qhtLjaLpDKAH9JUvowUpCL7Bgjd7AEc1Te_IUwwxlpCupgseOEL2nrY8enP6On7BO7BBpngmVwnD1DvuA4lNoaaFyWUopha5Dxd5jw64wMqP4lz13C6Kqs8mINZkkw-NgE8DvWszaXeyPaowy-QpfXmPBnw75YLZlGcjr-WQsWQV7rUezq4Tl_11uPivR-fNcEWdG1mAtsnQnB_zJJKaHYlE0g4fey_6H9FKmCvcNkpBGo9ylbitb7jIuExbFEvEd2r_4wKl0g'; - - setupSignatureMock(testJwt); - - await expect(verify(testJwt)).rejects.toHaveProperty( - 'name', - 'a0.idtoken.missing_issuer_claim', - ); - }); - - it('fails when "iss" is not a string', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOjQyLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.Fv0hopDxEJ_84UibQLCD9fnyr8jJ-oBg6GyJcSQZHmXgmqDYDMh3KTVUz88EvwmGv6yxoSo5PnkbzgM8Qqcjs9X1K9nvC09y6RIhQ1WzJOAOFRoUMp4yaAjrWfgQOpR21AOTpCQhLaW4EX2hNU5hjYwR1r7jhFb1AjeeOo-VCfKbwn_-GAKH5FF7fjmEdgCPcAX5DD7WIYCKE_oz5X4BbT4G9hEPEE-GxigcHrFZuCaYGwjUFDpT6zWB6_wJLRVQLl42Xjiwl8GasjnTSyF_uPidMANeYj1nA-JzPVYtuvjBr9EsDWylTlpyZe6pq6osZXNIRfv7oPVx9hLeKS8l1w'; - - setupSignatureMock(testJwt); - - await expect(verify(testJwt)).rejects.toHaveProperty( - 'name', - 'a0.idtoken.missing_issuer_claim', - ); - }); - - it('fails when "iss" is invalid', async () => { - const testJwt = - ' eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJzb21ldGhpbmctZWxzZSIsInN1YiI6ImF1dGgwfDEyMzQ1Njc4OSIsImF1ZCI6WyJ0b2tlbnMtdGVzdC0xMjMiLCJleHRlcm5hbC10ZXN0LTEyMyJdLCJleHAiOjE1Njc0ODY4MDAsImlhdCI6MTU2NzMxNDAwMCwibm9uY2UiOiJhNTl2azU5MiIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU2NzMxNDAwMH0.lHFHyg1ei3hK2vB7X1xB9nqksAEnxtv2KKpE_Gih6RezTruF9uZu1PAZTEwxhfj2UrQxwLqCb-t6wyVnxVpCsymSCq9JIiCVgg_cYV38siMs38N9y26BrVeyifj_VOP9Om_vI_hHjOzhi8WmysK2KKAQnn0skKAkq8epY4axCX3NkRaEIMhhTaITYia3GbJ5Qki8WDD9UVucUVOhgSZBV5p1dL39FKgc9k1MOVZJG-zAd_r5GsUIRk-xUwNX0WYwCR9sC2G-FjJTvlFph_4vksponoUWJ-LPTLM0RwGgmEUPhhnIG23UjsNwpnElY4gWfIL0hsO98-5DpGjn8Ejr0w'; - - setupSignatureMock(testJwt); - - await expect(verify(testJwt)).rejects.toHaveProperty( - 'name', - 'a0.idtoken.invalid_issuer_claim', - ); - }); - - it('fails when "sub" is missing', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.fDR9NSbbt75w9nzhL-eBfGjOp16HP2vfnO6m_Oav0xrmmgyYsBZSLOPd2C0O46bp6_2hKjeOUhnwYwjocsdXI4hvfQkyACERtneCkwHwSZPZK-1h6vgGF7b_7ILUywEcgo7F6e1qgFTM93Prqk63cCP53KgOBPyx02y0rqkhUOApCWRVBFrfP92tXvFN7E2phmpf9G68PPjwnEvvQtYOGjvFkaWSja7MKT98f7OxgbenBI_mAZy9LmOqUl3SKJOBe5Fibs1snI0l4nzrgQ1GNxVwyfHOdyq-srdGe8rlFx5kdhWh313EOzWxxGTg4RhGY7Tiz1QWago0VQ5yOt0w8A'; - - setupSignatureMock(testJwt); - - await expect(verify(testJwt)).rejects.toHaveProperty( - 'name', - 'a0.idtoken.missing_subject_claim', - ); - }); - - it('fails when "sub" is not a string', async () => { - const testJwt = - ' eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOjQyLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.tWst0Fp-1vN2urV1APmWLpqc2DjyGGeCBuHB7aDd-RKKSx7u65NQrX9EBf9FLmYh763uNlYxjWkZq1Orf8P7pbf2NFjgl2yKHrBU7RadQESLj8TDc1VBQMiXKF4SchjWFo8Yq3we81clbxfWYrzapmpvtUTerfXRuQukJza6CHNSzl3VhgVodtJOPasS11zSQsE7DBtHXGwLZwZ83zuwN_IiaFgVu-jZH8yG1ot5ThZGOqFiW9WUbje9XLTp6w_lQedd16-ll50ExhmzCZAnjK7DweiiH3jWdxK-6m1DeLZMwMPGSpz6BgVYsLfuPmoIHU-3CdRXjSXmuBf-WdSGlg'; - - setupSignatureMock(testJwt); - - await expect(verify(testJwt)).rejects.toHaveProperty( - 'name', - 'a0.idtoken.missing_subject_claim', - ); - }); - - it('fails when "aud" is missing', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJleHAiOjE1Njc0ODY4MDAsImlhdCI6MTU2NzMxNDAwMCwibm9uY2UiOiJhNTl2azU5MiIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU2NzMxNDAwMH0.XM-IM9CIZ2cJpZZaKooMSmNgvwHPTse6kcIOPATgewRZxrDdCEjtPHmzmSuyDGy84vSR__DJS_kM2jWWwbkjB_PahXes210dpUqitRW3is9xV0-k0LkVwxmhHCM-e9sClbTbcs4zLv6WWFRq4UEU5DU6HhuHLQeeH0eO2Nv_tkvu-JdpmoepHPjW3ecMs0lhzXRT6_2o-ErTPdYt4W6yqpBG57HRIMzs9F72AWcPC6vhLY0IhMqXaq68Ma3jnEPIXUmv52bll0PuQVBqKd-eDH_jD0ZHFUCkwbfWPrkhJz5Q5qLzSzUjnrWKA3KgP4_Z1KfHY2-nQA2ynMgNFSn_eA'; - - setupSignatureMock(testJwt); - - await expect(verify(testJwt)).rejects.toHaveProperty( - 'name', - 'a0.idtoken.missing_audience_claim', - ); - }); - - it('fails when "aud" is not a string or an array', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOjQyLCJleHAiOjE1Njc0ODY4MDAsImlhdCI6MTU2NzMxNDAwMCwibm9uY2UiOiJhNTl2azU5MiIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU2NzMxNDAwMH0.W0dV1Eddi_mr1o9XtGeznhgngXqUnGROoiRaNNpHgLwmyzQTndas7oZZuqnQRWd8W9eNgqjjM5YNP6UG3paEbSpwdwe32IzqhlKqL5Cqf0eMdYTsh1yXxdl7UB3wGccG_2IvdzlgSY-Zs_uZiBMQqksWSwq4V9eF9fCpmwv9yLMf9dkF9VGgR8TSi1-pOVj693rw0ZFFRa5zsB6fgBgkNjP2tqR7zErkDqWwyy2K_N8ArNSfWC3WmYqx7V4yI4pARciQeg36KofHWyyZkZPSIF6DhkxLxeMlc0fx2NC6iBaiqGSe19nJbR1ETuouY7ozMbP42qfm5Yegv-dS_sPswA'; - - setupSignatureMock(testJwt); - - await expect(verify(testJwt)).rejects.toHaveProperty( - 'name', - 'a0.idtoken.missing_audience_claim', - ); - }); - - it('fails when "aud" is a string and does not contain the client ID', async () => { - const testJwt = - ' eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOiJleHRlcm5hbC10ZXN0LTEyMyIsImV4cCI6MTU2NzQ4NjgwMCwiaWF0IjoxNTY3MzE0MDAwLCJub25jZSI6ImE1OXZrNTkyIiwiYXpwIjoidG9rZW5zLXRlc3QtMTIzIiwiYXV0aF90aW1lIjoxNTY3MzE0MDAwfQ.SxeNIhm8reywgtSSkZ6jCpbZ8KyC09couFjpcrJFktAYKmJZnGQkv0gQLNUuGejORvysznOlhfO2nkF10yT6pKBiye9xZ8TstWQBorDKHL-74n6ZAxjPg1F0vHNokZq0zpPkwV-gKIFY6aPw3vyZTxzR6CMyoJdwc19A0RXPzPt6T7csQeqX0lzGEqqeIbU4VI5XM5RG1VvN82CgTlOQXlFZrKhyJx_xwslyWWDzx7tpPNid1wusvfznTGxoWO2wUBCyW6EhmyHp2euFi1gdJqHQVbrydutPtQ-FGQEwyWACNN8kBWqQ7UEbqimg6C0NTGrRkkKkJ79DmiW7aULHZQ'; - - setupSignatureMock(testJwt); - - await expect(verify(testJwt)).rejects.toHaveProperty( - 'name', - 'a0.idtoken.invalid_audience_claim', - ); - }); - - it('fails when "aud" is an array and does not contain the client ID', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.EAzP2R_Y8eu1hpJmluV_gjD5WF_y0bSOhtL-GsLTXKHdNmvE9nYsBjemGfNcl_xQVzcpakMD6dL7qtElvT2TuiJQhz0n5uB1DW-DVvWg301fYMqtCE1FlCJJJw7qkKi5WgqPWLkp3QYBuHvC0mPTYkRM5nbHXU3QymJ39koFTR7eq10VeKu9k0dUzwc1Oy_9SnL-mbtUn_0Nx0RFB-0BRkPLdbK1yhnNIUGEUSOJRKpoJz8dBRGyzSZSINxoWOMcclJT2O_o45DZnX8jZpct_XHc6G4_qGJFU3RNUfFoDoyjtK0aU_OFYCYmY41CDYyPj2LKAnhJ2pUdFrz7lxzzEw'; - - setupSignatureMock(testJwt); - - await expect(verify(testJwt)).rejects.toHaveProperty( - 'name', - 'a0.idtoken.invalid_audience_claim', - ); - }); - - it('fails when "exp" is missing', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiaWF0IjoxNTY3MzE0MDAwLCJub25jZSI6ImE1OXZrNTkyIiwiYXpwIjoidG9rZW5zLXRlc3QtMTIzIiwiYXV0aF90aW1lIjoxNTY3MzE0MDAwfQ.b6saYAZCnCSzpVO0nrAUKVSC1n3GoqUfwrjOXG5gVxda0oFohpYJe68QwzsTmS4fOm7JtbN1FqjVRN6S4i-BnH-XGnciGOMFF4EfaOzsgo7DCrrLrjfx6rmqW8UPYalbfJTQL8mXYnLOxzMGP3DEXNlk-41GSZoFujwTAIqYjrV_Y3MUGYmzcVxdL_h2psLm_p07knMLCm7Cuo8znzKrU4PtuaLflvzorg57S4BD79oLv4uv0_dmhwPUgJDvqWeicR5Qry4aX2L5BT6V-nBWAcu3qVZDymSKcjtTebxszxY1siyA7BQe88ZmgP1bW1KXtMk_fOGsgWHFdu_AH77yow'; - - setupSignatureMock(testJwt); - - await expect(verify(testJwt)).rejects.toHaveProperty( - 'name', - 'a0.idtoken.missing_expires_at_claim', - ); - }); - - it('fails when "exp" is not a number', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoidGhpcyBpcyBhIHN0cmluZyIsImlhdCI6MTU2NzMxNDAwMCwibm9uY2UiOiJhNTl2azU5MiIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU2NzMxNDAwMH0.Yqj36cxYA_G7kT-uMjf_8fg-iC37E5SfskQzeLaxdErm9AG-efq6hmpN8YH1054T5a337flE7nLHmFC-Kzdf5cn4lYkTgujP3hph4WObwec95WskyyaFrm9GGdU2FjO6kT1pFy_GNW3wlmCXnyu2BRESCv_AMy7kF5Xo5hZyt1hUyW7loMxwPg2e8RficC-mm-6C70mCWSxKCSeuK088qMJDnp94iHMLfYLP38nEQmh1cxhnWgdibA9conk3SjyDTMOPtnS7tR6i6TDCky63ivvqJEdifJ7E3c2QTXoFXydfAKy7LWrCBx-iPhDyeTZQvAMz4LUzgEC1FGUDJaPqPw'; - - setupSignatureMock(testJwt); - - await expect(verify(testJwt)).rejects.toHaveProperty( - 'name', - 'a0.idtoken.missing_expires_at_claim', - ); - }); - - it('fails when "exp" is in the past and outside of default leeway', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3MzE0MDAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.uDn-4wtiigGddUw2kis_QyfDE3w75rWvu9NolMgD3b7l4_fedhQOk-z_mYID588ZXpnpLRKKiD5I2IFsXl7Qcc10rx1LIZxNqdzyc3VrgFf677x7fFZ4guR2WalH-zdJEluruMRdCIFQczIjXnGKPHGQ8gPH1LRozv43dl-bO2viX6MU4pTgNq3GIsU4ureyHrx1o9JSqF4b_RzuYvVWVVX7ABC2csMJP_ocVbEIQjUBhp1V7VcQY-Zgq0prk_HvY13g8FxK4KvSza637ZWAfonn599SKuy22PeMJqDfd64SbunWrt-mKBz9PHeAo9t4LJPLsAqSd3IQ2aJTsnqJRA'; - - setupSignatureMock(testJwt); - - // token exp claim of 2019-09-01T05:00:00.000Z - const clock = new Date('2019-09-01T05:00:00.000Z'); - clock.setSeconds(clock.getSeconds() + (DEFAULT_LEEWAY + 1)); - - const overrides = { - _clock: clock, - }; - - await expect(verify(testJwt, overrides)).rejects.toHaveProperty( - 'name', - 'a0.idtoken.invalid_expires_at_claim', - ); - }); - - it('succeeds when "exp" is in the past but within the default leeway', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3MzE0MDAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.uDn-4wtiigGddUw2kis_QyfDE3w75rWvu9NolMgD3b7l4_fedhQOk-z_mYID588ZXpnpLRKKiD5I2IFsXl7Qcc10rx1LIZxNqdzyc3VrgFf677x7fFZ4guR2WalH-zdJEluruMRdCIFQczIjXnGKPHGQ8gPH1LRozv43dl-bO2viX6MU4pTgNq3GIsU4ureyHrx1o9JSqF4b_RzuYvVWVVX7ABC2csMJP_ocVbEIQjUBhp1V7VcQY-Zgq0prk_HvY13g8FxK4KvSza637ZWAfonn599SKuy22PeMJqDfd64SbunWrt-mKBz9PHeAo9t4LJPLsAqSd3IQ2aJTsnqJRA'; - - setupSignatureMock(testJwt); - - // token exp claim of 2019-09-01T05:00:00.000Z - const clock = new Date('2019-09-01T05:00:00.000Z'); - clock.setSeconds(clock.getSeconds() + (DEFAULT_LEEWAY - 1)); - - const overrides = { - _clock: clock, - }; - - await expect(verify(testJwt, overrides)).resolves.toBeUndefined(); - }); - - it('fails when "exp" is in the past and outside of custom leeway', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3MzE0MDAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.uDn-4wtiigGddUw2kis_QyfDE3w75rWvu9NolMgD3b7l4_fedhQOk-z_mYID588ZXpnpLRKKiD5I2IFsXl7Qcc10rx1LIZxNqdzyc3VrgFf677x7fFZ4guR2WalH-zdJEluruMRdCIFQczIjXnGKPHGQ8gPH1LRozv43dl-bO2viX6MU4pTgNq3GIsU4ureyHrx1o9JSqF4b_RzuYvVWVVX7ABC2csMJP_ocVbEIQjUBhp1V7VcQY-Zgq0prk_HvY13g8FxK4KvSza637ZWAfonn599SKuy22PeMJqDfd64SbunWrt-mKBz9PHeAo9t4LJPLsAqSd3IQ2aJTsnqJRA'; - - const leeway = 120; - setupSignatureMock(testJwt); - - // token exp claim of 2019-09-01T05:00:00.000Z - const clock = new Date('2019-09-01T05:00:00.000Z'); - clock.setSeconds(clock.getSeconds() + (leeway + 1)); - - const overrides = { - leeway, - _clock: clock, - }; - - await expect(verify(testJwt, overrides)).rejects.toHaveProperty( - 'name', - 'a0.idtoken.invalid_expires_at_claim', - ); - }); - - it('succeeds when "exp" is in the past but within the custom leeway', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3MzE0MDAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.uDn-4wtiigGddUw2kis_QyfDE3w75rWvu9NolMgD3b7l4_fedhQOk-z_mYID588ZXpnpLRKKiD5I2IFsXl7Qcc10rx1LIZxNqdzyc3VrgFf677x7fFZ4guR2WalH-zdJEluruMRdCIFQczIjXnGKPHGQ8gPH1LRozv43dl-bO2viX6MU4pTgNq3GIsU4ureyHrx1o9JSqF4b_RzuYvVWVVX7ABC2csMJP_ocVbEIQjUBhp1V7VcQY-Zgq0prk_HvY13g8FxK4KvSza637ZWAfonn599SKuy22PeMJqDfd64SbunWrt-mKBz9PHeAo9t4LJPLsAqSd3IQ2aJTsnqJRA'; - - const leeway = 120; - - setupSignatureMock(testJwt); - - // token exp claim of 2019-09-01T05:00:00.000Z - const clock = new Date('2019-09-01T05:00:00.000Z'); - clock.setSeconds(clock.getSeconds() + (leeway - 1)); - - const overrides = { - leeway, - _clock: clock, - }; - - await expect(verify(testJwt, overrides)).resolves.toBeUndefined(); - }); - - it('fails when "iat" is missing', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJub25jZSI6ImE1OXZrNTkyIiwiYXpwIjoidG9rZW5zLXRlc3QtMTIzIiwiYXV0aF90aW1lIjoxNTY3MzE0MDAwfQ.SJDgK8W9Y8stMtE9LG2OzHzXzbIDCXg8lRhKyOim4rRXbkg3k0on7gCzN-sy2d5z5TQ-lQzbY3V4z-so3ltVDUYd_8RjmUiKgNK_95UsxfTDM2BlBEQ6USMVl3ojC5jcTBhg5MF16ZBEn94IjIGC9Uks9GPseM-JrtUPx4Uj5VvsBtmeKxLc3rSGt7rYC4JU65Oa-O5pFYRSCbNzRFNHRlmnb5b2uPHxoVLjrJAT0FhlXcsNgfz65MlbXBgAyz7xjCEhw_tTpvptaCwPTeG0mgBYlGQ7Sl3xHJzgG4jLbA7Pvvfcx0MpBPHUZxADh1FFQnf2nHB0ppddiDfOq2mHNA'; - - setupSignatureMock(testJwt); - - await expect(verify(testJwt)).rejects.toHaveProperty( - 'name', - 'a0.idtoken.missing_issued_at_claim', - ); - }); - - it('fails when "iat" is not a number', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOiJ0aGlzIGlzIGEgc3RyaW5nIiwibm9uY2UiOiJhNTl2azU5MiIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU2NzMxNDAwMH0.XVGkRmJU5L3behnzAII-Y4kbguVr2MF9TSdQxc_PibjxdE5VK_bhwcbo956aKbiptIq99HhoInoR6ZtF3MYNHOR0KlmxqrwmA9aBXNG8sDQu74MFFB_nqY5Ct4Ia93ZjGqlx5cRw-2k4dExw0QqbzAItOtkiZH8CgCCf4AgwOhVHqkzqcUGqh96rfnv7b8qaWi5S_lmXfMeHXFbeg7wqr1lC3p1wAg2OvON95jQrw13dMc7j6HFO95u6ICdotYROCT6ScfnYyxFmNTlGpLvudPu1dCCxd96sEL20XKpTEEnjXyk_wd5KmtzWNFYs-3aGnhIMC37KlBG8W2iysaUnbA'; - - setupSignatureMock(testJwt); - - await expect(verify(testJwt)).rejects.toHaveProperty( - 'name', - 'a0.idtoken.missing_issued_at_claim', - ); - }); - - it('fails when "nonce" sent on authentication request but missing from token claims', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU2NzMxNDAwMH0.ZRYK4s72pKXJUSadByPp_MNyuaACmPCyj9RaIfxuTTLXE45YJ0toLK6XjjDv_861E_fRmEKMthnJAmHcKXiDWGb73l3iDtD7noWBOo3KJO2cwkM1uYNpG1kbNkg6WDvgGlVsC7buxr8dbL8fI2e0g53Jl48lE9Ohi5Z_7iRmRoVAx5HE60UDfEqFeAKZyu5VsAahp9q3PwhLfaJVDobtAzWP0LcRA3x8FOA0ZdBBNpvRmeBRugU2GQTSDLSMtGzgi5xXUwXly7pr5bX-lIYICU1Q9R5n-8uYlEaFuiaYTqzxY0fmSzzGeFkwrj7b0yTQ2OwAFVT3MWCSbvjKsy-JWQ'; - - setupSignatureMock(testJwt); - - await expect(verify(testJwt)).rejects.toHaveProperty( - 'name', - 'a0.idtoken.missing_nonce_claim', - ); - }); - - it('fails when "nonce" sent on authentication request but "nonce" claim is invalid', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiMDAwOTk5IiwiYXpwIjoidG9rZW5zLXRlc3QtMTIzIiwiYXV0aF90aW1lIjoxNTY3MzE0MDAwfQ.n4jIX01mNucMs92F8IZtKJeCvgUYPwrrOsaZX91fnzVkDC5tAqi4HLRGHjtUJe1PwmIijJk63FskeuApVPfxfAbITL1KBVDHiin2RVeDSAl5lhSnsSYW-k5MfzXx11MJxhS_VD5zvOgbWmuRYUHlc1zh48YyJZQE-OaEFvxGyyEM7Zhgzfz4D5_kjd2qV890WsXGs_GadyzxATfP59XENnPzMo3VLXyBC4cQ0e7rzBIqquBKo9-MT6rhy_qSwMrZJhyzSzE5gTtMd2Od9YgPUtLznBt34rBD1uJaSs_a4s1Ox3h4jTCm85xWFabGx3kz7xkD33nCiMKQ_FSy1d-toQ'; - - setupSignatureMock(testJwt); - - await expect( - verify(testJwt, { - nonce: 'nonce-on-authrequest', - }), - ).rejects.toHaveProperty('name', 'a0.idtoken.invalid_nonce_claim'); - }); - - it('fails when "organization" sent on authentication request but "org_id" is missing from token claims', async () => { - const testJwt = - 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU2NzMxNDAwMCwibm9uY2UiOiJhNTl2azU5MiJ9.nfnx9bMtDJa4EuGMMps5-Yh_Ma-in6k9bzVEcQT648g'; - - setupSignatureMock(testJwt); - - await expect( - verify(testJwt, { - orgId: 'org-test', - }), - ).rejects.toHaveProperty('name', 'a0.idtoken.missing_org_id_claim'); - }); - - it('fails when "organization" sent on authentication request but "org_id" claim is invalid', async () => { - const testJwt = - 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm9yZ19pZCI6InRlc3Qtb3JnIiwiYXpwIjoidG9rZW5zLXRlc3QtMTIzIiwiYXV0aF90aW1lIjoxNTY3MzE0MDAwLCJub25jZSI6ImE1OXZrNTkyIn0.mP50Orcdc-n-tUm_MqKKbpiC0hi9Gh2j_eUaIOHo8qA'; - - setupSignatureMock(testJwt); - - await expect( - verify(testJwt, { - orgId: 'nope', - }), - ).rejects.toHaveProperty('name', 'a0.idtoken.invalid_org_id_claim'); - }); - - it('succeeds when "organization" sent on authentication request matches the received "org_id" claim', async () => { - const testJwt = - 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm9yZ19pZCI6InRlc3Qtb3JnIiwiYXpwIjoidG9rZW5zLXRlc3QtMTIzIiwiYXV0aF90aW1lIjoxNTY3MzE0MDAwLCJub25jZSI6ImE1OXZrNTkyIn0.mP50Orcdc-n-tUm_MqKKbpiC0hi9Gh2j_eUaIOHo8qA'; - - setupSignatureMock(testJwt); - - await expect( - verify(testJwt, { - orgId: 'test-org', - }), - ).resolves.toBeUndefined(); - }); - - it('fails when "aud" is array with multiple items, and "azp" is missing', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.ab1cp7PTjoRNQwlJ6-ENjmFmxuoKtDHOzgB_3YiCxsIVN3WqSgv-l-AonhvnTg8qV1YXArYXlRxkE7IeXVTgB6981cHhaOywQJgZ_8NeNN7eMOyTVlcmQBP-1Ar2-Hgb8RKjNVFb-rMOGqhn2B9yu_E5amSGyPzHrATQ1wcfO-XSuzYdCbTokurEA2LsE8Sr4eMMUlRLNLjBSy-aLmIyggFOKkvw1qCiJq28tBfI24p0Al0NLfyS3EbimJIqk6JIMyOh40sdlxi9wrVt6iUjbxhN6xYA2JYBXHjMmF8l4xWPL4I4aX5g-5vpj_w10A0kepvFDhw_EbKpR-XqZ-GW3Q'; - - setupSignatureMock(testJwt); - - await expect(verify(testJwt)).rejects.toHaveProperty( - 'name', - 'a0.idtoken.missing_authorized_party_claim', - ); - }); - - it('fails when "aud" is array with multiple items, and "azp" is not a string', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOjQyLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.SliF71jOX9JsGeUPCySf3ucY_tGr3uh183cbcUN9ze3qRiOAc5bi7vdsBtODtlVJgsx0Elt0JrISTJ8SoNkpA4SxrjFpxSsfzPBwQtJrlg7pqflgBH7g6zKGVGRs2Z0jxZaCvXQvRuUZRZwFIncZ2zTFIDI3X5xLeJAGRGWaInOvLLlumGzWzfNLUG_G5uHZQW6sRgyIw9qrdqEWXO6sGjOBG9Au6jIo2IH0I53-UujAnNHWeJRPsM5xw2bHPteIde1xn4N0w26BlZ4GEQifVQDFw3ukah35SQ-ENMMS58Siu-sysF5F3oxdwVaMidyYgrD2VUN_iXIaMPwA2i0M5Q'; - - setupSignatureMock(testJwt); - - await expect(verify(testJwt)).rejects.toHaveProperty( - 'name', - 'a0.idtoken.missing_authorized_party_claim', - ); - }); - - it('fails when "aud" is array with multiple items, and "azp" does not match client ID', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJleHRlcm5hbC10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU2NzMxNDAwMH0.GLuChuSum2S6h79rfRbJrJfe_7Fw_D6RHXj9zrAhixoNLMyBosO2GBPsOgoaLTDMonJzCyqskjan-w-SJ5nw7fUmDkWfPVjXcS0x5pt72j0dgfLMu6eOFIA9jWHWN4hsN3XKJktZ9202AohI8fXO5BYQ-jMi0HWQaiUj3f6wITHEN6fTydLo_t24hriExkO1670AgzM22BVTfb-JJlrs32t6ffY77zrF5ahIg_h4ROgrcf_3LejF7ZnubHbpJ-wX-byxW9YXT5tN_JjD5EP6jC37s9iL8ArGEZtBzHVfCO0kqlaH-9PVZXgz8SjMSJ8iA2fXXN0L35ySdzida3hhzw'; - - setupSignatureMock(testJwt); - - await expect(verify(testJwt)).rejects.toHaveProperty( - 'name', - 'a0.idtoken.invalid_authorized_party_claim', - ); - }); - - it('fails when "max_age" was sent on the authentication request but "auth_time" is missing', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMifQ.Gb36qNHgQgac1fXh9AHX7ZMroymT0j4TjNol3ZirbIOyxuHV4OxCbGcoAAxC8Zt_dIc3DH9SX3QUIwTkE3DsFxS-VJ58R2d9RbXJl5p8pO1sJNFjo59njLKbiBxVil4z8PUsw77c_4f2QtKn6LHzhGqL9CS84LUCgNPPBsBHYyNRJDwIauPrrLyOsZAS3dWlZiUDBFurSYe0Y-O6d8zF_uKOcTD8A2E3SQQlZJQ12T94IprQ9V0tbbWI8VSGQ23JghR62QwZC-rBOF9pQMcLLCNRLFTTF9sXqZuS9XRv7PZ6rRjaonHDWn8WqGjSleWSycPsvwvjjSUVR8Z3iDBZig'; - - setupSignatureMock(testJwt); - - await expect( - verify(testJwt, { - maxAge: 200, - }), - ).rejects.toHaveProperty( - 'name', - 'a0.idtoken.missing_authorization_time_claim', - ); - }); - - it('fails when "max_age" was sent on the authentication request but "auth_time" is not a number', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOiJ0aGlzIGlzIGEgc3RyaW5nIn0.Af6-W_IPinZ4Sy7_vJfvjez3NWKmGzCglRqYwEkkGWRXr9rJDMB7mrpba992g9cbkCVo_ywQmfHxBaxr-fss-vzMVlqfhduZM_29aGwk56thzMYPiHlcpMUxGa6lb6BrzadQEsu2WL2vstueQbvo6H4d_k-NrkYD6UN4WMjbKZaC9VE2qae_o_UAIjPCseHWW645vtQgVDDOXf8kjjmpJfQaU_ZWu2L1HMZiCbb70ndXcJ-5qj0YbJH9YrmDbYe1RwXR-GKPnTF4TYlhsYpGXMfCFTpVu_KVCSOT1U4kVeNpzoB0bBRtbfEyOaK3aSpusHJ_ECJBoAdWrdAh-k41Jg'; - - setupSignatureMock(testJwt); - - await expect( - verify(testJwt, { - maxAge: 200, - }), - ).rejects.toHaveProperty( - 'name', - 'a0.idtoken.missing_authorization_time_claim', - ); - }); - - it('fails when "max_age" was sent on the authentication request but "auth_time" is outside of default leeway', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.AbSYZ_Tu0-ZelCRPuu9jOd9y1M19yIlk8bjSQDVVgAekRZLdRA_T_gi_JeWyFysKZVpRcHC1YJhTH4YH8CCMRTwviq3woIsLmdUecjydyZkHcUlhHXj2DbC15cyELalPNe3T9eZ4ySwk9qRJSOkjBAgXAT0a7M6rwri6QHnL0WxTLX4us4rGu8Ui3kuf1WaZH9DNoeWYs1N3xUnOwTkRKaqXnuKjnwSVmsuwxFSlnIPJOiMUUZksiaBq_OUvOkB-dEG7OFiDX9XWj1m62yBHkvZHun8LBr9VW3mt1IrcBdbbtzjWwfn6ioK2c4dbtPFhuYohXsmRDaSekP63Dmlw3A'; - - setupSignatureMock(testJwt); - - const maxAge = 120; - // token auth_time claim of 2019-09-01T05:00:00.000Z - const clock = new Date('2019-09-01T05:00:00.000Z'); - clock.setSeconds(clock.getSeconds() + maxAge + (DEFAULT_LEEWAY + 1)); - - await expect( - verify(testJwt, { - maxAge, - _clock: clock, - }), - ).rejects.toHaveProperty( - 'name', - 'a0.idtoken.invalid_authorization_time_claim', - ); - }); - - it('succeeds when "max_age" was sent on the authentication request and "auth_time" is within default leeway', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.AbSYZ_Tu0-ZelCRPuu9jOd9y1M19yIlk8bjSQDVVgAekRZLdRA_T_gi_JeWyFysKZVpRcHC1YJhTH4YH8CCMRTwviq3woIsLmdUecjydyZkHcUlhHXj2DbC15cyELalPNe3T9eZ4ySwk9qRJSOkjBAgXAT0a7M6rwri6QHnL0WxTLX4us4rGu8Ui3kuf1WaZH9DNoeWYs1N3xUnOwTkRKaqXnuKjnwSVmsuwxFSlnIPJOiMUUZksiaBq_OUvOkB-dEG7OFiDX9XWj1m62yBHkvZHun8LBr9VW3mt1IrcBdbbtzjWwfn6ioK2c4dbtPFhuYohXsmRDaSekP63Dmlw3A'; - - setupSignatureMock(testJwt); - - const maxAge = 120; - // token auth_time claim of 2019-09-01T05:00:00.000Z - const clock = new Date('2019-09-01T05:00:00.000Z'); - clock.setSeconds(clock.getSeconds() + maxAge + (DEFAULT_LEEWAY - 1)); - - await expect( - verify(testJwt, { - maxAge, - _clock: clock, - }), - ).resolves.toBeUndefined(); - }); - - it('fails when "max_age" was sent on the authentication request but "auth_time" is outside of custom leeway', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.AbSYZ_Tu0-ZelCRPuu9jOd9y1M19yIlk8bjSQDVVgAekRZLdRA_T_gi_JeWyFysKZVpRcHC1YJhTH4YH8CCMRTwviq3woIsLmdUecjydyZkHcUlhHXj2DbC15cyELalPNe3T9eZ4ySwk9qRJSOkjBAgXAT0a7M6rwri6QHnL0WxTLX4us4rGu8Ui3kuf1WaZH9DNoeWYs1N3xUnOwTkRKaqXnuKjnwSVmsuwxFSlnIPJOiMUUZksiaBq_OUvOkB-dEG7OFiDX9XWj1m62yBHkvZHun8LBr9VW3mt1IrcBdbbtzjWwfn6ioK2c4dbtPFhuYohXsmRDaSekP63Dmlw3A'; - - setupSignatureMock(testJwt); - - const maxAge = 120; - const leeway = 15; - - // token auth_time claim of 2019-09-01T05:00:00.000Z - const clock = new Date('2019-09-01T05:00:00.000Z'); - clock.setSeconds(clock.getSeconds() + maxAge + (leeway + 1)); - - await expect( - verify(testJwt, { - maxAge, - _clock: clock, - leeway, - }), - ).rejects.toHaveProperty( - 'name', - 'a0.idtoken.invalid_authorization_time_claim', - ); - }); - - it('succeeds when "max_age" was sent on the authentication request and "auth_time" is within custom leeway', async () => { - const testJwt = - 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.AbSYZ_Tu0-ZelCRPuu9jOd9y1M19yIlk8bjSQDVVgAekRZLdRA_T_gi_JeWyFysKZVpRcHC1YJhTH4YH8CCMRTwviq3woIsLmdUecjydyZkHcUlhHXj2DbC15cyELalPNe3T9eZ4ySwk9qRJSOkjBAgXAT0a7M6rwri6QHnL0WxTLX4us4rGu8Ui3kuf1WaZH9DNoeWYs1N3xUnOwTkRKaqXnuKjnwSVmsuwxFSlnIPJOiMUUZksiaBq_OUvOkB-dEG7OFiDX9XWj1m62yBHkvZHun8LBr9VW3mt1IrcBdbbtzjWwfn6ioK2c4dbtPFhuYohXsmRDaSekP63Dmlw3A'; - - setupSignatureMock(testJwt); - - const maxAge = 120; - const leeway = 15; - - // token auth_time claim of 2019-09-01T05:00:00.000Z - const clock = new Date('2019-09-01T05:00:00.000Z'); - clock.setSeconds(clock.getSeconds() + maxAge + (leeway - 1)); - - await expect( - verify(testJwt, { - maxAge, - _clock: clock, - leeway, - }), - ).resolves.toBeUndefined(); - }); - - it('succeeds with valid token claims using default clock', async () => { - const yesterday = Math.round(Date.now() / 1000 - 3600 * 24); - const tomorrow = Math.round(Date.now() / 1000 + 3600 * 24); - - const decodedJwt = { - iss: `https://${BASE_EXPECTATIONS.domain}/`, - sub: 'auth0|123456789', - aud: BASE_EXPECTATIONS.clientId, - exp: tomorrow, - iat: yesterday, - nonce: BASE_EXPECTATIONS.nonce, - }; - - sigMock.mockImplementation(() => Promise.resolve(decodedJwt)); - - await expect( - verify('jwt-string-mocked', { - _clock: undefined, - }), - ).resolves.toBeUndefined(); - }); - - const setupSignatureMock = jwt => { - sigMock.mockImplementation(() => Promise.resolve(jwtDecoder(jwt))); - }; - }); - - const BASE_EXPECTATIONS = { - clientId: 'tokens-test-123', - clientIdAlt: 'external-test-123', - domain: 'tokens-test.auth0.com', - issuer: 'https://tokens-test.auth0.com/', - nonce: 'a59vk592', - }; - - const verify = (idToken, optionsOverrides = {}) => { - const optionsDefaults = { - domain: BASE_EXPECTATIONS.domain, - clientId: BASE_EXPECTATIONS.clientId, - scope: 'openid profile email', - nonce: BASE_EXPECTATIONS.nonce, - _clock: new Date('2019-09-02T05:00:00.000Z'), - }; - - const options = Object.assign({}, optionsDefaults, optionsOverrides); - return verifyToken(idToken, options); - }; - - const setupFetchMock = ({ - domain = BASE_EXPECTATIONS.domain, - jwks = getExpectedJwks(), - } = {}) => { - const expectedDiscoveryUri = `https://${domain}/.well-known/openid-configuration`; - const expectedJwksUri = `https://${domain}/.well-known/jwks.json`; - - fetchMock.get(expectedDiscoveryUri, {jwks_uri: expectedJwksUri}); - fetchMock.get(expectedJwksUri, jwks); - }; - - const getExpectedJwks = () => { - return JSON.parse( - fs.readFileSync(path.resolve(__dirname, './jwks.json'), 'utf8'), - ); - }; -}); diff --git a/src/jwt/__tests__/pubkey.pem b/src/jwt/__tests__/pubkey.pem deleted file mode 100644 index fefd2f26..00000000 --- a/src/jwt/__tests__/pubkey.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuGbXWiK3dQTyCbX5xdE4 -yCuYp0AF2d15Qq1JSXT/lx8CEcXb9RbDddl8jGDv+spi5qPa8qEHiK7FwV2KpRE9 -83wGPnYsAm9BxLFb4YrLYcDFOIGULuk2FtrPS512Qea1bXASuvYXEpQNpGbnTGVs -WXI9C+yjHztqyL2h8P6mlThPY9E9ue2fCqdgixfTFIF9Dm4SLHbphUS2iw7w1JgT -69s7of9+I9l5lsJ9cozf1rxrXX4V1u/SotUuNB3Fp8oB4C1fLBEhSlMcUJirz1E8 -AziMCxS+VrRPDM+zfvpIJg3JljAh3PJHDiLu902v9w+Iplu1WyoB2aPfitxEhRN0 -YwIDAQAB ------END PUBLIC KEY----- \ No newline at end of file diff --git a/src/jwt/base64.ts b/src/jwt/base64.ts deleted file mode 100644 index e07af64b..00000000 --- a/src/jwt/base64.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Borrowed from IDToken-verifier package - * https://github.com/auth0/idtoken-verifier/blob/master/src/helpers/base64.js - */ -import base64 from 'base64-js'; - -export function padding(str: string) { - const paddingLength = 4; - const mod = str.length % paddingLength; - const pad = paddingLength - mod; - - if (mod === 0) { - return str; - } - - return str + new Array(1 + pad).join('='); -} - -function byteArrayToHex(raw: Uint8Array) { - let HEX = ''; - - for (let i = 0; i < raw.length; i++) { - const _hex = raw[i].toString(16); - HEX += _hex.length === 2 ? _hex : '0' + _hex; - } - - return HEX; -} - -export function decodeToHEX(str: string) { - return byteArrayToHex(base64.toByteArray(padding(str))); -} diff --git a/src/jwt/index.ts b/src/jwt/index.ts deleted file mode 100644 index b716e779..00000000 --- a/src/jwt/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import {verifyToken} from './validator'; - -export default verifyToken; diff --git a/src/jwt/rsa-verifier.ts b/src/jwt/rsa-verifier.ts deleted file mode 100644 index e258a260..00000000 --- a/src/jwt/rsa-verifier.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* -Based on the work of Tom Wu -http://www-cs-students.stanford.edu/~tjw/jsbn/ -http://www-cs-students.stanford.edu/~tjw/jsbn/LICENSE -*/ - -import {BigInteger} from 'jsbn'; -import SHA256 from 'crypto-js/sha256'; - -const digestInfoHead: {[key: string]: string} = { - sha256: '3031300d060960864801650304020105000420', -}; - -const digestAlgs: {[key: string]: any} = { - sha256: SHA256, -}; - -class RSAVerifier { - private n: BigInteger; - private e = 0; - - constructor(modulus: string, exp: string) { - if (modulus && modulus.length > 0 && exp && exp.length > 0) { - this.n = new BigInteger(modulus, 16); - this.e = parseInt(exp, 16); - } else { - throw new Error('Invalid key data'); - } - } - - verify(msg: string, encodedSignature: string) { - const decodedSignature = encodedSignature.replace( - /[^0-9a-f]|[\s\n]]/gi, - '', - ); - - const signature = new BigInteger(decodedSignature, 16); - if (signature.bitLength() > this.n.bitLength()) { - //Signature does not match with the key modulus. - return false; - } - - const decryptedSignature = signature.modPowInt(this.e, this.n); - const digest = decryptedSignature.toString(16).replace(/^1f+00/, ''); - - const digestInfo = getAlgorithmFromDigest(digest); - if (digestInfo === null) { - //Hashing algorithm is not found - return false; - } - - if (!digestAlgs.hasOwnProperty(digestInfo.alg)) { - //Hashing algorithm is not supported - return false; - } - - const msgHash = digestAlgs[digestInfo.alg](msg).toString(); - return digestInfo.hash === msgHash; - } -} -function getAlgorithmFromDigest( - hDigestInfo: string, -): {alg: string; hash: string} | null { - for (let algName in digestInfoHead) { - const head = digestInfoHead[algName]; - const len = head.length; - - if (hDigestInfo.substring(0, len) === head) { - return { - alg: algName, - hash: hDigestInfo.substring(len), - }; - } - } - return null; -} - -export default RSAVerifier; diff --git a/src/jwt/signatureVerifier.ts b/src/jwt/signatureVerifier.ts deleted file mode 100644 index 68fe3f59..00000000 --- a/src/jwt/signatureVerifier.ts +++ /dev/null @@ -1,116 +0,0 @@ -import AuthError from '../auth/authError'; -import RSAVerifier from './rsa-verifier'; -import * as base64 from './base64'; -import {JwtHeader, JwtPayload} from 'jwt-decode'; -const jwtDecoder = require('jwt-decode'); - -const ALLOWED_ALGORITHMS = ['RS256', 'HS256']; - -/** - * Verifies that an ID token is signed with a supported algorithm (HS256 or RS256), and verifies the signature - * if signed with RS256. Note that this function is specific to the internals of this SDK, and not supported for general use. - * @param {String} idToken the ID token - * @param {Object} options required to verify an ID token's signature - * @param {String} [options.domain] the Auth0 domain of the token's issuer - * @returns {Promise} A promise that resolves to the decoded payload of the ID token, or rejects if the verification fails. - */ -export const verifySignature = (idToken: string, options: {domain: string}) => { - let header: JwtHeader & {kid: string}; - let payload: JwtPayload; - - try { - header = jwtDecoder(idToken, {header: true}); - payload = jwtDecoder(idToken); - } catch (err) { - return Promise.reject( - idTokenError({ - error: 'token_decoding_error', - desc: 'ID token could not be decoded', - }), - ); - } - - const alg = header.alg; - - if (!alg || !ALLOWED_ALGORITHMS.includes(alg)) { - return Promise.reject( - idTokenError({ - error: 'invalid_algorithm', - desc: `Signature algorithm of "${alg}" is not supported. Expected "RS256" or "HS256".`, - }), - ); - } - - // HS256 tokens require private key, which cannot be stored securely in public clients. - // Since the ID token exchange is done via CODE with PKCE flow, we can skip signature verification in this case. - if (alg === 'HS256') { - return Promise.resolve(payload); - } - - return getJwk(options.domain, header.kid).then(jwk => { - const rsaVerifier = rsaVerifierForKey(jwk); - const encodedParts = idToken.split('.'); - const headerAndPayload = encodedParts[0] + '.' + encodedParts[1]; - const signature = base64.decodeToHEX(encodedParts[2]); - if (rsaVerifier.verify(headerAndPayload, signature)) { - return Promise.resolve(payload); - } - return Promise.reject( - idTokenError({ - error: 'invalid_signature', - desc: 'Invalid ID token signature', - }), - ); - }); -}; - -const rsaVerifierForKey = (jwk: {n: string; e: string}) => { - const modulus = base64.decodeToHEX(jwk.n); - const exponent = base64.decodeToHEX(jwk.e); - return new RSAVerifier(modulus, exponent); -}; - -const getJwk = (domain: string, kid: string) => { - return getJwksUri(domain) - .then(uri => fetchJson(uri)) - .then(jwk => { - const keys = jwk.keys; - const key = keys - .filter( - (k: any) => k.use === 'sig' && k.kty === 'RSA' && k.kid && k.n && k.e, - ) - .find((k: any) => k.kid === kid); - if (!key) { - throw new Error('Key not present'); - } - return Promise.resolve(key); - }) - .catch(err => { - return Promise.reject( - idTokenError({ - error: 'key_retrieval_error', - desc: `Could not find a public key for Key ID (kid) "${kid}"`, - }), - ); - }); -}; - -const getJwksUri = (domain: string) => { - return fetch(`https://${domain}/.well-known/openid-configuration`) - .then(resp => resp.json()) - .then(openIdConfig => openIdConfig.jwks_uri); -}; - -const fetchJson = (uri: string) => { - return fetch(uri).then(resp => resp.json()); -}; - -const idTokenError = (err: {error: string; desc: string}) => { - return new AuthError({ - json: { - error: `a0.idtoken.${err.error}`, - error_description: err.desc, - }, - status: 0, - }); -}; diff --git a/src/jwt/validator.ts b/src/jwt/validator.ts deleted file mode 100644 index c180d512..00000000 --- a/src/jwt/validator.ts +++ /dev/null @@ -1,235 +0,0 @@ -import AuthError from '../auth/authError'; -import { verifySignature } from './signatureVerifier'; - -type TokenVerificationOptions = { - domain: string; - clientId: string; - nonce?: string; - maxAge?: number; - scope?: string; - leeway?: number; - orgId?: string; - _clock?: Date; -}; - -// default clock skew, in seconds -const DEFAULT_LEEWAY = 60; - -/** - * Verifies an ID token according to the OIDC specification. Note that this function is specific to the internals of this SDK, - * and is not supported for general use. - * @param {String} idToken the string token to verify - * @param {Object}options the options required to run this verification - * @returns {Promise} A promise that resolves if the verification is successful, or will reject the promise if validation fails - */ -export const verifyToken = ( - idToken: string, - options: TokenVerificationOptions -) => { - if (typeof idToken !== 'string') { - return Promise.resolve(); - } - - return verifySignature(idToken, { domain: options.domain }) - .then((payload) => validateClaims(payload, options)) - .then(() => Promise.resolve()); -}; - -const validateClaims = (payload: any, opts: TokenVerificationOptions) => { - // Issuer - if (typeof payload.iss !== 'string') { - return Promise.reject( - idTokenError({ - error: 'missing_issuer_claim', - desc: 'Issuer (iss) claim must be a string present in the ID token', - }) - ); - } - - if (payload.iss !== 'https://' + opts.domain + '/') { - return Promise.reject( - idTokenError({ - error: 'invalid_issuer_claim', - desc: `Issuer (iss) claim mismatch in the ID token; expected "https://${opts.domain}/", found "${payload.iss}"`, - }) - ); - } - - // Subject - if (typeof payload.sub !== 'string') { - return Promise.reject( - idTokenError({ - error: 'missing_subject_claim', - desc: 'Subject (sub) claim must be a string present in the ID token', - }) - ); - } - - // Audience - if (!(typeof payload.aud === 'string' || Array.isArray(payload.aud))) { - return Promise.reject( - idTokenError({ - error: 'missing_audience_claim', - desc: 'Audience (aud) claim must be a string or array of strings present in the ID token', - }) - ); - } - - if (Array.isArray(payload.aud) && !payload.aud.includes(opts.clientId)) { - return Promise.reject( - idTokenError({ - error: 'invalid_audience_claim', - desc: `Audience (aud) claim mismatch in the ID token; expected "${ - opts.clientId - }" but was not one of "${payload.aud.join(', ')}"`, - }) - ); - } else if (typeof payload.aud === 'string' && payload.aud !== opts.clientId) { - return Promise.reject( - idTokenError({ - error: 'invalid_audience_claim', - desc: `Audience (aud) claim mismatch in the ID token; expected "${opts.clientId}" but found "${payload.aud}"`, - }) - ); - } - - //--Time validation (epoch)-- - const now = opts._clock - ? getEpochTimeInSeconds(opts._clock) - : getEpochTimeInSeconds(new Date()); - const leeway = typeof opts.leeway === 'number' ? opts.leeway : DEFAULT_LEEWAY; - - //Expires at - if (typeof payload.exp !== 'number') { - return Promise.reject( - idTokenError({ - error: 'missing_expires_at_claim', - desc: 'Expiration Time (exp) claim must be a number present in the ID token', - }) - ); - } - - const expTime = payload.exp + leeway; - - if (now > expTime) { - return Promise.reject( - idTokenError({ - error: 'invalid_expires_at_claim', - desc: `Expiration Time (exp) claim error in the ID token; current time "${now}" is after expiration time "${expTime}"`, - }) - ); - } - - //Issued at - if (typeof payload.iat !== 'number') { - return Promise.reject( - idTokenError({ - error: 'missing_issued_at_claim', - desc: 'Issued At (iat) claim must be a number present in the ID token', - }) - ); - } - - //Nonce - if (opts.nonce) { - if (typeof payload.nonce !== 'string') { - return Promise.reject( - idTokenError({ - error: 'missing_nonce_claim', - desc: 'Nonce (nonce) claim must be a string present in the ID token', - }) - ); - } - if (payload.nonce !== opts.nonce) { - return Promise.reject( - idTokenError({ - error: 'invalid_nonce_claim', - desc: `Nonce (nonce) claim mismatch in the ID token; expected "${opts.nonce}", found "${payload.nonce}"`, - }) - ); - } - } - - // Organization ID - if (opts.orgId) { - if (typeof payload.org_id !== 'string') { - return Promise.reject( - idTokenError({ - error: 'missing_org_id_claim', - desc: 'Organization ID (org_id) claim must be a string present in the ID token', - }) - ); - } - if (payload.org_id !== opts.orgId) { - return Promise.reject( - idTokenError({ - error: 'invalid_org_id_claim', - desc: `Organization ID (org_id) claim mismatch in the ID token; expected "${opts.orgId}", found "${payload.org_id}"`, - }) - ); - } - } - - //Authorized party - if (Array.isArray(payload.aud) && payload.aud.length > 1) { - if (typeof payload.azp !== 'string') { - return Promise.reject( - idTokenError({ - error: 'missing_authorized_party_claim', - desc: 'Authorized Party (azp) claim must be a string present in the ID token when Audience (aud) claim has multiple values', - }) - ); - } - - if (payload.azp !== opts.clientId) { - return Promise.reject( - idTokenError({ - error: 'invalid_authorized_party_claim', - desc: `Authorized Party (azp) claim mismatch in the ID token; expected "${opts.clientId}", found "${payload.azp}"`, - }) - ); - } - } - - //Authentication time - if (typeof opts.maxAge === 'number') { - if (typeof payload.auth_time !== 'number') { - return Promise.reject( - idTokenError({ - error: 'missing_authorization_time_claim', - desc: 'Authentication Time (auth_time) claim must be a number present in the ID token when Max Age (max_age) is specified', - }) - ); - } - - const authValidUntil = payload.auth_time + opts.maxAge + leeway; - - if (now > authValidUntil) { - return Promise.reject( - idTokenError({ - error: 'invalid_authorization_time_claim', - desc: `Authentication Time (auth_time) claim in the ID token indicates that too much time has passed since the last end-user authentication. Current time "${now}" is after last auth time "${authValidUntil}"`, - }) - ); - } - } - - return Promise.resolve(); -}; - -const getEpochTimeInSeconds = (date: Date) => { - return Math.round(date.getTime() / 1000); -}; - -const idTokenError = ({ - error = 'verification_error', - desc = 'Error verifying ID token', -} = {}) => { - return new AuthError({ - json: { - error: `a0.idtoken.${error}`, - error_description: desc, - }, - status: 0, - }); -}; diff --git a/yarn.lock b/yarn.lock index 88450033..7f000f13 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2683,11 +2683,6 @@ resolved "https://registry.yarnpkg.com/@types/base-64/-/base-64-1.0.0.tgz#de9c6070ea457fbd65a1b5ebf13976b3ac0bdad0" integrity sha512-AvCJx/HrfYHmOQRFdVvgKMplXfzTUizmh0tz9GFTpDePWgCY4uoKll84zKlaRoeiYiCr7c9ZnqSTzkl0BUVD6g== -"@types/crypto-js@^4.0.0": - version "4.2.1" - resolved "https://registry.yarnpkg.com/@types/crypto-js/-/crypto-js-4.2.1.tgz#480edd76991a63050cb88db1a8840758c55a7135" - integrity sha512-FSPGd9+OcSok3RsM0UZ/9fcvMOXJ1ENE/ZbLfOPlBWj7BgXtEAM8VYfTtT760GiLbQIMoVozwVuisjvsVwqYWw== - "@types/http-cache-semantics@^4.0.1": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.2.tgz#abe102d06ccda1efdf0ed98c10ccf7f36a785a41" @@ -2720,11 +2715,6 @@ expect "^29.0.0" pretty-format "^29.0.0" -"@types/jsbn@^1.1.0": - version "1.2.33" - resolved "https://registry.yarnpkg.com/@types/jsbn/-/jsbn-1.2.33.tgz#470a4ff059f40fa6ca59838a8fa3f30c62a8c5ac" - integrity sha512-ZlLkHfu8xqqVFSbCe1FSPtAMUs7LKxk7TPskMb+sI5IbuzqyVqIEt9SVaQfFD2vrFcQunqKAmEBOuBEkoNLw4g== - "@types/jsdom@^20.0.0": version "20.0.1" resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-20.0.1.tgz#07c14bc19bd2f918c1929541cdaacae894744808" @@ -4558,11 +4548,6 @@ crypt@0.0.2, crypt@~0.0.1: resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== -crypto-js@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" - integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== - crypto-random-string@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" @@ -7379,6 +7364,20 @@ jest-haste-map@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== + dependencies: + "@jest/types" "^29.6.3" + "@types/graceful-fs" "^4.1.3" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + jest-worker "^29.7.0" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" jest-leak-detector@^29.7.0: version "29.7.0" @@ -7661,11 +7660,6 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -jsbn@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" - integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== - jsc-android@^250231.0.0: version "250231.0.0" resolved "https://registry.yarnpkg.com/jsc-android/-/jsc-android-250231.0.0.tgz#91720f8df382a108872fa4b3f558f33ba5e95262"