Skip to content

Commit

Permalink
[SDK-2750] Expose mfa_token from the mfa_required error when getting …
Browse files Browse the repository at this point in the history
…new tokens (#789)

* Expose mfa_token from the mfa_required error when getting new tokens

* Ignore super calls from istanbul

See: gotwarlost/istanbul#690

* Export MfaRequiredError

Co-authored-by: Steve Hobbs <[email protected]>
  • Loading branch information
frederikprijck and Steve Hobbs authored Sep 8, 2021
1 parent b51eb7a commit 6806c83
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 11 deletions.
32 changes: 31 additions & 1 deletion __tests__/http.test.ts
Original file line number Diff line number Diff line change
@@ -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');
Expand All @@ -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');
});
});
25 changes: 20 additions & 5 deletions src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -53,16 +53,31 @@ 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);
}
}

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);
}
}
10 changes: 7 additions & 3 deletions src/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -133,16 +133,20 @@ export async function getJSON<T>(
}

const {
json: { error, error_description, ...success },
json: { error, error_description, ...data },
ok
} = response;

if (!ok) {
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;
}
4 changes: 3 additions & 1 deletion src/index.cjs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import createAuth0Client, {
GenericError,
AuthenticationError,
TimeoutError,
PopupTimeoutError
PopupTimeoutError,
MfaRequiredError
} from './index';

/**
Expand All @@ -17,5 +18,6 @@ wrapper.GenericError = GenericError;
wrapper.AuthenticationError = AuthenticationError;
wrapper.TimeoutError = TimeoutError;
wrapper.PopupTimeoutError = PopupTimeoutError;
wrapper.MfaRequiredError = MfaRequiredError;

export default wrapper;
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ export {
AuthenticationError,
TimeoutError,
PopupTimeoutError,
PopupCancelledError
PopupCancelledError,
MfaRequiredError
} from './errors';

export { ICache, LocalStorageCache, InMemoryCache, Cacheable } from './cache';

0 comments on commit 6806c83

Please sign in to comment.