diff --git a/__tests__/Auth0Client/getTokenWithPopup.test.ts b/__tests__/Auth0Client/getTokenWithPopup.test.ts index 926ea36f7..a43093302 100644 --- a/__tests__/Auth0Client/getTokenWithPopup.test.ts +++ b/__tests__/Auth0Client/getTokenWithPopup.test.ts @@ -8,6 +8,7 @@ import * as scope from '../../src/scope'; import { assertPostFn, fetchResponse, + setupFailingPopup, setupFn, setupMessageEventLister } from './helpers'; @@ -24,7 +25,7 @@ import { TEST_STATE } from '../constants'; -import { Auth0ClientOptions } from '../../src'; +import { Auth0ClientOptions, PopupOpenError } from '../../src'; import { DEFAULT_AUTH0_CLIENT } from '../../src/constants'; import { expect } from '@jest/globals'; @@ -214,5 +215,12 @@ describe('Auth0Client', () => { expect(config.popup.location.href).toMatch(/global-audience/); }); + + it('should fail if the popup cannot be opened', async () => { + const auth0 = setup(); + setupFailingPopup(mockWindow); + + await expect(auth0.getTokenWithPopup()).rejects.toThrow(PopupOpenError); + }) }); }); diff --git a/__tests__/Auth0Client/helpers.ts b/__tests__/Auth0Client/helpers.ts index 6d94f0d82..27dd28159 100644 --- a/__tests__/Auth0Client/helpers.ts +++ b/__tests__/Auth0Client/helpers.ts @@ -304,6 +304,12 @@ export const setupMessageEventLister = ( }); }; +export const setupFailingPopup = ( + mockWindow: any +) => { + mockWindow.open.mockReturnValue(null); +} + export const loginWithPopupFn = (mockWindow, mockFetch) => { return async ( auth0, diff --git a/src/Auth0Client.ts b/src/Auth0Client.ts index b338a7372..56146240b 100644 --- a/src/Auth0Client.ts +++ b/src/Auth0Client.ts @@ -37,6 +37,7 @@ import { AuthenticationError, GenericError, MissingRefreshTokenError, + PopupOpenError, TimeoutError } from './errors'; @@ -361,9 +362,7 @@ export class Auth0Client { config.popup = openPopup(''); if (!config.popup) { - throw new Error( - 'Unable to open a popup for loginWithPopup - window.open returned `null`' - ); + throw new PopupOpenError(); } } @@ -625,7 +624,7 @@ export class Auth0Client { * * If refresh tokens are used, the token endpoint is called directly with the * 'refresh_token' grant. If no refresh token is available to make this call, - * the SDK will only fall back to using an iframe to the '/authorize' URL if + * the SDK will only fall back to using an iframe to the '/authorize' URL if * the `useRefreshTokensFallback` setting has been set to `true`. By default this * setting is `false`. * diff --git a/src/errors.ts b/src/errors.ts index d8518f854..6b42ca7c6 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -66,6 +66,14 @@ export class PopupCancelledError extends GenericError { } } +export class PopupOpenError extends GenericError { + constructor() { + super('popup_open', 'Unable to open a popup for loginWithPopup - window.open returned `null`'); + //https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, PopupOpenError.prototype); + } +} + /** * Error thrown when the token exchange results in a `mfa_required` error */ diff --git a/src/index.ts b/src/index.ts index d1a559690..0657d514b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -28,6 +28,7 @@ export { TimeoutError, PopupTimeoutError, PopupCancelledError, + PopupOpenError, MfaRequiredError, MissingRefreshTokenError } from './errors';