diff --git a/.husky/pre-push b/.husky/pre-push
new file mode 100644
index 00000000..c9272ed3
--- /dev/null
+++ b/.husky/pre-push
@@ -0,0 +1 @@
+npm run prepush
diff --git a/.shiprc b/.shiprc
index 23070538..04893e42 100644
--- a/.shiprc
+++ b/.shiprc
@@ -1,6 +1,6 @@
{
"files": {
- "src/networking/telemetry.ts": [],
+ "src/core/utils/telemetry.ts": [],
".version": []
},
"postbump": "node scripts/jsdocs.js"
diff --git a/EXAMPLES-WEB.md b/EXAMPLES-WEB.md
new file mode 100644
index 00000000..24f7b7ed
--- /dev/null
+++ b/EXAMPLES-WEB.md
@@ -0,0 +1,171 @@
+# React Native Auth0 for Web: Examples
+
+This guide provides usage examples specifically for developers targeting **React Native Web**. The web platform uses the underlying `@auth0/auth0-spa-js` library, and its features are aligned with browser security best practices.
+
+## 1. The Hooks-Based Approach (Recommended)
+
+This is the simplest and recommended way to integrate Auth0. The `Auth0Provider` handles all the complexity of the redirect flow automatically.
+
+### Step 1: Wrap Your App in the `Auth0Provider`
+
+In your main application entry point (e.g., `App.tsx`), wrap your application with the provider.
+
+```jsx
+// src/App.tsx
+import React from 'react';
+import { Auth0Provider } from 'react-native-auth0';
+import config from './auth0-configuration';
+import MainComponent from './MainComponent'; // Your main app UI
+
+const App = () => (
+
+
+
+);
+
+export default App;
+```
+
+### Step 2: Use the `useAuth0` Hook in Your Components
+
+The `Auth0Provider` will automatically handle the redirect callback when your app loads. The `useAuth0` hook will then provide the authentication state.
+
+```jsx
+// src/MainComponent.tsx
+import React from 'react';
+import { useAuth0 } from 'react-native-auth0';
+import { View, Button, Text, ActivityIndicator } from 'react-native';
+
+const MainComponent = () => {
+ const { authorize, clearSession, user, isLoading, error } = useAuth0();
+
+ const onLogin = async () => {
+ try {
+ // This triggers a redirect to the Auth0 Universal Login page.
+ await authorize({ scope: 'openid profile email' });
+ } catch (e) {
+ console.error('Login error:', e);
+ }
+ };
+
+ const onLogout = async () => {
+ try {
+ await clearSession();
+ } catch (e) {
+ console.error('Logout error:', e);
+ }
+ };
+
+ if (isLoading) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+ {error && Error: {error.message}}
+ {user ? (
+ <>
+ Welcome, {user.name}!
+
+ >
+ ) : (
+
+ )}
+
+ );
+};
+```
+
+## 2. The Class-Based / Manual Approach
+
+If you are not using React Hooks or need more fine-grained control, you can instantiate the `Auth0` class and handle the redirect callback manually.
+
+### Step 1: Instantiate the `Auth0` Client
+
+Create a singleton instance of the client.
+
+```javascript
+// src/api/auth0.ts
+import Auth0 from 'react-native-auth0';
+import config from '../auth0-configuration';
+
+const auth0 = new Auth0({
+ domain: config.domain,
+ clientId: config.clientId,
+});
+
+export default auth0;
+```
+
+### Step 2: Handle the Redirect Callback
+
+In your application's root component or entry point, you need to add logic to process the result from Auth0 after the user is redirected back.
+
+```jsx
+// src/App.tsx
+import React, { useEffect, useState } from 'react';
+import { View, Button, Text } from 'react-native';
+import auth0 from './api/auth0'; // Import your singleton
+import type { User } from 'react-native-auth0';
+
+const App = () => {
+ const [user, setUser] = useState(null);
+ const [isLoading, setIsLoading] = useState(true);
+
+ useEffect(() => {
+ const checkSession = async () => {
+ // Check if the URL contains redirect parameters
+ if (window.location.search.includes('code=') && window.location.search.includes('state=')) {
+ try {
+ // Process the redirect
+ await auth0.webAuth.handleRedirectCallback();
+ } catch (e) {
+ console.error(e);
+ }
+ // Clean the URL
+ window.history.replaceState({}, document.title, '/');
+ }
+
+ // After handling a potential redirect, check for an existing session
+ try {
+ const credentials = await auth0.credentialsManager.getCredentials();
+ // Assuming you have a way to decode the idToken to get the user
+ // const decodedUser = jwt_decode(credentials.idToken);
+ // setUser(decodedUser);
+ } catch (e) {
+ // No credentials, user is not logged in
+ setUser(null);
+ }
+ setIsLoading(false);
+ };
+
+ checkSession();
+ }, []);
+
+ const onLogin = async () => {
+ await auth0.webAuth.authorize({ scope: 'openid profile email' });
+ };
+
+ const onLogout = async () => {
+ await auth0.webAuth.clearSession();
+ };
+
+ // ... Render UI based on isLoading and user state ...
+};
+```
+
+## Unsupported Web Features
+
+For security reasons, the web platform **does not support** direct authentication grants. The following methods from the `auth` provider will throw a `NotImplemented` error:
+
+- `auth.passwordRealm()`
+- `auth.loginWithOTP()`
+- `auth.loginWithSMS()`
+- `auth.loginWithEmail()`
+- `auth.refreshToken()`
+
+All these flows should be configured in your [Auth0 Universal Login](https://auth0.com/docs/universal-login) page and initiated via the `authorize()` method.
diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md
index bcc70231..28c02d23 100644
--- a/MIGRATION_GUIDE.md
+++ b/MIGRATION_GUIDE.md
@@ -2,69 +2,159 @@
## Upgrading from v4 -> v5
-### Compatibility Requirements
+Version 5.0 of `react-native-auth0` is a significant update featuring a complete architectural overhaul. This new foundation improves performance, maintainability, and provides a more consistent API across all platforms.
-- **React**: v5 requires React 19 or higher
-- **React Native**: v5 requires React Native 0.78.0 or higher
-- **Expo**: v5 requires Expo 53 or higher
+Upgrading from v4.x requires addressing several breaking changes. Please follow this guide carefully.
-### Breaking Changes
+## 1. Compatibility & Installation
-- **Platform Compatibility**: The minimum iOS deployment target is now 14.0. Update your iOS/Podfile with:
+Before updating the library, ensure your project meets the new minimum requirements.
- ```
- platform :ios, '14.0'
- ```
+### Environment Requirements
-- **Android Requirements**: Android SDK API level 35 or higher is now required
+- **React:** `19.0.0` or higher
+- **React Native:** `0.78.0` or higher
+- **Expo:** SDK `53` or higher
+- **iOS:** Deployment Target `14.0`
+- **Android:** Target SDK `35` or higher
-### Migration Steps
+### Updating Your Project
-#### For Regular React Native Projects
+#### For Standard React Native Projects:
-1. First, ensure your project uses React 19 and React Native 0.78.0 or higher:
+1. **Upgrade React Native:**
+ ```bash
+ npm install react@^19.0.0 react-native@^0.78.0
+ ```
+2. **Update this Library:**
+ ```bash
+ npm install react-native-auth0@beta
+ ```
+3. **Update iOS Target:** In your `ios/Podfile`, set the platform version:
+ ```ruby
+ platform :ios, '14.0'
+ ```
+4. **Install Pods:**
+ ```bash
+ cd ios && pod install && cd ..
+ ```
- ```bash
- npm install react@^19.0.0
- npm install react-native@^0.78.0
- ```
+#### For Expo Projects:
-2. Update the react-native-auth0 package:
+1. **Upgrade Expo SDK:**
+ ```bash
+ npx expo upgrade
+ ```
+2. **Update this Library:**
+ ```bash
+ npm install react-native-auth0@beta
+ ```
+3. **Rebuild Native Code:**
+ ```bash
+ npx expo prebuild --clean
+ ```
+ > **Warning:** This will overwrite any manual changes in your `ios` and `android` directories.
- ```bash
- npm install react-native-auth0@beta
- ```
+## 2. Breaking API Changes
-3. Update your iOS minimum deployment target in your Podfile:
+The following API changes require code modifications in your application.
- ```ruby
- platform :ios, '14.0'
- ```
+### Change #1: User Profile Properties are now `camelCase`
-4. Install the updated pods:
- ```bash
- cd ios && pod install && cd ..
- ```
+To align with modern JavaScript standards, all properties on the `user` object are now `camelCase`.
-#### For Expo Projects
+**✅ Action Required:** Update all references to `user` properties.
-1. Update to Expo 53 or higher:
+| Before (snake_case) | After (camelCase) |
+| :-------------------- | :------------------- |
+| `user.given_name` | `user.givenName` |
+| `user.family_name` | `user.familyName` |
+| `user.email_verified` | `user.emailVerified` |
+| `user.phone_number` | `user.phoneNumber` |
+| ...and so on. | |
- ```bash
- npx expo upgrade
- ```
+### Change #2: Credentials Object uses `expiresAt`
-2. Update the react-native-auth0 package:
+The `Credentials` object no longer includes `expiresIn` (a duration). It now provides `expiresAt`, an absolute **UNIX timestamp** (in seconds), making expiration checks simpler and less error-prone.
- ```bash
- npm install react-native-auth0@beta
- ```
+**✅ Action Required:** Replace all logic using `expiresIn` with `expiresAt`.
-3. Rebuild your app:
- ```bash
- npx expo prebuild --clean
- ```
- Note: This will reset any manual changes to your native code.
+**Before:**
+
+```javascript
+const expiresAt = Date.now() / 1000 + credentials.expiresIn;
+if (isExpired(expiresAt)) {
+ // ...
+}
+```
+
+**After:**
+
+```javascript
+// Direct comparison is now possible
+if (credentials.expiresAt < Date.now() / 1000) {
+ // ...
+}
+
+// Or, use the new helper method (if you have an instance of the Credentials model):
+if (credentials.isExpired()) {
+ // ...
+}
+```
+
+### Change #3: Standardized `AuthError` Object
+
+All errors thrown by the library are now instances of a single, consistent `AuthError` class. This replaces multiple error types like `CredentialsManagerError`.
+
+**✅ Action Required:** Update your `try...catch` blocks to handle the new unified error object.
+
+**Before:**
+
+```javascript
+catch (e) {
+ // Inconsistent properties like e.error, e.error_description
+ console.error(e.message);
+}
+```
+
+**After:**
+
+```javascript
+import { AuthError } from 'react-native-auth0';
+
+catch (e) {
+ if (e instanceof AuthError) {
+ // Consistent properties are now available
+ console.error(e.name, e.message); // e.g., 'invalid_grant', 'The refresh token is invalid.'
+ }
+}
+```
+
+### Change #4: Updated `authorize` and `clearSession` Signatures
+
+For improved clarity, SDK-specific options (like `ephemeralSession`) have been moved into a separate, second `options` object.
+
+**✅ Action Required:** Restructure calls to `authorize` and `clearSession`.
+
+**Before:**
+
+```javascript
+// Mixed parameters and options
+await authorize({
+ scope: 'openid profile',
+ ephemeralSession: true,
+});
+```
+
+**After:**
+
+```javascript
+// Parameters and options are now separate arguments
+await authorize(
+ { scope: 'openid profile' }, // 1. OIDC / Auth0 Parameters
+ { ephemeralSession: true } // 2. SDK-Specific Options
+);
+```
## Upgrading from v3 -> v4
diff --git a/README.md b/README.md
index 52d23be2..7fc21ff2 100644
--- a/README.md
+++ b/README.md
@@ -664,6 +664,32 @@ _Note_ : We have platform agnostic error codes available only for `CredentialsMa
| `NO_NETWORK` | `NO_NETWORK` | |
| `API_ERROR` | `API_ERROR` | |
+## Features and Platform Support
+
+This library provides a unified API across Native (iOS/Android) and Web platforms. However, due to security models and underlying technology, not all features are available on every platform.
+
+| Feature / Method Category | Native (iOS/Android) | Web (Browser) | Notes & Rationale |
+| ------------------------------------------ | :------------------: | :-----------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| **Web Authentication** | | | --- |
+| `webAuth.authorize()` | ✅ | ✅ | **Primary login method.** Uses `ASWebAuthenticationSession`/`Custom Tabs` on Native and `loginWithRedirect` on Web. |
+| `webAuth.clearSession()` | ✅ | ✅ | **Primary logout method.** Clears the session cookie on the server via a browser redirect. |
+| `webAuth.handleRedirectCallback()` | ❌ | ✅ | **Web-only.** Manually processes the callback from Auth0. Handled automatically when using the `Auth0Provider` hook. |
+| **Credential Management** | | | --- |
+| `credentialsManager.getCredentials()` | ✅ | ✅ | Retrieves stored tokens. On Native, it uses the secure Keychain/Keystore. On Web, it uses the `@auth0/auth0-spa-js` cache and `getTokenSilently`. |
+| `credentialsManager.hasValidCredentials()` | ✅ | ✅ | Checks for a valid local session. |
+| `credentialsManager.saveCredentials()` | ✅ | ❌ | **Native-only.** Manually saving credentials is required on Native. On Web, this is handled automatically by the underlying SPA SDK and is a no-op. |
+| `credentialsManager.clearCredentials()` | ✅ | ✅ | Clears locally stored tokens. On Web, this performs a "local-only" logout. |
+| **Direct Authentication Grants** | | | --- |
+| `auth.passwordRealm()` | ✅ | ❌ | **Not supported on Web for security reasons.** The Resource Owner Password Grant exposes credentials to the browser and is not recommended for Single Page Applications. |
+| `auth.passwordless...()` | ✅ | ❌ | **Not supported on Web.** Passwordless flows on the web should be configured via Universal Login and initiated with `webAuth.authorize()`. |
+| `auth.loginWith...()` (OTP/SMS etc) | ✅ | ❌ | **Not supported on Web.** These direct grant flows are not secure for public clients like browsers. |
+| **Token & User Management** | | | --- |
+| `auth.refreshToken()` | ✅ | ❌ | **Not supported on Web.** Token refresh is handled automatically by `getCredentials()` via `getTokenSilently()` on the web. |
+| `auth.userInfo()` | ✅ | ✅ | Fetches the user's profile from the `/userinfo` endpoint using an access token. |
+| `auth.createUser()` | ✅ | ✅ | Calls the `/dbconnections/signup` endpoint. Works on both platforms. |
+| `auth.resetPassword()` | ✅ | ✅ | Calls the `/dbconnections/change_password` endpoint. Works on both platforms. |
+| `users(token).patchUser()` | ✅ | ✅ | Calls the Management API. Works on any platform with a valid token, but use with caution in the browser. |
+
## Troubleshooting
### Swift 6 Compatibility Issues on iOS
@@ -698,7 +724,7 @@ We appreciate feedback and contribution to this repo! Before you get started, pl
- [Auth0's general contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md)
- [Auth0's code of conduct guidelines](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md)
-- [This repo's development guide](DEVELOPMENT.md)
+- [This repo's development guide](CONTRIBUTING.md)
### Raise an issue
diff --git a/__mocks__/react-native.js b/__mocks__/react-native.js
new file mode 100644
index 00000000..f03ed1cc
--- /dev/null
+++ b/__mocks__/react-native.js
@@ -0,0 +1,50 @@
+/**
+ * This file provides a project-wide, self-contained mock for the 'react-native' package.
+ */
+
+const React = require('react');
+
+// A fake native module to be returned by TurboModuleRegistry.getEnforcing.
+const mockNativeModule = {
+ // ... (all the mock native methods from before)
+ initializeAuth0WithConfiguration: jest.fn(() => Promise.resolve()),
+ webAuth: jest.fn(() => Promise.resolve({})),
+ webAuthLogout: jest.fn(() => Promise.resolve()),
+ cancelWebAuth: jest.fn(() => Promise.resolve()),
+ getBundleIdentifier: jest.fn(() => Promise.resolve('com.my.app')),
+ resumeWebAuth: jest.fn(() => Promise.resolve(true)),
+ saveCredentials: jest.fn(() => Promise.resolve()),
+ getCredentials: jest.fn(() => Promise.resolve({})),
+ hasValidCredentials: jest.fn(() => Promise.resolve(true)),
+ clearCredentials: jest.fn(() => Promise.resolve()),
+ hasValidInstance: jest.fn(() => Promise.resolve(true)),
+};
+
+// Export the mocked module.
+module.exports = {
+ // Mock the Platform module
+ Platform: {
+ OS: 'ios',
+ },
+ // Mock the TurboModuleRegistry
+ TurboModuleRegistry: {
+ getEnforcing: () => mockNativeModule,
+ },
+ // Mock the Linking module
+ Linking: {
+ addEventListener: jest.fn(() => ({ remove: jest.fn() })),
+ removeEventListener: jest.fn(),
+ getInitialURL: jest.fn(() => Promise.resolve(null)),
+ },
+
+ // FIX: Add mocks for core UI components used in tests.
+ // We can use a simple functional component that just renders its children
+ // and passes along any props.
+ View: (props) => React.createElement('View', props, props.children),
+ Text: (props) => React.createElement('Text', props, props.children),
+ Button: (props) => React.createElement('Button', props, props.children),
+ // Add any other components your tests might use, e.g., StyleSheet
+ StyleSheet: {
+ create: (styles) => styles,
+ },
+};
diff --git a/example/index.js b/example/index.js
index 117ddcae..24ba9241 100644
--- a/example/index.js
+++ b/example/index.js
@@ -3,3 +3,9 @@ import App from './src/App';
import { name as appName } from './app.json';
AppRegistry.registerComponent(appName, () => App);
+
+// This is the entry point for the React Native web application.
+if (typeof document !== 'undefined') {
+ const rootTag = document.getElementById('root');
+ AppRegistry.runApplication(appName, { rootTag });
+}
diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock
index 302edddc..c8af046b 100644
--- a/example/ios/Podfile.lock
+++ b/example/ios/Podfile.lock
@@ -1,5 +1,5 @@
PODS:
- - A0Auth0 (5.0.0-beta.2):
+ - A0Auth0 (5.0.0-beta.3):
- Auth0 (= 2.10)
- DoubleConversion
- glog
@@ -1363,7 +1363,7 @@ PODS:
- React-jsiexecutor
- React-RCTFBReactNativeSpec
- ReactCommon/turbomodule/core
- - react-native-safe-area-context (5.4.0):
+ - react-native-safe-area-context (5.4.1):
- DoubleConversion
- glog
- hermes-engine
@@ -1378,8 +1378,8 @@ PODS:
- React-hermes
- React-ImageManager
- React-jsi
- - react-native-safe-area-context/common (= 5.4.0)
- - react-native-safe-area-context/fabric (= 5.4.0)
+ - react-native-safe-area-context/common (= 5.4.1)
+ - react-native-safe-area-context/fabric (= 5.4.1)
- React-NativeModulesApple
- React-RCTFabric
- React-renderercss
@@ -1389,7 +1389,7 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- - react-native-safe-area-context/common (5.4.0):
+ - react-native-safe-area-context/common (5.4.1):
- DoubleConversion
- glog
- hermes-engine
@@ -1413,7 +1413,7 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- - react-native-safe-area-context/fabric (5.4.0):
+ - react-native-safe-area-context/fabric (5.4.1):
- DoubleConversion
- glog
- hermes-engine
@@ -1760,7 +1760,31 @@ PODS:
- React-logger (= 0.79.2)
- React-perflogger (= 0.79.2)
- React-utils (= 0.79.2)
- - RNScreens (4.10.0):
+ - RNGestureHandler (2.26.0):
+ - DoubleConversion
+ - glog
+ - hermes-engine
+ - RCT-Folly (= 2024.11.18.00)
+ - RCTRequired
+ - RCTTypeSafety
+ - React-Core
+ - React-debug
+ - React-Fabric
+ - React-featureflags
+ - React-graphics
+ - React-hermes
+ - React-ImageManager
+ - React-jsi
+ - React-NativeModulesApple
+ - React-RCTFabric
+ - React-renderercss
+ - React-rendererdebug
+ - React-utils
+ - ReactCodegen
+ - ReactCommon/turbomodule/bridging
+ - ReactCommon/turbomodule/core
+ - Yoga
+ - RNScreens (4.11.1):
- DoubleConversion
- glog
- hermes-engine
@@ -1784,9 +1808,9 @@ PODS:
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- - RNScreens/common (= 4.10.0)
+ - RNScreens/common (= 4.11.1)
- Yoga
- - RNScreens/common (4.10.0):
+ - RNScreens/common (4.11.1):
- DoubleConversion
- glog
- hermes-engine
@@ -1889,6 +1913,7 @@ DEPENDENCIES:
- ReactAppDependencyProvider (from `build/generated/ios`)
- ReactCodegen (from `build/generated/ios`)
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
+ - RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- RNScreens (from `../node_modules/react-native-screens`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
@@ -2043,13 +2068,15 @@ EXTERNAL SOURCES:
:path: build/generated/ios
ReactCommon:
:path: "../node_modules/react-native/ReactCommon"
+ RNGestureHandler:
+ :path: "../node_modules/react-native-gesture-handler"
RNScreens:
:path: "../node_modules/react-native-screens"
Yoga:
:path: "../node_modules/react-native/ReactCommon/yoga"
SPEC CHECKSUMS:
- A0Auth0: 6b64eb955d9bf2d80bd147ae26c9ac9c69e66d97
+ A0Auth0: 8a3cbbc2f85fcfcca13c3bc804955599cc94a8bf
Auth0: 2876d0c36857422eda9cb580a6cc896c7d14cb36
boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90
DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb
@@ -2090,7 +2117,7 @@ SPEC CHECKSUMS:
React-logger: 8edfcedc100544791cd82692ca5a574240a16219
React-Mapbuffer: c3f4b608e4a59dd2f6a416ef4d47a14400194468
React-microtasksnativemodule: 054f34e9b82f02bd40f09cebd4083828b5b2beb6
- react-native-safe-area-context: 562163222d999b79a51577eda2ea8ad2c32b4d06
+ react-native-safe-area-context: 5594ec631ede9c311c5c0efa244228eff845ce88
React-NativeModulesApple: 2c4377e139522c3d73f5df582e4f051a838ff25e
React-oscompat: ef5df1c734f19b8003e149317d041b8ce1f7d29c
React-perflogger: 9a151e0b4c933c9205fd648c246506a83f31395d
@@ -2122,7 +2149,8 @@ SPEC CHECKSUMS:
ReactAppDependencyProvider: 04d5eb15eb46be6720e17a4a7fa92940a776e584
ReactCodegen: c63eda03ba1d94353fb97b031fc84f75a0d125ba
ReactCommon: 76d2dc87136d0a667678668b86f0fca0c16fdeb0
- RNScreens: 5621e3ad5a329fbd16de683344ac5af4192b40d3
+ RNGestureHandler: c7986dd1eb909e329882af067c05db0702a659b3
+ RNScreens: 482e9707f9826230810c92e765751af53826d509
SimpleKeychain: 768cf43ae778b1c21816e94dddf01bb8ee96a075
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
Yoga: c758bfb934100bb4bf9cbaccb52557cee35e8bdf
diff --git a/example/package.json b/example/package.json
index b33bfc44..61026d6b 100644
--- a/example/package.json
+++ b/example/package.json
@@ -5,6 +5,8 @@
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
+ "web": "webpack serve --mode development",
+ "web:build": "webpack --mode production",
"start": "react-native start",
"build:android": "react-native build-android --extra-params \"--no-daemon --console=plain -PreactNativeArchitectures=arm64-v8a\"",
"build:ios": "react-native build-ios --mode Debug",
@@ -12,13 +14,16 @@
"postinstall": "npm run pods"
},
"dependencies": {
- "@react-navigation/native": "^7.1.8",
- "@react-navigation/native-stack": "^7.3.12",
+ "@auth0/auth0-spa-js": "^2.2.0",
+ "@react-navigation/bottom-tabs": "^7.4.2",
+ "@react-navigation/native": "^7.1.13",
+ "@react-navigation/stack": "^7.3.6",
"react": "19.0.0",
"react-native": "0.79.2",
- "react-native-paper": "^5.14.0",
- "react-native-safe-area-context": "^5.4.0",
- "react-native-screens": "^4.10.0"
+ "react-native-gesture-handler": "^2.26.0",
+ "react-native-safe-area-context": "^5.4.1",
+ "react-native-screens": "^4.11.1",
+ "react-native-web": "^0.20.0"
},
"devDependencies": {
"@babel/core": "^7.25.2",
@@ -31,7 +36,14 @@
"@react-native/metro-config": "0.79.2",
"@react-native/typescript-config": "0.79.2",
"@types/react": "^19.1.3",
- "react-native-builder-bob": "^0.40.10"
+ "babel-loader": "^10.0.0",
+ "babel-plugin-react-native-web": "^0.20.0",
+ "html-webpack-plugin": "^5.6.3",
+ "react-native-builder-bob": "^0.40.10",
+ "url-loader": "^4.1.1",
+ "webpack": "^5.99.9",
+ "webpack-cli": "^6.0.1",
+ "webpack-dev-server": "^5.2.2"
},
"engines": {
"node": ">=20"
diff --git a/example/public/index.html b/example/public/index.html
new file mode 100644
index 00000000..cf8ad994
--- /dev/null
+++ b/example/public/index.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+ Auth0 React Native Example
+
+
+
+
+
+
+
diff --git a/example/src/App.tsx b/example/src/App.tsx
index bad60610..ad0833c4 100644
--- a/example/src/App.tsx
+++ b/example/src/App.tsx
@@ -1,154 +1,22 @@
+import React from 'react';
+import { StatusBar } from 'react-native';
+import { NavigationContainer } from '@react-navigation/native';
+import RootNavigator from './navigation/RootNavigator';
+
/**
- * Sample React Native App
- * https://github.com/facebook/react-native
+ * The absolute root component of the example application.
*
- * @format
- * @flow strict-local
+ * It sets up the main navigation container and renders the RootNavigator,
+ * which then decides which demo flow (Hooks or Class-based) to display.
+ * The Auth0Provider is now scoped within the Hooks demo flow itself.
*/
-
-import { Alert, Button, StyleSheet, Text, View } from 'react-native';
-import {
- useAuth0,
- Auth0Provider,
- type LocalAuthenticationOptions,
- LocalAuthenticationLevel,
- LocalAuthenticationStrategy,
-} from 'react-native-auth0';
-
-import config from './auth0-configuration';
-
-import {
- type NavigationProp,
- NavigationContainer,
-} from '@react-navigation/native';
-import { createNativeStackNavigator } from '@react-navigation/native-stack';
-
-const Stack = createNativeStackNavigator();
-
-const Home = ({ navigation }: { navigation: NavigationProp }) => {
- const { authorize, clearSession, user, getCredentials, error } = useAuth0();
-
- const onLogin = async () => {
- await authorize();
- const credentials = await getCredentials(undefined, 0, {});
- Alert.alert('AccessToken: ' + credentials?.accessToken);
- };
-
- const loggedIn = user !== undefined && user !== null;
-
- const onLogout = async () => {
- await clearSession();
- };
-
- return (
-
- Auth0Sample - Login
- {user && You are logged in as {user.name}}
- {!user && You are not logged in}
-
-
- );
-};
-
-const HomeProvider = ({ navigation }: { navigation: NavigationProp }) => {
- const localAuthOptions: LocalAuthenticationOptions = {
- title: 'Authenticate to retreive your credentials',
- subtitle: 'Please authenticate to continue',
- description: 'We need to authenticate you to retrieve your credentials',
- cancelTitle: 'Cancel',
- evaluationPolicy: LocalAuthenticationStrategy.deviceOwnerWithBiometrics,
- fallbackTitle: 'Use Passcode',
- authenticationLevel: LocalAuthenticationLevel.strong,
- deviceCredentialFallback: true,
- };
-
- return (
-
-
-
- );
-};
-
-const SecondScreen = () => {
- const { authorize, clearSession, user, getCredentials, error } = useAuth0();
-
- const onLogin = async () => {
- await authorize();
- const credentials = await getCredentials(undefined, 0, {});
- Alert.alert('AccessToken: ' + credentials?.accessToken);
- };
-
- const loggedIn = user !== undefined && user !== null;
-
- const onLogout = async () => {
- await clearSession();
- };
-
- return (
-
- Auth0Sample - Login
- {user && You are logged in as {user.name}}
- {!user && You are not logged in}
-
- {error && {error.message}}
-
- );
-};
-
-const SecondScreenProvider = () => {
- return (
-
-
-
- );
-};
-
-const App = () => {
+function App(): React.JSX.Element {
return (
-
-
-
-
+
+
);
-};
-
-const styles = StyleSheet.create({
- container: {
- flex: 1,
- justifyContent: 'center',
- alignItems: 'center',
- backgroundColor: '#F5FCFF',
- },
- header: {
- fontSize: 20,
- textAlign: 'center',
- margin: 10,
- },
- error: {
- margin: 20,
- textAlign: 'center',
- color: '#D8000C',
- },
-});
+}
export default App;
diff --git a/example/src/App.web.tsx b/example/src/App.web.tsx
new file mode 100644
index 00000000..13830002
--- /dev/null
+++ b/example/src/App.web.tsx
@@ -0,0 +1,415 @@
+import React, { useState } from 'react';
+import {
+ SafeAreaView,
+ ScrollView,
+ View,
+ Text,
+ StyleSheet,
+ ActivityIndicator,
+} from 'react-native';
+import Auth0, { Auth0Provider, useAuth0, User } from 'react-native-auth0';
+
+import config from './auth0-configuration';
+import Button from './components/Button';
+import Header from './components/Header';
+import Result from './components/Result';
+import LabeledInput from './components/LabeledInput';
+
+// ========================================================================
+// --- 1. HOOKS-BASED IMPLEMENTATION (Recommended) ---
+// ========================================================================
+
+const HooksAuthContent = (): React.JSX.Element => {
+ const {
+ authorize,
+ clearSession,
+ user,
+ error,
+ isLoading,
+ getCredentials,
+ createUser,
+ resetPassword,
+ auth,
+ users,
+ } = useAuth0();
+
+ const [result, setResult] = useState