Skip to content

Commit e3f427b

Browse files
feat: implement MFA methods related tests
1 parent 2cdb37b commit e3f427b

File tree

4 files changed

+125
-29
lines changed

4 files changed

+125
-29
lines changed

EXAMPLES.md

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -902,39 +902,20 @@ const { mfa } = useAuth0();
902902
const authenticators = await mfa.getAuthenticators(mfaToken);
903903

904904
// For OTP: Challenge is OPTIONAL - code already available in authenticator app
905-
// Skip directly to verify() with the 6-digit code
906-
907-
// For SMS: Challenge REQUIRED to send code
908-
const smsResponse = await mfa.challenge({
909-
mfaToken,
910-
challengeType: 'sms',
911-
authenticatorId: authenticators[0].id
912-
});
913-
console.log('OOB Code:', smsResponse.oobCode); // SMS sent to phone
914-
915-
// For Voice: Challenge REQUIRED to initiate call
916-
const voiceResponse = await mfa.challenge({
905+
// Skip directly to verify() with the 6-digit code, or optionally challenge with:
906+
const otpResponse = await mfa.challenge({
917907
mfaToken,
918-
challengeType: 'voice',
908+
challengeType: 'otp',
919909
authenticatorId: authenticators[0].id
920910
});
921-
console.log('OOB Code:', voiceResponse.oobCode); // Voice call initiated
922911

923-
// For Email: Challenge REQUIRED to send email
924-
const emailResponse = await mfa.challenge({
912+
// For SMS/Voice/Email/Push: Challenge REQUIRED to send code (use 'oob' type)
913+
const oobResponse = await mfa.challenge({
925914
mfaToken,
926-
challengeType: 'email',
927-
authenticatorId: authenticators[0].id
928-
});
929-
console.log('OOB Code:', emailResponse.oobCode); // Email sent
930-
931-
// For Push: Challenge REQUIRED to send notification
932-
await mfa.challenge({
933-
mfaToken,
934-
challengeType: 'push',
935-
authenticatorId: authenticators[0].id
915+
challengeType: 'oob', // Use 'oob' for all out-of-band authenticators
916+
authenticatorId: authenticators[0].id // ID of SMS/Voice/Email/Push authenticator
936917
});
937-
// Push notification sent to Guardian app
918+
console.log('OOB Code:', oobResponse.oobCode); // Code sent via SMS/Voice/Email/Push
938919
```
939920
940921
### Verifying Challenges

__mocks__/@auth0/auth0-spa-js.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ const setDpopNonce = jest.fn();
2020
const generateDpopProof = jest.fn();
2121
const createFetcher = jest.fn();
2222
const getConfiguration = jest.fn();
23+
const mfaGetAuthenticators = jest.fn(() => Promise.resolve([]));
24+
const mfaEnroll = jest.fn(() => Promise.resolve({ id: 'test-id', barcodeUri: 'test-uri', recoveryCodes: [] }));
25+
const mfaChallenge = jest.fn(() => Promise.resolve({ challengeType: 'otp', oobCode: null }));
26+
const mfaVerify = jest.fn(() => Promise.resolve({ access_token: 'test-token', id_token: 'test-id-token' }));
27+
const mfaGetEnrollmentFactors = jest.fn(() => Promise.resolve([]));
2328

2429
export const Auth0Client = jest.fn(() => {
2530
return {
@@ -43,7 +48,21 @@ export const Auth0Client = jest.fn(() => {
4348
generateDpopProof,
4449
createFetcher,
4550
getConfiguration,
51+
mfa: {
52+
getAuthenticators: mfaGetAuthenticators,
53+
enroll: mfaEnroll,
54+
challenge: mfaChallenge,
55+
verify: mfaVerify,
56+
getEnrollmentFactors: mfaGetEnrollmentFactors,
57+
},
4658
};
4759
});
4860

49-
export const ResponseType = actual.ResponseType;
61+
export const ResponseType = actual.ResponseType;
62+
63+
export const MfaError = actual.MfaError;
64+
export const MfaListAuthenticatorsError = actual.MfaListAuthenticatorsError;
65+
export const MfaEnrollmentError = actual.MfaEnrollmentError;
66+
export const MfaChallengeError = actual.MfaChallengeError;
67+
export const MfaVerifyError = actual.MfaVerifyError;
68+
export const MfaEnrollmentFactorsError = actual.MfaEnrollmentFactorsError;

__tests__/mfa.test.tsx

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { renderHook, waitFor } from '@testing-library/react';
2+
import useAuth0 from '../src/use-auth0';
3+
import { createWrapper } from './helpers';
4+
5+
describe('MFA API', () => {
6+
describe('Basic Availability', () => {
7+
it('should provide mfa client through useAuth0', async () => {
8+
const wrapper = createWrapper();
9+
const { result } = renderHook(() => useAuth0(), { wrapper });
10+
11+
await waitFor(() => {
12+
expect(result.current.mfa).toBeDefined();
13+
});
14+
});
15+
16+
it('should provide all five MFA methods', async () => {
17+
const wrapper = createWrapper();
18+
const { result } = renderHook(() => useAuth0(), { wrapper });
19+
20+
await waitFor(() => {
21+
expect(result.current.mfa.getAuthenticators).toBeDefined();
22+
expect(result.current.mfa.enroll).toBeDefined();
23+
expect(result.current.mfa.challenge).toBeDefined();
24+
expect(result.current.mfa.verify).toBeDefined();
25+
expect(result.current.mfa.getEnrollmentFactors).toBeDefined();
26+
});
27+
});
28+
});
29+
30+
describe('Method Success Tests', () => {
31+
it('should call mfa.getAuthenticators', async () => {
32+
const wrapper = createWrapper();
33+
const { result } = renderHook(() => useAuth0(), { wrapper });
34+
35+
await waitFor(async () => {
36+
const authenticators = await result.current.mfa.getAuthenticators('test-mfa-token');
37+
expect(authenticators).toBeDefined();
38+
expect(Array.isArray(authenticators)).toBe(true);
39+
});
40+
});
41+
42+
it('should call mfa.enroll', async () => {
43+
const wrapper = createWrapper();
44+
const { result } = renderHook(() => useAuth0(), { wrapper });
45+
46+
await waitFor(async () => {
47+
const enrollment = await result.current.mfa.enroll({
48+
mfaToken: 'test-mfa-token',
49+
factorType: 'otp',
50+
});
51+
expect(enrollment).toBeDefined();
52+
expect(enrollment.id).toBe('test-id');
53+
});
54+
});
55+
56+
it('should call mfa.challenge', async () => {
57+
const wrapper = createWrapper();
58+
const { result } = renderHook(() => useAuth0(), { wrapper });
59+
60+
await waitFor(async () => {
61+
const response = await result.current.mfa.challenge({
62+
mfaToken: 'test-mfa-token',
63+
challengeType: 'otp',
64+
authenticatorId: 'test-auth-id',
65+
});
66+
expect(response).toBeDefined();
67+
expect(response.challengeType).toBe('otp');
68+
});
69+
});
70+
71+
it('should call mfa.verify', async () => {
72+
const wrapper = createWrapper();
73+
const { result } = renderHook(() => useAuth0(), { wrapper });
74+
75+
await waitFor(async () => {
76+
const tokens = await result.current.mfa.verify({
77+
mfaToken: 'test-mfa-token',
78+
otp: '123456',
79+
});
80+
expect(tokens).toBeDefined();
81+
expect(tokens.access_token).toBe('test-token');
82+
});
83+
});
84+
85+
it('should call mfa.getEnrollmentFactors', async () => {
86+
const wrapper = createWrapper();
87+
const { result } = renderHook(() => useAuth0(), { wrapper });
88+
89+
await waitFor(async () => {
90+
const factors = await result.current.mfa.getEnrollmentFactors('test-mfa-token');
91+
expect(factors).toBeDefined();
92+
expect(Array.isArray(factors)).toBe(true);
93+
});
94+
});
95+
});
96+
});

src/auth0-context.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ export const initialContext = {
400400
challenge: stub,
401401
verify: stub,
402402
getEnrollmentFactors: stub,
403-
} as MfaApiClient,
403+
} as unknown as MfaApiClient,
404404
};
405405

406406
/**

0 commit comments

Comments
 (0)