From 8d2df480d6a245003e71b0584dc010baba294a17 Mon Sep 17 00:00:00 2001 From: Ryan Wolfe Date: Fri, 9 Oct 2020 17:12:40 -0400 Subject: [PATCH 01/13] Refactor logout to use buildLogoutUrl auth0/auth0-spa-js#273 --- __tests__/index.test.ts | 369 ++++++++++++++++++++++++++++++++++------ src/Auth0Client.ts | 41 +++-- src/global.ts | 36 ++++ 3 files changed, 382 insertions(+), 64 deletions(-) diff --git a/__tests__/index.test.ts b/__tests__/index.test.ts index 0d319c17c..2f6690ed9 100644 --- a/__tests__/index.test.ts +++ b/__tests__/index.test.ts @@ -2165,10 +2165,218 @@ describe('Auth0', () => { }); }); - describe('logout()', () => { + describe('buildAuthorizeUrl', () => { + const REDIRECT_OPTIONS = { + redirect_uri: 'https://redirect.uri', + appState: TEST_APP_STATE, + connection: 'test-connection' + }; + + it('encodes state with random string', async () => { + const { auth0, utils } = await setup(); + + await auth0.buildAuthorizeUrl(REDIRECT_OPTIONS); + expect(utils.encode).toHaveBeenCalledWith(TEST_RANDOM_STRING); + }); + + it('creates `code_challenge` by using `utils.sha256` with the result of `utils.createRandomString`', async () => { + const { auth0, utils } = await setup(); + + await auth0.buildAuthorizeUrl(REDIRECT_OPTIONS); + expect(utils.sha256).toHaveBeenCalledWith(TEST_RANDOM_STRING); + expect(utils.bufferToBase64UrlEncoded).toHaveBeenCalledWith( + TEST_ARRAY_BUFFER + ); + }); + + it('creates correct query params', async () => { + const { auth0, utils } = await setup(); + + await auth0.buildAuthorizeUrl(REDIRECT_OPTIONS); + + expect(utils.createQueryParams).toHaveBeenCalledWith({ + client_id: TEST_CLIENT_ID, + scope: TEST_SCOPES, + response_type: TEST_CODE, + response_mode: 'query', + state: TEST_ENCODED_STATE, + nonce: TEST_ENCODED_STATE, + redirect_uri: REDIRECT_OPTIONS.redirect_uri, + code_challenge: TEST_BASE64_ENCODED_STRING, + code_challenge_method: 'S256', + connection: 'test-connection' + }); + }); + + it('creates correct query params with different default scopes', async () => { + const { auth0, utils } = await setup({ + advancedOptions: { + defaultScope: 'email' + } + }); + + await auth0.buildAuthorizeUrl(REDIRECT_OPTIONS); + + expect(utils.createQueryParams).toHaveBeenCalledWith({ + client_id: TEST_CLIENT_ID, + scope: 'openid email', + response_type: TEST_CODE, + response_mode: 'query', + state: TEST_ENCODED_STATE, + nonce: TEST_ENCODED_STATE, + redirect_uri: REDIRECT_OPTIONS.redirect_uri, + code_challenge: TEST_BASE64_ENCODED_STRING, + code_challenge_method: 'S256', + connection: 'test-connection' + }); + }); + + it('creates correct query params when using refresh tokens', async () => { + const { auth0, utils } = await setup({ + useRefreshTokens: true + }); + + await auth0.buildAuthorizeUrl(REDIRECT_OPTIONS); + + expect(utils.createQueryParams).toHaveBeenCalledWith({ + client_id: TEST_CLIENT_ID, + scope: `${TEST_SCOPES} offline_access`, + response_type: TEST_CODE, + response_mode: 'query', + state: TEST_ENCODED_STATE, + nonce: TEST_ENCODED_STATE, + redirect_uri: REDIRECT_OPTIONS.redirect_uri, + code_challenge: TEST_BASE64_ENCODED_STRING, + code_challenge_method: 'S256', + connection: 'test-connection' + }); + }); + + it('creates correct query params without leeway', async () => { + const { auth0, utils } = await setup({ leeway: 10 }); + + await auth0.buildAuthorizeUrl(REDIRECT_OPTIONS); + expect(utils.createQueryParams).toHaveBeenCalledWith({ + client_id: TEST_CLIENT_ID, + scope: TEST_SCOPES, + response_type: TEST_CODE, + response_mode: 'query', + state: TEST_ENCODED_STATE, + nonce: TEST_ENCODED_STATE, + redirect_uri: REDIRECT_OPTIONS.redirect_uri, + code_challenge: TEST_BASE64_ENCODED_STRING, + code_challenge_method: 'S256', + connection: 'test-connection' + }); + }); + + it('creates correct query params when providing a default redirect_uri', async () => { + const redirect_uri = 'https://custom-redirect-uri/callback'; + const { redirect_uri: _ignore, ...options } = REDIRECT_OPTIONS; + const { auth0, utils } = await setup({ + redirect_uri + }); + + await auth0.buildAuthorizeUrl(options); + + expect(utils.createQueryParams).toHaveBeenCalledWith({ + client_id: TEST_CLIENT_ID, + scope: TEST_SCOPES, + response_type: TEST_CODE, + response_mode: 'query', + state: TEST_ENCODED_STATE, + nonce: TEST_ENCODED_STATE, + redirect_uri, + code_challenge: TEST_BASE64_ENCODED_STRING, + code_challenge_method: 'S256', + connection: 'test-connection' + }); + }); + + it('creates correct query params when overriding redirect_uri', async () => { + const redirect_uri = 'https://custom-redirect-uri/callback'; + const { auth0, utils } = await setup({ + redirect_uri + }); + + await auth0.buildAuthorizeUrl(REDIRECT_OPTIONS); + + expect(utils.createQueryParams).toHaveBeenCalledWith({ + client_id: TEST_CLIENT_ID, + scope: TEST_SCOPES, + response_type: TEST_CODE, + response_mode: 'query', + state: TEST_ENCODED_STATE, + nonce: TEST_ENCODED_STATE, + redirect_uri: REDIRECT_OPTIONS.redirect_uri, + code_challenge: TEST_BASE64_ENCODED_STRING, + code_challenge_method: 'S256', + connection: 'test-connection' + }); + }); + + it('creates correct query params with custom params', async () => { + const { auth0, utils } = await setup(); + + await auth0.buildAuthorizeUrl({ ...REDIRECT_OPTIONS, audience: 'test' }); + + expect(utils.createQueryParams).toHaveBeenCalledWith({ + audience: 'test', + client_id: TEST_CLIENT_ID, + scope: TEST_SCOPES, + response_type: TEST_CODE, + response_mode: 'query', + state: TEST_ENCODED_STATE, + nonce: TEST_ENCODED_STATE, + redirect_uri: REDIRECT_OPTIONS.redirect_uri, + code_challenge: TEST_BASE64_ENCODED_STRING, + code_challenge_method: 'S256', + connection: 'test-connection' + }); + }); + + it('calls `transactionManager.create` with new transaction', async () => { + const { auth0, transactionManager } = await setup(); + + await auth0.buildAuthorizeUrl(REDIRECT_OPTIONS); + + expect(transactionManager.create).toHaveBeenCalledWith({ + appState: TEST_APP_STATE, + audience: 'default', + code_verifier: TEST_RANDOM_STRING, + nonce: TEST_ENCODED_STATE, + scope: TEST_SCOPES, + redirect_uri: 'https://redirect.uri' + }); + }); + + it('returns the url', async () => { + const { auth0 } = await setup(); + + const url = await auth0.buildAuthorizeUrl({ + ...REDIRECT_OPTIONS + }); + + expect(url).toBe( + `https://test.auth0.com/authorize?query=params${TEST_AUTH0_CLIENT_QUERY_STRING}` + ); + }); + + it('returns the url when no arguments are passed', async () => { + const { auth0 } = await setup(); + + const url = await auth0.buildAuthorizeUrl(); + + expect(url).toBe( + `https://test.auth0.com/authorize?query=params${TEST_AUTH0_CLIENT_QUERY_STRING}` + ); + }); + }); + + describe('buildLogoutUrl()', () => { it('removes `auth0.is.authenticated` key from storage', async () => { const { auth0, cookieStorage } = await setup(); - auth0.logout(); + auth0.buildLogoutUrl(); expect(cookieStorage.remove).toHaveBeenCalledWith( 'auth0.is.authenticated' ); @@ -2177,7 +2385,7 @@ describe('Auth0', () => { it('creates correct query params with empty options', async () => { const { auth0, utils } = await setup(); - auth0.logout(); + auth0.buildLogoutUrl(); expect(utils.createQueryParams).toHaveBeenCalledWith({ client_id: TEST_CLIENT_ID }); @@ -2186,14 +2394,14 @@ describe('Auth0', () => { it('creates correct query params with `options.client_id` is null', async () => { const { auth0, utils } = await setup(); - auth0.logout({ client_id: null }); + auth0.buildLogoutUrl({ client_id: null }); expect(utils.createQueryParams).toHaveBeenCalledWith({}); }); it('creates correct query params with `options.client_id` defined', async () => { const { auth0, utils } = await setup(); - auth0.logout({ client_id: 'another-client-id' }); + auth0.buildLogoutUrl({ client_id: 'another-client-id' }); expect(utils.createQueryParams).toHaveBeenCalledWith({ client_id: 'another-client-id' }); @@ -2202,7 +2410,7 @@ describe('Auth0', () => { it('creates correct query params with `options.returnTo` defined', async () => { const { auth0, utils } = await setup(); - auth0.logout({ returnTo: 'https://return.to', client_id: null }); + auth0.buildLogoutUrl({ returnTo: 'https://return.to', client_id: null }); expect(utils.createQueryParams).toHaveBeenCalledWith({ returnTo: 'https://return.to' }); @@ -2211,75 +2419,130 @@ describe('Auth0', () => { it('creates correct query params when `options.federated` is true', async () => { const { auth0, utils } = await setup(); - auth0.logout({ federated: true, client_id: null }); + auth0.buildLogoutUrl({ federated: true, client_id: null }); expect(utils.createQueryParams).toHaveBeenCalledWith({}); }); - it('calls `window.location.assign` with the correct url', async () => { - const { auth0 } = await setup(); + it('clears the cache', async () => { + const { auth0, cache } = await setup(); - auth0.logout(); - expect(window.location.assign).toHaveBeenCalledWith( - `https://test.auth0.com/v2/logout?query=params${TEST_AUTH0_CLIENT_QUERY_STRING}` - ); + auth0.buildLogoutUrl(); + + expect(cache.clear).toHaveBeenCalled(); }); + }); +}); - it('calls `window.location.assign` with the correct url when `options.federated` is true', async () => { - const { auth0 } = await setup(); +describe('logout()', () => { + it('removes `auth0.is.authenticated` key from storage', async () => { + const { auth0, cookieStorage } = await setup(); + auth0.logout(); + expect(cookieStorage.remove).toHaveBeenCalledWith('auth0.is.authenticated'); + }); - auth0.logout({ federated: true }); - expect(window.location.assign).toHaveBeenCalledWith( - `https://test.auth0.com/v2/logout?query=params${TEST_AUTH0_CLIENT_QUERY_STRING}&federated` - ); - }); + it('creates correct query params with empty options', async () => { + const { auth0, utils } = await setup(); - it('calls `window.location.assign` with the correct url with custom `options.auth0Client`', async () => { - const auth0Client = { name: '__test_client_name__', version: '9.9.9' }; - const { auth0 } = await setup({ auth0Client }); - auth0.logout(); - expectToHaveBeenCalledWithAuth0ClientParam( - window.location.assign, - auth0Client - ); + auth0.logout(); + expect(utils.createQueryParams).toHaveBeenCalledWith({ + client_id: TEST_CLIENT_ID }); + }); - it('clears the cache', async () => { - const { auth0, cache } = await setup(); + it('creates correct query params with `options.client_id` is null', async () => { + const { auth0, utils } = await setup(); - auth0.logout(); + auth0.logout({ client_id: null }); + expect(utils.createQueryParams).toHaveBeenCalledWith({}); + }); - expect(cache.clear).toHaveBeenCalled(); + it('creates correct query params with `options.client_id` defined', async () => { + const { auth0, utils } = await setup(); + + auth0.logout({ client_id: 'another-client-id' }); + expect(utils.createQueryParams).toHaveBeenCalledWith({ + client_id: 'another-client-id' }); + }); - it('removes `auth0.is.authenticated` key from storage when `options.localOnly` is true', async () => { - const { auth0, cookieStorage } = await setup(); + it('creates correct query params with `options.returnTo` defined', async () => { + const { auth0, utils } = await setup(); - auth0.logout({ localOnly: true }); - expect(cookieStorage.remove).toHaveBeenCalledWith( - 'auth0.is.authenticated' - ); + auth0.logout({ returnTo: 'https://return.to', client_id: null }); + expect(utils.createQueryParams).toHaveBeenCalledWith({ + returnTo: 'https://return.to' }); + }); - it('skips `window.location.assign` when `options.localOnly` is true', async () => { - const { auth0 } = await setup(); + it('creates correct query params when `options.federated` is true', async () => { + const { auth0, utils } = await setup(); - auth0.logout({ localOnly: true }); - expect(window.location.assign).not.toHaveBeenCalledWith(); - }); + auth0.logout({ federated: true, client_id: null }); + expect(utils.createQueryParams).toHaveBeenCalledWith({}); + }); - it('calls `window.location.assign` when `options.localOnly` is false', async () => { - const { auth0 } = await setup(); + it('calls `window.location.assign` with the correct url', async () => { + const { auth0 } = await setup(); - auth0.logout({ localOnly: false }); - expect(window.location.assign).toHaveBeenCalled(); - }); + auth0.logout(); + expect(window.location.assign).toHaveBeenCalledWith( + `https://test.auth0.com/v2/logout?query=params${TEST_AUTH0_CLIENT_QUERY_STRING}` + ); + }); - it('throws when both `options.localOnly` and `options.federated` are true', async () => { - const { auth0 } = await setup(); + it('calls `window.location.assign` with the correct url when `options.federated` is true', async () => { + const { auth0 } = await setup(); - const fn = () => auth0.logout({ localOnly: true, federated: true }); - expect(fn).toThrow(); - }); + auth0.logout({ federated: true }); + expect(window.location.assign).toHaveBeenCalledWith( + `https://test.auth0.com/v2/logout?query=params${TEST_AUTH0_CLIENT_QUERY_STRING}&federated` + ); + }); + + it('calls `window.location.assign` with the correct url with custom `options.auth0Client`', async () => { + const auth0Client = { name: '__test_client_name__', version: '9.9.9' }; + const { auth0 } = await setup({ auth0Client }); + auth0.logout(); + expectToHaveBeenCalledWithAuth0ClientParam( + window.location.assign, + auth0Client + ); + }); + + it('clears the cache', async () => { + const { auth0, cache } = await setup(); + + auth0.logout(); + + expect(cache.clear).toHaveBeenCalled(); + }); + + it('removes `auth0.is.authenticated` key from storage when `options.localOnly` is true', async () => { + const { auth0, cookieStorage } = await setup(); + + auth0.logout({ localOnly: true }); + expect(cookieStorage.remove).toHaveBeenCalledWith('auth0.is.authenticated'); + }); + + it('skips `window.location.assign` when `options.localOnly` is true', async () => { + const { auth0 } = await setup(); + + auth0.logout({ localOnly: true }); + expect(window.location.assign).not.toHaveBeenCalledWith(); + }); + + it('calls `window.location.assign` when `options.localOnly` is false', async () => { + const { auth0 } = await setup(); + + auth0.logout({ localOnly: false }); + expect(window.location.assign).toHaveBeenCalled(); + }); + + it('throws when both `options.localOnly` and `options.federated` are true', async () => { + const { auth0 } = await setup(); + + const fn = () => auth0.logout({ localOnly: true, federated: true }); + expect(fn).toThrow(); }); }); diff --git a/src/Auth0Client.ts b/src/Auth0Client.ts index c0d0e7852..4a807640c 100644 --- a/src/Auth0Client.ts +++ b/src/Auth0Client.ts @@ -52,7 +52,8 @@ import { LogoutOptions, RefreshTokenOptions, OAuthTokenOptions, - CacheLocation + CacheLocation, + LogoutUrlOptions } from './global'; // @ts-ignore @@ -695,6 +696,31 @@ export default class Auth0Client { return !!user; } + /** + * ```js + * await auth0.buildLogoutUrl(options); + * ``` + * + * Clears the application session and builds an `/v2/logout` URL for logout using + * the parameters provided as arguments. + * @param options + */ + public buildLogoutUrl(options: LogoutUrlOptions = {}): string { + if (options.client_id !== null) { + options.client_id = options.client_id || this.options.client_id; + } else { + delete options.client_id; + } + + const { federated, ...logoutOptions } = options; + this.cache.clear(); + this.cookieStorage.remove('auth0.is.authenticated'); + const federatedQuery = federated ? `&federated` : ''; + const url = this._url(`/v2/logout?${createQueryParams(logoutOptions)}`); + + return url + federatedQuery; + } + /** * ```js * auth0.logout(); @@ -713,11 +739,9 @@ export default class Auth0Client { public logout(options: LogoutOptions = {}) { if (options.client_id !== null) { options.client_id = options.client_id || this.options.client_id; - } else { - delete options.client_id; } - const { federated, localOnly, ...logoutOptions } = options; + const { federated, localOnly } = options; if (localOnly && federated) { throw new Error( @@ -725,17 +749,12 @@ export default class Auth0Client { ); } - this.cache.clear(); - this.cookieStorage.remove('auth0.is.authenticated'); - if (localOnly) { return; } - const federatedQuery = federated ? `&federated` : ''; - const url = this._url(`/v2/logout?${createQueryParams(logoutOptions)}`); - - window.location.assign(`${url}${federatedQuery}`); + const url = this.buildLogoutUrl(options); + window.location.assign(url); } private async _getTokenFromIFrame( diff --git a/src/global.ts b/src/global.ts index 808f4b948..f621827fc 100644 --- a/src/global.ts +++ b/src/global.ts @@ -281,6 +281,42 @@ export interface GetTokenSilentlyOptions { export interface GetTokenWithPopupOptions extends PopupLoginOptions {} +export interface LogoutUrlOptions { + /** + * The URL where Auth0 will redirect your browser to after the logout. + * + * **Note**: If the `client_id` parameter is included, the + * `returnTo` URL that is provided must be listed in the + * Application's "Allowed Logout URLs" in the Auth0 dashboard. + * However, if the `client_id` parameter is not included, the + * `returnTo` URL must be listed in the "Allowed Logout URLs" at + * the account level in the Auth0 dashboard. + * + * [Read more about how redirecting after logout works](https://auth0.com/docs/logout/guides/redirect-users-after-logout) + */ + returnTo?: string; + + /** + * The `client_id` of your application. + * + * If this property is not set, then the `client_id` that was used during initialization of the SDK is sent to the logout endpoint. + * + * If this property is set to `null`, then no client ID value is sent to the logout endpoint. + * + * [Read more about how redirecting after logout works](https://auth0.com/docs/logout/guides/redirect-users-after-logout) + */ + client_id?: string; + + /** + * When supported by the upstream identity provider, + * forces the user to logout of their identity provider + * and from Auth0. + * This option cannot be specified along with the `localOnly` option. + * [Read more about how federated logout works at Auth0](https://auth0.com/docs/logout/guides/logout-idps) + */ + federated?: boolean; +} + export interface LogoutOptions { /** * The URL where Auth0 will redirect your browser to after the logout. From 1054ba48d8054a97dda0ed6e6fbc83fd46b6b8f1 Mon Sep 17 00:00:00 2001 From: Ryan Wolfe Date: Mon, 12 Oct 2020 11:15:38 -0400 Subject: [PATCH 02/13] refer to latest mock result --- __tests__/helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__tests__/helpers.ts b/__tests__/helpers.ts index 02425402d..accd883af 100644 --- a/__tests__/helpers.ts +++ b/__tests__/helpers.ts @@ -1,5 +1,5 @@ export const expectToHaveBeenCalledWithAuth0ClientParam = (mock, expected) => { - const [[url]] = (mock).mock.calls; + const url = (mock).mock.calls[mock.mock.calls.length - 1][0]; const param = new URL(url).searchParams.get('auth0Client'); const decodedParam = decodeURIComponent(atob(param)); const actual = JSON.parse(decodedParam); From 03f72f43d988505f44ff55c0287256614a218aca Mon Sep 17 00:00:00 2001 From: Ryan Wolfe Date: Tue, 13 Oct 2020 09:42:09 -0400 Subject: [PATCH 03/13] fix test blocks --- __tests__/helpers.ts | 2 +- __tests__/index.test.ts | 309 ++++++++++++++++++++-------------------- 2 files changed, 157 insertions(+), 154 deletions(-) diff --git a/__tests__/helpers.ts b/__tests__/helpers.ts index accd883af..02425402d 100644 --- a/__tests__/helpers.ts +++ b/__tests__/helpers.ts @@ -1,5 +1,5 @@ export const expectToHaveBeenCalledWithAuth0ClientParam = (mock, expected) => { - const url = (mock).mock.calls[mock.mock.calls.length - 1][0]; + const [[url]] = (mock).mock.calls; const param = new URL(url).searchParams.get('auth0Client'); const decodedParam = decodeURIComponent(atob(param)); const actual = JSON.parse(decodedParam); diff --git a/__tests__/index.test.ts b/__tests__/index.test.ts index 2f6690ed9..7c269cde0 100644 --- a/__tests__/index.test.ts +++ b/__tests__/index.test.ts @@ -2431,219 +2431,222 @@ describe('Auth0', () => { expect(cache.clear).toHaveBeenCalled(); }); }); -}); -describe('logout()', () => { - it('removes `auth0.is.authenticated` key from storage', async () => { - const { auth0, cookieStorage } = await setup(); - auth0.logout(); - expect(cookieStorage.remove).toHaveBeenCalledWith('auth0.is.authenticated'); - }); + describe('logout()', () => { + it('removes `auth0.is.authenticated` key from storage', async () => { + const { auth0, cookieStorage } = await setup(); + auth0.logout(); + expect(cookieStorage.remove).toHaveBeenCalledWith( + 'auth0.is.authenticated' + ); + }); - it('creates correct query params with empty options', async () => { - const { auth0, utils } = await setup(); + it('creates correct query params with empty options', async () => { + const { auth0, utils } = await setup(); - auth0.logout(); - expect(utils.createQueryParams).toHaveBeenCalledWith({ - client_id: TEST_CLIENT_ID + auth0.logout(); + expect(utils.createQueryParams).toHaveBeenCalledWith({ + client_id: TEST_CLIENT_ID + }); }); - }); - it('creates correct query params with `options.client_id` is null', async () => { - const { auth0, utils } = await setup(); + it('creates correct query params with `options.client_id` is null', async () => { + const { auth0, utils } = await setup(); - auth0.logout({ client_id: null }); - expect(utils.createQueryParams).toHaveBeenCalledWith({}); - }); + auth0.logout({ client_id: null }); + expect(utils.createQueryParams).toHaveBeenCalledWith({}); + }); - it('creates correct query params with `options.client_id` defined', async () => { - const { auth0, utils } = await setup(); + it('creates correct query params with `options.client_id` defined', async () => { + const { auth0, utils } = await setup(); - auth0.logout({ client_id: 'another-client-id' }); - expect(utils.createQueryParams).toHaveBeenCalledWith({ - client_id: 'another-client-id' + auth0.logout({ client_id: 'another-client-id' }); + expect(utils.createQueryParams).toHaveBeenCalledWith({ + client_id: 'another-client-id' + }); }); - }); - it('creates correct query params with `options.returnTo` defined', async () => { - const { auth0, utils } = await setup(); + it('creates correct query params with `options.returnTo` defined', async () => { + const { auth0, utils } = await setup(); - auth0.logout({ returnTo: 'https://return.to', client_id: null }); - expect(utils.createQueryParams).toHaveBeenCalledWith({ - returnTo: 'https://return.to' + auth0.logout({ returnTo: 'https://return.to', client_id: null }); + expect(utils.createQueryParams).toHaveBeenCalledWith({ + returnTo: 'https://return.to' + }); }); - }); - it('creates correct query params when `options.federated` is true', async () => { - const { auth0, utils } = await setup(); + it('creates correct query params when `options.federated` is true', async () => { + const { auth0, utils } = await setup(); - auth0.logout({ federated: true, client_id: null }); - expect(utils.createQueryParams).toHaveBeenCalledWith({}); - }); + auth0.logout({ federated: true, client_id: null }); + expect(utils.createQueryParams).toHaveBeenCalledWith({}); + }); - it('calls `window.location.assign` with the correct url', async () => { - const { auth0 } = await setup(); + it('calls `window.location.assign` with the correct url', async () => { + const { auth0 } = await setup(); - auth0.logout(); - expect(window.location.assign).toHaveBeenCalledWith( - `https://test.auth0.com/v2/logout?query=params${TEST_AUTH0_CLIENT_QUERY_STRING}` - ); - }); + auth0.logout(); + expect(window.location.assign).toHaveBeenCalledWith( + `https://test.auth0.com/v2/logout?query=params${TEST_AUTH0_CLIENT_QUERY_STRING}` + ); + }); - it('calls `window.location.assign` with the correct url when `options.federated` is true', async () => { - const { auth0 } = await setup(); + it('calls `window.location.assign` with the correct url when `options.federated` is true', async () => { + const { auth0 } = await setup(); - auth0.logout({ federated: true }); - expect(window.location.assign).toHaveBeenCalledWith( - `https://test.auth0.com/v2/logout?query=params${TEST_AUTH0_CLIENT_QUERY_STRING}&federated` - ); - }); + auth0.logout({ federated: true }); + expect(window.location.assign).toHaveBeenCalledWith( + `https://test.auth0.com/v2/logout?query=params${TEST_AUTH0_CLIENT_QUERY_STRING}&federated` + ); + }); - it('calls `window.location.assign` with the correct url with custom `options.auth0Client`', async () => { - const auth0Client = { name: '__test_client_name__', version: '9.9.9' }; - const { auth0 } = await setup({ auth0Client }); - auth0.logout(); - expectToHaveBeenCalledWithAuth0ClientParam( - window.location.assign, - auth0Client - ); - }); + it('calls `window.location.assign` with the correct url with custom `options.auth0Client`', async () => { + const auth0Client = { name: '__test_client_name__', version: '9.9.9' }; + const { auth0 } = await setup({ auth0Client }); + auth0.logout(); + expectToHaveBeenCalledWithAuth0ClientParam( + window.location.assign, + auth0Client + ); + }); - it('clears the cache', async () => { - const { auth0, cache } = await setup(); + it('clears the cache', async () => { + const { auth0, cache } = await setup(); - auth0.logout(); + auth0.logout(); + expect(cache.clear).toHaveBeenCalled(); + }); - expect(cache.clear).toHaveBeenCalled(); - }); + it('removes `auth0.is.authenticated` key from storage when `options.localOnly` is true', async () => { + const { auth0, cookieStorage } = await setup(); - it('removes `auth0.is.authenticated` key from storage when `options.localOnly` is true', async () => { - const { auth0, cookieStorage } = await setup(); + auth0.logout({ localOnly: true }); + expect(cookieStorage.remove).toHaveBeenCalledWith( + 'auth0.is.authenticated' + ); + }); - auth0.logout({ localOnly: true }); - expect(cookieStorage.remove).toHaveBeenCalledWith('auth0.is.authenticated'); - }); + it('skips `window.location.assign` when `options.localOnly` is true', async () => { + const { auth0 } = await setup(); - it('skips `window.location.assign` when `options.localOnly` is true', async () => { - const { auth0 } = await setup(); + auth0.logout({ localOnly: true }); + expect(window.location.assign).not.toHaveBeenCalledWith(); + }); - auth0.logout({ localOnly: true }); - expect(window.location.assign).not.toHaveBeenCalledWith(); - }); + it('calls `window.location.assign` when `options.localOnly` is false', async () => { + const { auth0 } = await setup(); - it('calls `window.location.assign` when `options.localOnly` is false', async () => { - const { auth0 } = await setup(); + auth0.logout({ localOnly: false }); + expect(window.location.assign).toHaveBeenCalled(); + }); + + it('throws when both `options.localOnly` and `options.federated` are true', async () => { + const { auth0 } = await setup(); - auth0.logout({ localOnly: false }); - expect(window.location.assign).toHaveBeenCalled(); + const fn = () => auth0.logout({ localOnly: true, federated: true }); + expect(fn).toThrow(); + }); }); - it('throws when both `options.localOnly` and `options.federated` are true', async () => { - const { auth0 } = await setup(); + describe('default creation function', () => { + it('does nothing if there is nothing in storage', async () => { + jest.spyOn(Auth0Client.prototype, 'getTokenSilently'); + const getSpy = jest + .spyOn(require('../src/storage').CookieStorageWithLegacySameSite, 'get') + .mockReturnValueOnce(false); - const fn = () => auth0.logout({ localOnly: true, federated: true }); - expect(fn).toThrow(); - }); -}); + const auth0 = await createAuth0Client({ + domain: TEST_DOMAIN, + client_id: TEST_CLIENT_ID + }); -describe('default creation function', () => { - it('does nothing if there is nothing in storage', async () => { - jest.spyOn(Auth0Client.prototype, 'getTokenSilently'); - const getSpy = jest - .spyOn(require('../src/storage').CookieStorageWithLegacySameSite, 'get') - .mockReturnValueOnce(false); + expect(getSpy).toHaveBeenCalledWith('auth0.is.authenticated'); - const auth0 = await createAuth0Client({ - domain: TEST_DOMAIN, - client_id: TEST_CLIENT_ID + expect(auth0.getTokenSilently).not.toHaveBeenCalled(); }); - expect(getSpy).toHaveBeenCalledWith('auth0.is.authenticated'); - - expect(auth0.getTokenSilently).not.toHaveBeenCalled(); - }); + it('calls getTokenSilently if there is a storage item with key `auth0.is.authenticated`', async () => { + Auth0Client.prototype.getTokenSilently = jest.fn(); - it('calls getTokenSilently if there is a storage item with key `auth0.is.authenticated`', async () => { - Auth0Client.prototype.getTokenSilently = jest.fn(); + require('../src/storage').CookieStorage.get.mockReturnValue(true); - require('../src/storage').CookieStorage.get.mockReturnValue(true); + const auth0 = await createAuth0Client({ + domain: TEST_DOMAIN, + client_id: TEST_CLIENT_ID + }); - const auth0 = await createAuth0Client({ - domain: TEST_DOMAIN, - client_id: TEST_CLIENT_ID + expect(auth0.getTokenSilently).toHaveBeenCalledWith(undefined); }); - expect(auth0.getTokenSilently).toHaveBeenCalledWith(undefined); - }); + describe('when refresh tokens are not used', () => { + it('calls getTokenSilently', async () => { + const utils = require('../src/utils'); - describe('when refresh tokens are not used', () => { - it('calls getTokenSilently', async () => { - const utils = require('../src/utils'); + const options = { + audience: 'the-audience', + scope: 'the-scope' + }; - const options = { - audience: 'the-audience', - scope: 'the-scope' - }; + Auth0Client.prototype.getTokenSilently = jest.fn(); - Auth0Client.prototype.getTokenSilently = jest.fn(); + require('../src/storage').get = () => true; - require('../src/storage').get = () => true; + const auth0 = await createAuth0Client({ + domain: TEST_DOMAIN, + client_id: TEST_CLIENT_ID, + ...options + }); - const auth0 = await createAuth0Client({ - domain: TEST_DOMAIN, - client_id: TEST_CLIENT_ID, - ...options + expect(auth0.getTokenSilently).toHaveBeenCalledWith(undefined); }); - - expect(auth0.getTokenSilently).toHaveBeenCalledWith(undefined); }); - }); - describe('when refresh tokens are used', () => { - it('creates the client with the correct scopes', async () => { - const options = { - audience: 'the-audience', - scope: 'the-scope', - useRefreshTokens: true - }; + describe('when refresh tokens are used', () => { + it('creates the client with the correct scopes', async () => { + const options = { + audience: 'the-audience', + scope: 'the-scope', + useRefreshTokens: true + }; - Auth0Client.prototype.getTokenSilently = jest.fn(); + Auth0Client.prototype.getTokenSilently = jest.fn(); - require('../src/storage').get = () => true; + require('../src/storage').get = () => true; - const auth0 = await createAuth0Client({ - domain: TEST_DOMAIN, - client_id: TEST_CLIENT_ID, - ...options - }); + const auth0 = await createAuth0Client({ + domain: TEST_DOMAIN, + client_id: TEST_CLIENT_ID, + ...options + }); - expect((auth0).scope).toBe('the-scope offline_access'); + expect((auth0).scope).toBe('the-scope offline_access'); - expect(auth0.getTokenSilently).toHaveBeenCalledWith(undefined); + expect(auth0.getTokenSilently).toHaveBeenCalledWith(undefined); + }); }); - }); - describe('when localstorage is used', () => { - it('refreshes token state regardless of isauthenticated cookie', async () => { - const cacheLocation: CacheLocation = 'localstorage'; + describe('when localstorage is used', () => { + it('refreshes token state regardless of isauthenticated cookie', async () => { + const cacheLocation: CacheLocation = 'localstorage'; - const options = { - audience: 'the-audience', - scope: 'the-scope', - cacheLocation - }; + const options = { + audience: 'the-audience', + scope: 'the-scope', + cacheLocation + }; - Auth0Client.prototype.getTokenSilently = jest.fn(); + Auth0Client.prototype.getTokenSilently = jest.fn(); - require('../src/storage').get = () => false; + require('../src/storage').get = () => false; - const auth0 = await createAuth0Client({ - domain: TEST_DOMAIN, - client_id: TEST_CLIENT_ID, - ...options - }); + const auth0 = await createAuth0Client({ + domain: TEST_DOMAIN, + client_id: TEST_CLIENT_ID, + ...options + }); - expect(auth0.getTokenSilently).toHaveBeenCalledWith(undefined); + expect(auth0.getTokenSilently).toHaveBeenCalledWith(undefined); + }); }); }); }); From 7e585061dbef95039cfb9e4bbce1080de58e1fe5 Mon Sep 17 00:00:00 2001 From: Ryan Wolfe Date: Tue, 13 Oct 2020 09:42:29 -0400 Subject: [PATCH 04/13] remove reference to localOnly in logoutUrlOptions --- src/global.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/global.ts b/src/global.ts index f621827fc..544443d67 100644 --- a/src/global.ts +++ b/src/global.ts @@ -311,7 +311,6 @@ export interface LogoutUrlOptions { * When supported by the upstream identity provider, * forces the user to logout of their identity provider * and from Auth0. - * This option cannot be specified along with the `localOnly` option. * [Read more about how federated logout works at Auth0](https://auth0.com/docs/logout/guides/logout-idps) */ federated?: boolean; From d9612f1c1b17ac059afac084c7b6c18041b08f39 Mon Sep 17 00:00:00 2001 From: Ryan Wolfe Date: Tue, 13 Oct 2020 09:45:55 -0400 Subject: [PATCH 05/13] clear local cookie/cache only in logout() --- __tests__/index.test.ts | 16 ---------------- src/Auth0Client.ts | 5 +++-- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/__tests__/index.test.ts b/__tests__/index.test.ts index 7c269cde0..1a44cc331 100644 --- a/__tests__/index.test.ts +++ b/__tests__/index.test.ts @@ -2374,14 +2374,6 @@ describe('Auth0', () => { }); describe('buildLogoutUrl()', () => { - it('removes `auth0.is.authenticated` key from storage', async () => { - const { auth0, cookieStorage } = await setup(); - auth0.buildLogoutUrl(); - expect(cookieStorage.remove).toHaveBeenCalledWith( - 'auth0.is.authenticated' - ); - }); - it('creates correct query params with empty options', async () => { const { auth0, utils } = await setup(); @@ -2422,14 +2414,6 @@ describe('Auth0', () => { auth0.buildLogoutUrl({ federated: true, client_id: null }); expect(utils.createQueryParams).toHaveBeenCalledWith({}); }); - - it('clears the cache', async () => { - const { auth0, cache } = await setup(); - - auth0.buildLogoutUrl(); - - expect(cache.clear).toHaveBeenCalled(); - }); }); describe('logout()', () => { diff --git a/src/Auth0Client.ts b/src/Auth0Client.ts index 4a807640c..789c63ba7 100644 --- a/src/Auth0Client.ts +++ b/src/Auth0Client.ts @@ -713,8 +713,6 @@ export default class Auth0Client { } const { federated, ...logoutOptions } = options; - this.cache.clear(); - this.cookieStorage.remove('auth0.is.authenticated'); const federatedQuery = federated ? `&federated` : ''; const url = this._url(`/v2/logout?${createQueryParams(logoutOptions)}`); @@ -749,6 +747,9 @@ export default class Auth0Client { ); } + this.cache.clear(); + this.cookieStorage.remove('auth0.is.authenticated'); + if (localOnly) { return; } From c5e1c8b8880aa042f8a47e79daa5c688f210e987 Mon Sep 17 00:00:00 2001 From: Ryan Wolfe Date: Tue, 13 Oct 2020 10:53:31 -0400 Subject: [PATCH 06/13] prevent passing localOnly to buildLogoutUrl --- src/Auth0Client.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Auth0Client.ts b/src/Auth0Client.ts index 789c63ba7..7c41832df 100644 --- a/src/Auth0Client.ts +++ b/src/Auth0Client.ts @@ -701,8 +701,8 @@ export default class Auth0Client { * await auth0.buildLogoutUrl(options); * ``` * - * Clears the application session and builds an `/v2/logout` URL for logout using - * the parameters provided as arguments. + * Builds a `/v2/logout` URL for logout using the parameters provided as arguments. + * No session data is removed. * @param options */ public buildLogoutUrl(options: LogoutUrlOptions = {}): string { @@ -753,7 +753,7 @@ export default class Auth0Client { if (localOnly) { return; } - + delete options.localOnly; const url = this.buildLogoutUrl(options); window.location.assign(url); } From 0a3ff20531bf9bc2b0da6d838fa1900d0e274cf5 Mon Sep 17 00:00:00 2001 From: Ryan Wolfe Date: Tue, 13 Oct 2020 10:54:07 -0400 Subject: [PATCH 07/13] do not handle client_id in logout() --- src/Auth0Client.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Auth0Client.ts b/src/Auth0Client.ts index 7c41832df..fff546013 100644 --- a/src/Auth0Client.ts +++ b/src/Auth0Client.ts @@ -735,10 +735,6 @@ export default class Auth0Client { * @param options */ public logout(options: LogoutOptions = {}) { - if (options.client_id !== null) { - options.client_id = options.client_id || this.options.client_id; - } - const { federated, localOnly } = options; if (localOnly && federated) { From 72253f6ef34d95ea0c6f8e982e7551cf5f67620d Mon Sep 17 00:00:00 2001 From: Ryan Wolfe Date: Tue, 13 Oct 2020 15:56:16 -0400 Subject: [PATCH 08/13] remove duplicate buildAuthorizeUrl test --- __tests__/index.test.ts | 208 ---------------------------------------- 1 file changed, 208 deletions(-) diff --git a/__tests__/index.test.ts b/__tests__/index.test.ts index 1a44cc331..8d47ced92 100644 --- a/__tests__/index.test.ts +++ b/__tests__/index.test.ts @@ -2165,214 +2165,6 @@ describe('Auth0', () => { }); }); - describe('buildAuthorizeUrl', () => { - const REDIRECT_OPTIONS = { - redirect_uri: 'https://redirect.uri', - appState: TEST_APP_STATE, - connection: 'test-connection' - }; - - it('encodes state with random string', async () => { - const { auth0, utils } = await setup(); - - await auth0.buildAuthorizeUrl(REDIRECT_OPTIONS); - expect(utils.encode).toHaveBeenCalledWith(TEST_RANDOM_STRING); - }); - - it('creates `code_challenge` by using `utils.sha256` with the result of `utils.createRandomString`', async () => { - const { auth0, utils } = await setup(); - - await auth0.buildAuthorizeUrl(REDIRECT_OPTIONS); - expect(utils.sha256).toHaveBeenCalledWith(TEST_RANDOM_STRING); - expect(utils.bufferToBase64UrlEncoded).toHaveBeenCalledWith( - TEST_ARRAY_BUFFER - ); - }); - - it('creates correct query params', async () => { - const { auth0, utils } = await setup(); - - await auth0.buildAuthorizeUrl(REDIRECT_OPTIONS); - - expect(utils.createQueryParams).toHaveBeenCalledWith({ - client_id: TEST_CLIENT_ID, - scope: TEST_SCOPES, - response_type: TEST_CODE, - response_mode: 'query', - state: TEST_ENCODED_STATE, - nonce: TEST_ENCODED_STATE, - redirect_uri: REDIRECT_OPTIONS.redirect_uri, - code_challenge: TEST_BASE64_ENCODED_STRING, - code_challenge_method: 'S256', - connection: 'test-connection' - }); - }); - - it('creates correct query params with different default scopes', async () => { - const { auth0, utils } = await setup({ - advancedOptions: { - defaultScope: 'email' - } - }); - - await auth0.buildAuthorizeUrl(REDIRECT_OPTIONS); - - expect(utils.createQueryParams).toHaveBeenCalledWith({ - client_id: TEST_CLIENT_ID, - scope: 'openid email', - response_type: TEST_CODE, - response_mode: 'query', - state: TEST_ENCODED_STATE, - nonce: TEST_ENCODED_STATE, - redirect_uri: REDIRECT_OPTIONS.redirect_uri, - code_challenge: TEST_BASE64_ENCODED_STRING, - code_challenge_method: 'S256', - connection: 'test-connection' - }); - }); - - it('creates correct query params when using refresh tokens', async () => { - const { auth0, utils } = await setup({ - useRefreshTokens: true - }); - - await auth0.buildAuthorizeUrl(REDIRECT_OPTIONS); - - expect(utils.createQueryParams).toHaveBeenCalledWith({ - client_id: TEST_CLIENT_ID, - scope: `${TEST_SCOPES} offline_access`, - response_type: TEST_CODE, - response_mode: 'query', - state: TEST_ENCODED_STATE, - nonce: TEST_ENCODED_STATE, - redirect_uri: REDIRECT_OPTIONS.redirect_uri, - code_challenge: TEST_BASE64_ENCODED_STRING, - code_challenge_method: 'S256', - connection: 'test-connection' - }); - }); - - it('creates correct query params without leeway', async () => { - const { auth0, utils } = await setup({ leeway: 10 }); - - await auth0.buildAuthorizeUrl(REDIRECT_OPTIONS); - expect(utils.createQueryParams).toHaveBeenCalledWith({ - client_id: TEST_CLIENT_ID, - scope: TEST_SCOPES, - response_type: TEST_CODE, - response_mode: 'query', - state: TEST_ENCODED_STATE, - nonce: TEST_ENCODED_STATE, - redirect_uri: REDIRECT_OPTIONS.redirect_uri, - code_challenge: TEST_BASE64_ENCODED_STRING, - code_challenge_method: 'S256', - connection: 'test-connection' - }); - }); - - it('creates correct query params when providing a default redirect_uri', async () => { - const redirect_uri = 'https://custom-redirect-uri/callback'; - const { redirect_uri: _ignore, ...options } = REDIRECT_OPTIONS; - const { auth0, utils } = await setup({ - redirect_uri - }); - - await auth0.buildAuthorizeUrl(options); - - expect(utils.createQueryParams).toHaveBeenCalledWith({ - client_id: TEST_CLIENT_ID, - scope: TEST_SCOPES, - response_type: TEST_CODE, - response_mode: 'query', - state: TEST_ENCODED_STATE, - nonce: TEST_ENCODED_STATE, - redirect_uri, - code_challenge: TEST_BASE64_ENCODED_STRING, - code_challenge_method: 'S256', - connection: 'test-connection' - }); - }); - - it('creates correct query params when overriding redirect_uri', async () => { - const redirect_uri = 'https://custom-redirect-uri/callback'; - const { auth0, utils } = await setup({ - redirect_uri - }); - - await auth0.buildAuthorizeUrl(REDIRECT_OPTIONS); - - expect(utils.createQueryParams).toHaveBeenCalledWith({ - client_id: TEST_CLIENT_ID, - scope: TEST_SCOPES, - response_type: TEST_CODE, - response_mode: 'query', - state: TEST_ENCODED_STATE, - nonce: TEST_ENCODED_STATE, - redirect_uri: REDIRECT_OPTIONS.redirect_uri, - code_challenge: TEST_BASE64_ENCODED_STRING, - code_challenge_method: 'S256', - connection: 'test-connection' - }); - }); - - it('creates correct query params with custom params', async () => { - const { auth0, utils } = await setup(); - - await auth0.buildAuthorizeUrl({ ...REDIRECT_OPTIONS, audience: 'test' }); - - expect(utils.createQueryParams).toHaveBeenCalledWith({ - audience: 'test', - client_id: TEST_CLIENT_ID, - scope: TEST_SCOPES, - response_type: TEST_CODE, - response_mode: 'query', - state: TEST_ENCODED_STATE, - nonce: TEST_ENCODED_STATE, - redirect_uri: REDIRECT_OPTIONS.redirect_uri, - code_challenge: TEST_BASE64_ENCODED_STRING, - code_challenge_method: 'S256', - connection: 'test-connection' - }); - }); - - it('calls `transactionManager.create` with new transaction', async () => { - const { auth0, transactionManager } = await setup(); - - await auth0.buildAuthorizeUrl(REDIRECT_OPTIONS); - - expect(transactionManager.create).toHaveBeenCalledWith({ - appState: TEST_APP_STATE, - audience: 'default', - code_verifier: TEST_RANDOM_STRING, - nonce: TEST_ENCODED_STATE, - scope: TEST_SCOPES, - redirect_uri: 'https://redirect.uri' - }); - }); - - it('returns the url', async () => { - const { auth0 } = await setup(); - - const url = await auth0.buildAuthorizeUrl({ - ...REDIRECT_OPTIONS - }); - - expect(url).toBe( - `https://test.auth0.com/authorize?query=params${TEST_AUTH0_CLIENT_QUERY_STRING}` - ); - }); - - it('returns the url when no arguments are passed', async () => { - const { auth0 } = await setup(); - - const url = await auth0.buildAuthorizeUrl(); - - expect(url).toBe( - `https://test.auth0.com/authorize?query=params${TEST_AUTH0_CLIENT_QUERY_STRING}` - ); - }); - }); - describe('buildLogoutUrl()', () => { it('creates correct query params with empty options', async () => { const { auth0, utils } = await setup(); From 02e60647f7d53c3d18e6c6309f71c3425f19c46a Mon Sep 17 00:00:00 2001 From: Ryan Wolfe Date: Tue, 13 Oct 2020 16:19:34 -0400 Subject: [PATCH 09/13] correctly handle localOnly on logout --- src/Auth0Client.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Auth0Client.ts b/src/Auth0Client.ts index fff546013..5cdd97abb 100644 --- a/src/Auth0Client.ts +++ b/src/Auth0Client.ts @@ -735,9 +735,9 @@ export default class Auth0Client { * @param options */ public logout(options: LogoutOptions = {}) { - const { federated, localOnly } = options; + const { localOnly, ...logoutOptions } = options; - if (localOnly && federated) { + if (localOnly && logoutOptions.federated) { throw new Error( 'It is invalid to set both the `federated` and `localOnly` options to `true`' ); @@ -749,8 +749,7 @@ export default class Auth0Client { if (localOnly) { return; } - delete options.localOnly; - const url = this.buildLogoutUrl(options); + const url = this.buildLogoutUrl(logoutOptions); window.location.assign(url); } From 34964af9a0b9380da1ceb98d7a9f7654859ec6c1 Mon Sep 17 00:00:00 2001 From: Ryan Wolfe Date: Tue, 13 Oct 2020 16:26:36 -0400 Subject: [PATCH 10/13] revert whitespace changes --- __tests__/index.test.ts | 143 ++++++++++++++++++++-------------------- 1 file changed, 72 insertions(+), 71 deletions(-) diff --git a/__tests__/index.test.ts b/__tests__/index.test.ts index 8d47ced92..dc17cccb5 100644 --- a/__tests__/index.test.ts +++ b/__tests__/index.test.ts @@ -2290,6 +2290,7 @@ describe('Auth0', () => { const { auth0, cache } = await setup(); auth0.logout(); + expect(cache.clear).toHaveBeenCalled(); }); @@ -2323,106 +2324,106 @@ describe('Auth0', () => { expect(fn).toThrow(); }); }); +}); - describe('default creation function', () => { - it('does nothing if there is nothing in storage', async () => { - jest.spyOn(Auth0Client.prototype, 'getTokenSilently'); - const getSpy = jest - .spyOn(require('../src/storage').CookieStorageWithLegacySameSite, 'get') - .mockReturnValueOnce(false); - - const auth0 = await createAuth0Client({ - domain: TEST_DOMAIN, - client_id: TEST_CLIENT_ID - }); - - expect(getSpy).toHaveBeenCalledWith('auth0.is.authenticated'); +describe('default creation function', () => { + it('does nothing if there is nothing in storage', async () => { + jest.spyOn(Auth0Client.prototype, 'getTokenSilently'); + const getSpy = jest + .spyOn(require('../src/storage').CookieStorageWithLegacySameSite, 'get') + .mockReturnValueOnce(false); - expect(auth0.getTokenSilently).not.toHaveBeenCalled(); + const auth0 = await createAuth0Client({ + domain: TEST_DOMAIN, + client_id: TEST_CLIENT_ID }); - it('calls getTokenSilently if there is a storage item with key `auth0.is.authenticated`', async () => { - Auth0Client.prototype.getTokenSilently = jest.fn(); + expect(getSpy).toHaveBeenCalledWith('auth0.is.authenticated'); - require('../src/storage').CookieStorage.get.mockReturnValue(true); + expect(auth0.getTokenSilently).not.toHaveBeenCalled(); + }); - const auth0 = await createAuth0Client({ - domain: TEST_DOMAIN, - client_id: TEST_CLIENT_ID - }); + it('calls getTokenSilently if there is a storage item with key `auth0.is.authenticated`', async () => { + Auth0Client.prototype.getTokenSilently = jest.fn(); - expect(auth0.getTokenSilently).toHaveBeenCalledWith(undefined); + require('../src/storage').CookieStorage.get.mockReturnValue(true); + + const auth0 = await createAuth0Client({ + domain: TEST_DOMAIN, + client_id: TEST_CLIENT_ID }); - describe('when refresh tokens are not used', () => { - it('calls getTokenSilently', async () => { - const utils = require('../src/utils'); + expect(auth0.getTokenSilently).toHaveBeenCalledWith(undefined); + }); - const options = { - audience: 'the-audience', - scope: 'the-scope' - }; + describe('when refresh tokens are not used', () => { + it('calls getTokenSilently', async () => { + const utils = require('../src/utils'); - Auth0Client.prototype.getTokenSilently = jest.fn(); + const options = { + audience: 'the-audience', + scope: 'the-scope' + }; - require('../src/storage').get = () => true; + Auth0Client.prototype.getTokenSilently = jest.fn(); - const auth0 = await createAuth0Client({ - domain: TEST_DOMAIN, - client_id: TEST_CLIENT_ID, - ...options - }); + require('../src/storage').get = () => true; - expect(auth0.getTokenSilently).toHaveBeenCalledWith(undefined); + const auth0 = await createAuth0Client({ + domain: TEST_DOMAIN, + client_id: TEST_CLIENT_ID, + ...options }); + + expect(auth0.getTokenSilently).toHaveBeenCalledWith(undefined); }); + }); - describe('when refresh tokens are used', () => { - it('creates the client with the correct scopes', async () => { - const options = { - audience: 'the-audience', - scope: 'the-scope', - useRefreshTokens: true - }; + describe('when refresh tokens are used', () => { + it('creates the client with the correct scopes', async () => { + const options = { + audience: 'the-audience', + scope: 'the-scope', + useRefreshTokens: true + }; - Auth0Client.prototype.getTokenSilently = jest.fn(); + Auth0Client.prototype.getTokenSilently = jest.fn(); - require('../src/storage').get = () => true; + require('../src/storage').get = () => true; - const auth0 = await createAuth0Client({ - domain: TEST_DOMAIN, - client_id: TEST_CLIENT_ID, - ...options - }); + const auth0 = await createAuth0Client({ + domain: TEST_DOMAIN, + client_id: TEST_CLIENT_ID, + ...options + }); - expect((auth0).scope).toBe('the-scope offline_access'); + expect((auth0).scope).toBe('the-scope offline_access'); - expect(auth0.getTokenSilently).toHaveBeenCalledWith(undefined); - }); + expect(auth0.getTokenSilently).toHaveBeenCalledWith(undefined); }); + }); - describe('when localstorage is used', () => { - it('refreshes token state regardless of isauthenticated cookie', async () => { - const cacheLocation: CacheLocation = 'localstorage'; - - const options = { - audience: 'the-audience', - scope: 'the-scope', - cacheLocation - }; + describe('when localstorage is used', () => { + it('refreshes token state regardless of isauthenticated cookie', async () => { + const cacheLocation: CacheLocation = 'localstorage'; - Auth0Client.prototype.getTokenSilently = jest.fn(); + const options = { + audience: 'the-audience', + scope: 'the-scope', + cacheLocation + }; - require('../src/storage').get = () => false; + Auth0Client.prototype.getTokenSilently = jest.fn(); - const auth0 = await createAuth0Client({ - domain: TEST_DOMAIN, - client_id: TEST_CLIENT_ID, - ...options - }); + require('../src/storage').get = () => false; - expect(auth0.getTokenSilently).toHaveBeenCalledWith(undefined); + const auth0 = await createAuth0Client({ + domain: TEST_DOMAIN, + client_id: TEST_CLIENT_ID, + ...options }); + + expect(auth0.getTokenSilently).toHaveBeenCalledWith(undefined); }); }); }); From a7197e084f13e584d0942a83746cc2b4b2149370 Mon Sep 17 00:00:00 2001 From: Ryan Wolfe Date: Tue, 13 Oct 2020 16:27:40 -0400 Subject: [PATCH 11/13] clean buildLogoutUrl jsdocs --- src/Auth0Client.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Auth0Client.ts b/src/Auth0Client.ts index 5cdd97abb..26d6b9bb7 100644 --- a/src/Auth0Client.ts +++ b/src/Auth0Client.ts @@ -702,7 +702,6 @@ export default class Auth0Client { * ``` * * Builds a `/v2/logout` URL for logout using the parameters provided as arguments. - * No session data is removed. * @param options */ public buildLogoutUrl(options: LogoutUrlOptions = {}): string { From d16a1d3dfb4b13d3749970261dc96f2793a28ed2 Mon Sep 17 00:00:00 2001 From: Ryan Date: Wed, 14 Oct 2020 09:47:05 -0400 Subject: [PATCH 12/13] Update desc of buildLogoutURl Co-authored-by: Steve Hobbs --- src/Auth0Client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Auth0Client.ts b/src/Auth0Client.ts index 26d6b9bb7..6392ba8d4 100644 --- a/src/Auth0Client.ts +++ b/src/Auth0Client.ts @@ -701,7 +701,7 @@ export default class Auth0Client { * await auth0.buildLogoutUrl(options); * ``` * - * Builds a `/v2/logout` URL for logout using the parameters provided as arguments. + * Builds a URL to the logout endpoint using the parameters provided as arguments. * @param options */ public buildLogoutUrl(options: LogoutUrlOptions = {}): string { From f4230e661092e4e05eb815177f6552e51e37b14d Mon Sep 17 00:00:00 2001 From: Ryan Wolfe Date: Wed, 14 Oct 2020 09:52:06 -0400 Subject: [PATCH 13/13] remove duplicate tests --- __tests__/index.test.ts | 41 ----------------------------------------- 1 file changed, 41 deletions(-) diff --git a/__tests__/index.test.ts b/__tests__/index.test.ts index dc17cccb5..880c7b74c 100644 --- a/__tests__/index.test.ts +++ b/__tests__/index.test.ts @@ -2217,47 +2217,6 @@ describe('Auth0', () => { ); }); - it('creates correct query params with empty options', async () => { - const { auth0, utils } = await setup(); - - auth0.logout(); - expect(utils.createQueryParams).toHaveBeenCalledWith({ - client_id: TEST_CLIENT_ID - }); - }); - - it('creates correct query params with `options.client_id` is null', async () => { - const { auth0, utils } = await setup(); - - auth0.logout({ client_id: null }); - expect(utils.createQueryParams).toHaveBeenCalledWith({}); - }); - - it('creates correct query params with `options.client_id` defined', async () => { - const { auth0, utils } = await setup(); - - auth0.logout({ client_id: 'another-client-id' }); - expect(utils.createQueryParams).toHaveBeenCalledWith({ - client_id: 'another-client-id' - }); - }); - - it('creates correct query params with `options.returnTo` defined', async () => { - const { auth0, utils } = await setup(); - - auth0.logout({ returnTo: 'https://return.to', client_id: null }); - expect(utils.createQueryParams).toHaveBeenCalledWith({ - returnTo: 'https://return.to' - }); - }); - - it('creates correct query params when `options.federated` is true', async () => { - const { auth0, utils } = await setup(); - - auth0.logout({ federated: true, client_id: null }); - expect(utils.createQueryParams).toHaveBeenCalledWith({}); - }); - it('calls `window.location.assign` with the correct url', async () => { const { auth0 } = await setup();