diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e8298fec..b0f35583 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,8 +43,8 @@ jobs: - name: Run RL Scanner uses: auth0/devsecops-tooling/.github/actions/rl-scan@main with: - artifact-name: "react-native-auth0" - artifact-path: "${{ github.workspace }}/react-native-auth0.tgz" + artifact-name: 'react-native-auth0' + artifact-path: '${{ github.workspace }}/react-native-auth0.tgz' version: ${{ steps.get_version.outputs.version }} RLSECURE_LICENSE: ${{ secrets.RLSECURE_LICENSE }} RLSECURE_SITE_KEY: ${{ secrets.RLSECURE_SITE_KEY }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 8dbf3c52..72c67dc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,14 +5,15 @@ [Full Changelog](https://github.com/auth0/react-native-auth0/compare/v5.5.1...v5.6.0) **Added** + - feat: surface DPoP credential state errors from native SDKs [\#1529](https://github.com/auth0/react-native-auth0/pull/1529) ([@subhankarmaiti](https://github.com/subhankarmaiti)) - feat(android): expose allowedBrowserPackages option for web authentication [\#1513](https://github.com/auth0/react-native-auth0/pull/1513) ([@mrbrentkelly](https://github.com/mrbrentkelly)) **Fixed** + - fix: apply deepCamelCase to MFA challenge response [\#1510](https://github.com/auth0/react-native-auth0/pull/1510) ([@AkhtarZaman7](https://github.com/AkhtarZaman7)) - docs: add Expo callback URL format to README [\#1522](https://github.com/auth0/react-native-auth0/pull/1522) ([@subhankarmaiti](https://github.com/subhankarmaiti)) - ## [v5.5.1](https://github.com/auth0/react-native-auth0/tree/v5.5.1) (2026-04-23) [Full Changelog](https://github.com/auth0/react-native-auth0/compare/v5.5.0...v5.5.1) diff --git a/EXAMPLES-WEB.md b/EXAMPLES-WEB.md index 24f7b7ed..21ce8869 100644 --- a/EXAMPLES-WEB.md +++ b/EXAMPLES-WEB.md @@ -158,14 +158,120 @@ const App = () => { }; ``` -## Unsupported Web Features +## 3. MFA Flexible Factors Grant (Web) -For security reasons, the web platform **does not support** direct authentication grants. The following methods from the `auth` provider will throw a `NotImplemented` error: +The MFA Flexible Factors Grant is fully supported on the web platform. It uses the `@auth0/auth0-spa-js` MFA API under the hood. -- `auth.passwordRealm()` -- `auth.loginWithOTP()` -- `auth.loginWithSMS()` -- `auth.loginWithEmail()` -- `auth.refreshToken()` +### Using MFA with Hooks -All these flows should be configured in your [Auth0 Universal Login](https://auth0.com/docs/universal-login) page and initiated via the `authorize()` method. +```tsx +import React, { useState } from 'react'; +import { View, Button, TextInput, Text } from 'react-native'; +import { useAuth0, MfaError, MfaErrorCodes } from 'react-native-auth0'; + +function MfaScreen({ mfaToken }: { mfaToken: string }) { + const { mfa } = useAuth0(); + const [otp, setOtp] = useState(''); + + const listAuthenticators = async () => { + try { + const authenticators = await mfa.getAuthenticators({ mfaToken }); + console.log('Authenticators:', authenticators); + } catch (error) { + if (error instanceof MfaError) { + console.error('MFA error:', error.type, error.message); + } + } + }; + + const enrollTotp = async () => { + try { + const challenge = await mfa.enroll({ mfaToken, type: 'otp' }); + if (challenge.type === 'totp') { + console.log('Scan QR:', challenge.barcodeUri); + console.log('Secret:', challenge.secret); + } + } catch (error) { + if (error instanceof MfaError) { + console.error('Enrollment error:', error.type); + } + } + }; + + const verifyOtp = async () => { + try { + const credentials = await mfa.verify({ mfaToken, otp }); + console.log('Authenticated!', credentials.accessToken); + } catch (error) { + if (error instanceof MfaError) { + switch (error.type) { + case MfaErrorCodes.INVALID_OTP: + console.log('Incorrect code'); + break; + case MfaErrorCodes.TOO_MANY_ATTEMPTS: + console.log('Too many attempts'); + break; + case MfaErrorCodes.EXPIRED_MFA_TOKEN: + console.log('Session expired'); + break; + } + } + } + }; + + return ( + +