Skip to content

Commit 3ce9438

Browse files
feat(mfa): Implement Multi-Factor Authentication (MFA) support
1 parent dc64bf7 commit 3ce9438

35 files changed

Lines changed: 3517 additions & 53 deletions

EXAMPLES-WEB.md

Lines changed: 117 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -158,14 +158,123 @@ const App = () => {
158158
};
159159
```
160160

161-
## Unsupported Web Features
161+
## 3. MFA Flexible Factors Grant (Web)
162162

163-
For security reasons, the web platform **does not support** direct authentication grants. The following methods from the `auth` provider will throw a `NotImplemented` error:
163+
The MFA Flexible Factors Grant is fully supported on the web platform. It uses the `@auth0/auth0-spa-js` MFA API under the hood.
164164

165-
- `auth.passwordRealm()`
166-
- `auth.loginWithOTP()`
167-
- `auth.loginWithSMS()`
168-
- `auth.loginWithEmail()`
169-
- `auth.refreshToken()`
165+
### Using MFA with Hooks
170166

171-
All these flows should be configured in your [Auth0 Universal Login](https://auth0.com/docs/universal-login) page and initiated via the `authorize()` method.
167+
```tsx
168+
import React, { useState } from 'react';
169+
import { View, Button, TextInput, Text } from 'react-native';
170+
import { useAuth0, MfaError, MfaErrorCodes } from 'react-native-auth0';
171+
172+
function MfaScreen({ mfaToken }: { mfaToken: string }) {
173+
const { mfaGetAuthenticators, mfaEnroll, mfaChallenge, mfaVerify } =
174+
useAuth0();
175+
const [otp, setOtp] = useState('');
176+
177+
const listAuthenticators = async () => {
178+
try {
179+
const authenticators = await mfaGetAuthenticators({ mfaToken });
180+
console.log('Authenticators:', authenticators);
181+
} catch (error) {
182+
if (error instanceof MfaError) {
183+
console.error('MFA error:', error.type, error.message);
184+
}
185+
}
186+
};
187+
188+
const enrollTotp = async () => {
189+
try {
190+
const challenge = await mfaEnroll({ mfaToken, type: 'otp' });
191+
if (challenge.type === 'totp') {
192+
console.log('Scan QR:', challenge.barcodeUri);
193+
console.log('Secret:', challenge.secret);
194+
}
195+
} catch (error) {
196+
if (error instanceof MfaError) {
197+
console.error('Enrollment error:', error.type);
198+
}
199+
}
200+
};
201+
202+
const verifyOtp = async () => {
203+
try {
204+
const credentials = await mfaVerify({ mfaToken, otp });
205+
console.log('Authenticated!', credentials.accessToken);
206+
} catch (error) {
207+
if (error instanceof MfaError) {
208+
switch (error.type) {
209+
case MfaErrorCodes.INVALID_OTP:
210+
console.log('Incorrect code');
211+
break;
212+
case MfaErrorCodes.TOO_MANY_ATTEMPTS:
213+
console.log('Too many attempts');
214+
break;
215+
case MfaErrorCodes.EXPIRED_MFA_TOKEN:
216+
console.log('Session expired');
217+
break;
218+
}
219+
}
220+
}
221+
};
222+
223+
return (
224+
<View>
225+
<Button title="List Authenticators" onPress={listAuthenticators} />
226+
<Button title="Enroll TOTP" onPress={enrollTotp} />
227+
<TextInput placeholder="Enter OTP" value={otp} onChangeText={setOtp} />
228+
<Button title="Verify" onPress={verifyOtp} />
229+
</View>
230+
);
231+
}
232+
```
233+
234+
### Using MFA with Auth0 Class
235+
236+
```typescript
237+
import Auth0, { MfaError, MfaErrorCodes } from 'react-native-auth0';
238+
239+
const auth0 = new Auth0({
240+
domain: 'YOUR_AUTH0_DOMAIN',
241+
clientId: 'YOUR_AUTH0_CLIENT_ID',
242+
});
243+
244+
const mfaClient = auth0.mfa();
245+
246+
// List authenticators
247+
const authenticators = await mfaClient.getAuthenticators({
248+
mfaToken: 'mfa_token',
249+
});
250+
251+
// Enroll TOTP
252+
const challenge = await mfaClient.enroll({
253+
mfaToken: 'mfa_token',
254+
type: 'otp',
255+
});
256+
257+
// Enroll SMS
258+
const smsChallenge = await mfaClient.enroll({
259+
mfaToken: 'mfa_token',
260+
phoneNumber: '+12025550135',
261+
});
262+
263+
// Challenge an authenticator
264+
const challengeResult = await mfaClient.challenge({
265+
mfaToken: 'mfa_token',
266+
authenticatorId: 'sms|dev_123',
267+
});
268+
269+
// Verify OTP
270+
const credentials = await mfaClient.verify({
271+
mfaToken: 'mfa_token',
272+
otp: '123456',
273+
});
274+
```
275+
276+
## Web Platform Notes
277+
278+
The web platform supports direct authentication grants including `auth.passwordRealm()`, `auth.createUser()`, `auth.resetPassword()`, and the MFA Flexible Factors Grant. These methods make direct HTTP calls to the Auth0 API.
279+
280+
Token refresh is handled automatically by `credentialsManager.getCredentials()` on the web. The `auth.refreshToken()` method is not available.

0 commit comments

Comments
 (0)