diff --git a/__tests__/Auth0Client/getTokenSilently.test.ts b/__tests__/Auth0Client/getTokenSilently.test.ts index 1cdbb1c22..97c18d590 100644 --- a/__tests__/Auth0Client/getTokenSilently.test.ts +++ b/__tests__/Auth0Client/getTokenSilently.test.ts @@ -246,7 +246,7 @@ describe('Auth0Client', () => { ); }); - it('calls the token endpoint with the correct params when using refresh tokens', async () => { + it('calls the token endpoint with the correct params when using refresh tokens and not using useFormData', async () => { const auth0 = setup({ useRefreshTokens: true, useFormData: false @@ -274,7 +274,36 @@ describe('Auth0Client', () => { ); }); - it('calls the token endpoint with the correct params when passing redirect uri and using refresh tokens', async () => { + it('calls the token endpoint with the correct params when using refresh tokens', async () => { + const auth0 = setup({ + useRefreshTokens: true + }); + + await loginWithRedirect(auth0); + + mockFetch.mockReset(); + + await getTokenSilently(auth0, { + ignoreCache: true + }); + + assertPost( + 'https://auth0_domain/oauth/token', + { + redirect_uri: TEST_REDIRECT_URI, + client_id: TEST_CLIENT_ID, + grant_type: 'refresh_token', + refresh_token: TEST_REFRESH_TOKEN + }, + { + 'Auth0-Client': btoa(JSON.stringify(DEFAULT_AUTH0_CLIENT)) + }, + undefined, + false + ); + }); + + it('calls the token endpoint with the correct params when passing redirect uri, using refresh tokens and not using useFormData', async () => { const redirect_uri = 'https://custom'; const auth0 = setup({ @@ -305,7 +334,39 @@ describe('Auth0Client', () => { ); }); - it('calls the token endpoint with the correct params when not providing any redirect uri and using refresh tokens', async () => { + it('calls the token endpoint with the correct params when passing redirect uri and using refresh tokens', async () => { + const redirect_uri = 'https://custom'; + + const auth0 = setup({ + useRefreshTokens: true + }); + + await loginWithRedirect(auth0); + + mockFetch.mockReset(); + + await getTokenSilently(auth0, { + redirect_uri, + ignoreCache: true + }); + + assertPost( + 'https://auth0_domain/oauth/token', + { + redirect_uri, + client_id: TEST_CLIENT_ID, + grant_type: 'refresh_token', + refresh_token: TEST_REFRESH_TOKEN + }, + { + 'Auth0-Client': btoa(JSON.stringify(DEFAULT_AUTH0_CLIENT)) + }, + undefined, + false + ); + }); + + it('calls the token endpoint with the correct params when not providing any redirect uri, using refresh tokens and not using useFormData', async () => { const auth0 = setup({ useRefreshTokens: true, redirect_uri: null, @@ -335,6 +396,37 @@ describe('Auth0Client', () => { ); }); + it('calls the token endpoint with the correct params when not providing any redirect uri and using refresh tokens', async () => { + const auth0 = setup({ + useRefreshTokens: true, + redirect_uri: null + }); + + await loginWithRedirect(auth0); + + mockFetch.mockReset(); + + await getTokenSilently(auth0, { + redirect_uri: null, + ignoreCache: true + }); + + assertPost( + 'https://auth0_domain/oauth/token', + { + redirect_uri: 'http://localhost', + client_id: TEST_CLIENT_ID, + grant_type: 'refresh_token', + refresh_token: TEST_REFRESH_TOKEN + }, + { + 'Auth0-Client': btoa(JSON.stringify(DEFAULT_AUTH0_CLIENT)) + }, + undefined, + false + ); + }); + it('calls the token endpoint with the correct authorize timeout when using refresh tokens', async () => { const auth0 = setup({ useRefreshTokens: true @@ -562,7 +654,7 @@ describe('Auth0Client', () => { expect(mockFetch).toHaveBeenCalledTimes(1); }); - it('refreshes the token from a web worker', async () => { + it('refreshes the token from a web worker when not using useFormData', async () => { const auth0 = setup({ useRefreshTokens: true, useFormData: false @@ -591,7 +683,36 @@ describe('Auth0Client', () => { expect(access_token).toEqual(TEST_ACCESS_TOKEN); }); - it('refreshes the token without the worker', async () => { + it('refreshes the token from a web worker', async () => { + const auth0 = setup({ + useRefreshTokens: true + }); + + expect((auth0).worker).toBeDefined(); + + await loginWithRedirect(auth0); + + const access_token = await getTokenSilently(auth0, { ignoreCache: true }); + + assertPost( + 'https://auth0_domain/oauth/token', + { + client_id: TEST_CLIENT_ID, + grant_type: 'refresh_token', + redirect_uri: TEST_REDIRECT_URI, + refresh_token: TEST_REFRESH_TOKEN + }, + { + 'Auth0-Client': btoa(JSON.stringify(DEFAULT_AUTH0_CLIENT)) + }, + 1, + false + ); + + expect(access_token).toEqual(TEST_ACCESS_TOKEN); + }); + + it('refreshes the token without the worker when not using useFormData', async () => { const auth0 = setup({ useRefreshTokens: true, cacheLocation: 'localstorage', @@ -644,7 +765,62 @@ describe('Auth0Client', () => { expect(access_token).toEqual(TEST_ACCESS_TOKEN); }); - it('refreshes the token without the worker, when window.Worker is undefined', async () => { + it('refreshes the token without the worker', async () => { + const auth0 = setup({ + useRefreshTokens: true, + cacheLocation: 'localstorage' + }); + + expect((auth0).worker).toBeUndefined(); + + await loginWithRedirect(auth0); + + assertPost( + 'https://auth0_domain/oauth/token', + { + redirect_uri: TEST_REDIRECT_URI, + client_id: TEST_CLIENT_ID, + code_verifier: TEST_CODE_VERIFIER, + grant_type: 'authorization_code', + code: TEST_CODE + }, + { + 'Auth0-Client': btoa(JSON.stringify(DEFAULT_AUTH0_CLIENT)) + }, + undefined, + false + ); + + mockFetch.mockResolvedValueOnce( + fetchResponse(true, { + id_token: TEST_ID_TOKEN, + refresh_token: TEST_REFRESH_TOKEN, + access_token: TEST_ACCESS_TOKEN, + expires_in: 86400 + }) + ); + + const access_token = await auth0.getTokenSilently({ ignoreCache: true }); + + assertPost( + 'https://auth0_domain/oauth/token', + { + client_id: TEST_CLIENT_ID, + grant_type: 'refresh_token', + redirect_uri: TEST_REDIRECT_URI, + refresh_token: TEST_REFRESH_TOKEN + }, + { + 'Auth0-Client': btoa(JSON.stringify(DEFAULT_AUTH0_CLIENT)) + }, + 1, + false + ); + + expect(access_token).toEqual(TEST_ACCESS_TOKEN); + }); + + it('refreshes the token without the worker, when window.Worker is undefined when not using useFormData', async () => { mockWindow.Worker = undefined; const auth0 = setup({ @@ -690,6 +866,54 @@ describe('Auth0Client', () => { expect(access_token).toEqual(TEST_ACCESS_TOKEN); }); + it('refreshes the token without the worker, when window.Worker is undefined', async () => { + mockWindow.Worker = undefined; + + const auth0 = setup({ + useRefreshTokens: true, + cacheLocation: 'memory' + }); + + expect((auth0).worker).toBeUndefined(); + + await loginWithRedirect(auth0); + + assertPost( + 'https://auth0_domain/oauth/token', + { + redirect_uri: TEST_REDIRECT_URI, + client_id: TEST_CLIENT_ID, + code_verifier: TEST_CODE_VERIFIER, + grant_type: 'authorization_code', + code: TEST_CODE + }, + { + 'Auth0-Client': btoa(JSON.stringify(DEFAULT_AUTH0_CLIENT)) + }, + undefined, + false + ); + + const access_token = await getTokenSilently(auth0, { ignoreCache: true }); + + assertPost( + 'https://auth0_domain/oauth/token', + { + client_id: TEST_CLIENT_ID, + grant_type: 'refresh_token', + redirect_uri: TEST_REDIRECT_URI, + refresh_token: TEST_REFRESH_TOKEN + }, + { + 'Auth0-Client': btoa(JSON.stringify(DEFAULT_AUTH0_CLIENT)) + }, + 1, + false + ); + + expect(access_token).toEqual(TEST_ACCESS_TOKEN); + }); + describe('Worker browser support', () => { [ { @@ -1289,7 +1513,7 @@ describe('Auth0Client', () => { expect(releaseLockSpy).toHaveBeenCalled(); }); - it('sends custom options through to the token endpoint when using an iframe', async () => { + it('sends custom options through to the token endpoint when using an iframe when not using useFormData', async () => { const auth0 = setup({ custom_param: 'foo', another_custom_param: 'bar', @@ -1333,7 +1557,58 @@ describe('Auth0Client', () => { }); }); - it('sends custom options through to the token endpoint when using refresh tokens', async () => { + it('sends custom options through to the token endpoint when using an iframe', async () => { + const auth0 = setup({ + custom_param: 'foo', + another_custom_param: 'bar' + }); + + await loginWithRedirect(auth0); + + jest.spyOn(utils, 'runIframe').mockResolvedValue({ + access_token: TEST_ACCESS_TOKEN, + state: TEST_STATE + }); + + mockFetch.mockResolvedValue( + fetchResponse(true, { + id_token: TEST_ID_TOKEN, + refresh_token: TEST_REFRESH_TOKEN, + access_token: TEST_ACCESS_TOKEN, + expires_in: 86400 + }) + ); + + await auth0.getTokenSilently({ + ignoreCache: true, + custom_param: 'hello world' + }); + + expect( + (utils.runIframe).mock.calls[0][0].includes( + 'custom_param=hello%20world&another_custom_param=bar' + ) + ).toBe(true); + + assertPost( + 'https://auth0_domain/oauth/token', + { + redirect_uri: TEST_REDIRECT_URI, + client_id: TEST_CLIENT_ID, + grant_type: 'authorization_code', + custom_param: 'hello world', + another_custom_param: 'bar', + code_verifier: TEST_CODE_VERIFIER + }, + { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + 1, + false + ); + }); + + it('sends custom options through to the token endpoint when using refresh tokens when not using useFormData', async () => { const auth0 = setup({ useRefreshTokens: true, custom_param: 'foo', @@ -1380,6 +1655,61 @@ describe('Auth0Client', () => { expect(access_token).toEqual(TEST_ACCESS_TOKEN); }); + it('sends custom options through to the token endpoint when using refresh tokens', async () => { + + const auth0 = setup({ + useRefreshTokens: true, + custom_param: 'foo', + another_custom_param: 'bar' + }); + + await loginWithRedirect(auth0, undefined, { + token: { + response: { refresh_token: 'a_refresh_token' } + } + }); + + jest.spyOn(utils, 'runIframe').mockResolvedValue({ + access_token: TEST_ACCESS_TOKEN, + state: TEST_STATE + }); + + mockFetch.mockResolvedValue( + fetchResponse(true, { + id_token: TEST_ID_TOKEN, + refresh_token: TEST_REFRESH_TOKEN, + access_token: TEST_ACCESS_TOKEN, + expires_in: 86400 + }) + ); + + expect(utils.runIframe).not.toHaveBeenCalled(); + + const access_token = await auth0.getTokenSilently({ + ignoreCache: true, + custom_param: 'helloworld' + }); + + assertPost( + 'https://auth0_domain/oauth/token', + { + redirect_uri: TEST_REDIRECT_URI, + client_id: TEST_CLIENT_ID, + grant_type: 'refresh_token', + refresh_token: 'a_refresh_token', + custom_param: 'helloworld', + another_custom_param: 'bar' + }, + { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + 1, + false + ); + + expect(access_token).toEqual(TEST_ACCESS_TOKEN); + }); + it('calls `tokenVerifier.verify` with the `id_token` from in the oauth/token response', async () => { const auth0 = setup({ issuer: 'test-123.auth0.com' diff --git a/__tests__/Auth0Client/handleRedirectCallback.test.ts b/__tests__/Auth0Client/handleRedirectCallback.test.ts index 3ca7f7c0c..7ea06248a 100644 --- a/__tests__/Auth0Client/handleRedirectCallback.test.ts +++ b/__tests__/Auth0Client/handleRedirectCallback.test.ts @@ -312,7 +312,7 @@ describe('Auth0Client', () => { }); }); - it('calls oauth/token without redirect uri if not set in transaction', async () => { + it('calls oauth/token without redirect uri if not set in transaction when not using useFormData', async () => { window.history.pushState( {}, 'Test', @@ -341,6 +341,45 @@ describe('Auth0Client', () => { expect(fetchBody.redirect_uri).toBeUndefined(); }); + it('calls oauth/token without redirect uri if not set in transaction', async () => { + window.history.pushState( + {}, + 'Test', + `#/callback/?code=${TEST_CODE}&state=${TEST_ENCODED_STATE}` + ); + + mockFetch.mockResolvedValueOnce( + fetchResponse(true, { + id_token: TEST_ID_TOKEN, + refresh_token: TEST_REFRESH_TOKEN, + access_token: TEST_ACCESS_TOKEN, + expires_in: 86400 + }) + ); + + const auth0 = setup(); + delete auth0['options']['redirect_uri']; + + await loginWithRedirect(auth0); + + assertPostFn(mockFetch)( + 'https://auth0_domain/oauth/token', + { + redirect_uri: undefined, + client_id: TEST_CLIENT_ID, + code_verifier: TEST_CODE_VERIFIER, + grant_type: 'authorization_code', + code: TEST_CODE + }, + { + 'Auth0-Client': btoa(JSON.stringify(DEFAULT_AUTH0_CLIENT)), + 'Content-Type': 'application/x-www-form-urlencoded' + }, + 0, + false + ); + }); + it('calls oauth/token and uses form data if specified in the options', async () => { window.history.pushState( {}, @@ -357,7 +396,7 @@ describe('Auth0Client', () => { }) ); - const auth0 = setup({}); + const auth0 = setup(); await loginWithRedirect(auth0); diff --git a/__tests__/Auth0Client/loginWithPopup.test.ts b/__tests__/Auth0Client/loginWithPopup.test.ts index 197f13864..f4c1e4616 100644 --- a/__tests__/Auth0Client/loginWithPopup.test.ts +++ b/__tests__/Auth0Client/loginWithPopup.test.ts @@ -336,7 +336,7 @@ describe('Auth0Client', () => { }); it('should log the user in with a popup and get the token with form data', async () => { - const auth0 = setup({}); + const auth0 = setup(); await loginWithPopup(auth0); expect(mockWindow.open).toHaveBeenCalled(); diff --git a/__tests__/Auth0Client/loginWithRedirect.test.ts b/__tests__/Auth0Client/loginWithRedirect.test.ts index 4020c7e0a..cd16912f5 100644 --- a/__tests__/Auth0Client/loginWithRedirect.test.ts +++ b/__tests__/Auth0Client/loginWithRedirect.test.ts @@ -102,7 +102,7 @@ describe('Auth0Client', () => { }); describe('loginWithRedirect', () => { - it('should log the user in and get the token', async () => { + it('should log the user in and get the token when not using useFormData', async () => { const auth0 = setup({ useFormData: false }); @@ -143,6 +143,47 @@ describe('Auth0Client', () => { ); }); + it('should log the user in and get the token', async () => { + const auth0 = setup(); + + await loginWithRedirect(auth0); + + const url = new URL(mockWindow.location.assign.mock.calls[0][0]); + + assertUrlEquals(url, TEST_DOMAIN, '/authorize', { + client_id: TEST_CLIENT_ID, + redirect_uri: TEST_REDIRECT_URI, + scope: TEST_SCOPES, + response_type: 'code', + response_mode: 'query', + state: TEST_STATE, + nonce: TEST_NONCE, + code_challenge: TEST_CODE_CHALLENGE, + code_challenge_method: 'S256' + }); + + assertPost( + 'https://auth0_domain/oauth/token', + { + redirect_uri: TEST_REDIRECT_URI, + client_id: TEST_CLIENT_ID, + code_verifier: TEST_CODE_VERIFIER, + grant_type: 'authorization_code', + code: TEST_CODE + }, + { + 'Auth0-Client': btoa( + JSON.stringify({ + name: 'auth0-spa-js', + version: version + }) + ) + }, + undefined, + false + ); + }); + it('should log the user in using different default scope', async () => { const auth0 = setup({ advancedOptions: {