diff --git a/__tests__/http.test.ts b/__tests__/http.test.ts index 5d3c91351..5079e1750 100644 --- a/__tests__/http.test.ts +++ b/__tests__/http.test.ts @@ -1,5 +1,6 @@ import fetch from 'unfetch'; -import { switchFetch } from '../src/http'; +import { MfaRequiredError } from '../src/errors'; +import { switchFetch, getJSON } from '../src/http'; jest.mock('../src/worker/token.worker'); jest.mock('unfetch'); @@ -19,3 +20,32 @@ describe('switchFetch', () => { expect(clearTimeout).toBeCalledTimes(1); }); }); + +describe('getJson', () => { + it('throws MfaRequiredError when mfa_required is returned', async () => { + mockUnfetch.mockImplementation(() => + Promise.resolve({ + ok: false, + json: () => Promise.resolve({ error: 'mfa_required' }) + }) + ); + + await expect( + getJSON('https://test.com/', null, null, null, {}, undefined) + ).rejects.toBeInstanceOf(MfaRequiredError); + }); + + it('reads the mfa_token when mfa_required is returned', async () => { + mockUnfetch.mockImplementation(() => + Promise.resolve({ + ok: false, + json: () => + Promise.resolve({ error: 'mfa_required', mfa_token: '1234' }) + }) + ); + + await expect( + getJSON('https://test.com/', null, null, null, {}, undefined) + ).rejects.toHaveProperty('mfa_token', '1234'); + }); +}); diff --git a/src/errors.ts b/src/errors.ts index 1a2446df0..86cd19734 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -3,7 +3,7 @@ */ export class GenericError extends Error { constructor(public error: string, public error_description: string) { - super(error_description); + super(error_description) /* istanbul ignore next */; Object.setPrototypeOf(this, GenericError.prototype); } @@ -30,7 +30,7 @@ export class AuthenticationError extends GenericError { public state: string, public appState: any = null ) { - super(error, error_description); + super(error, error_description) /* istanbul ignore next */; //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, AuthenticationError.prototype); } @@ -42,7 +42,7 @@ export class AuthenticationError extends GenericError { */ export class TimeoutError extends GenericError { constructor() { - super('timeout', 'Timeout'); + super('timeout', 'Timeout') /* istanbul ignore next */; //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, TimeoutError.prototype); } @@ -53,7 +53,7 @@ export class TimeoutError extends GenericError { */ export class PopupTimeoutError extends TimeoutError { constructor(public popup: Window) { - super(); + super() /* istanbul ignore next */; //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, PopupTimeoutError.prototype); } @@ -61,8 +61,23 @@ export class PopupTimeoutError extends TimeoutError { export class PopupCancelledError extends GenericError { constructor(public popup: Window) { - super('cancelled', 'Popup closed'); + super('cancelled', 'Popup closed') /* istanbul ignore next */; //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, PopupCancelledError.prototype); } } + +/** + * Error thrown when the token exchange results in a `mfa_required` error + */ +export class MfaRequiredError extends GenericError { + constructor( + error: string, + error_description: string, + public mfa_token: string + ) { + super(error, error_description) /* istanbul ignore next */; + //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, MfaRequiredError.prototype); + } +} diff --git a/src/http.ts b/src/http.ts index 63c81567c..b2ab4ad10 100644 --- a/src/http.ts +++ b/src/http.ts @@ -7,7 +7,7 @@ import { import { sendMessage } from './worker/worker.utils'; import { FetchOptions } from './global'; -import { GenericError } from './errors'; +import { GenericError, MfaRequiredError } from './errors'; export const createAbortController = () => new AbortController(); @@ -133,7 +133,7 @@ export async function getJSON( } const { - json: { error, error_description, ...success }, + json: { error, error_description, ...data }, ok } = response; @@ -141,8 +141,12 @@ export async function getJSON( const errorMessage = error_description || `HTTP error. Unable to fetch ${url}`; + if (error === 'mfa_required') { + throw new MfaRequiredError(error, errorMessage, data.mfa_token); + } + throw new GenericError(error || 'request_error', errorMessage); } - return success; + return data; } diff --git a/src/index.cjs.ts b/src/index.cjs.ts index 921737d14..ab2f5c971 100644 --- a/src/index.cjs.ts +++ b/src/index.cjs.ts @@ -3,7 +3,8 @@ import createAuth0Client, { GenericError, AuthenticationError, TimeoutError, - PopupTimeoutError + PopupTimeoutError, + MfaRequiredError } from './index'; /** @@ -17,5 +18,6 @@ wrapper.GenericError = GenericError; wrapper.AuthenticationError = AuthenticationError; wrapper.TimeoutError = TimeoutError; wrapper.PopupTimeoutError = PopupTimeoutError; +wrapper.MfaRequiredError = MfaRequiredError; export default wrapper; diff --git a/src/index.ts b/src/index.ts index 8c8928cb2..d32417312 100644 --- a/src/index.ts +++ b/src/index.ts @@ -29,7 +29,8 @@ export { AuthenticationError, TimeoutError, PopupTimeoutError, - PopupCancelledError + PopupCancelledError, + MfaRequiredError } from './errors'; export { ICache, LocalStorageCache, InMemoryCache, Cacheable } from './cache';