├── .npmignore ├── .nvmrc ├── .version ├── .watchmanconfig ├── example ├── .watchmanconfig ├── tsconfig.json ├── app.json ├── android │ ├── app │ │ ├── src │ │ │ └── main │ │ │ │ ├── res │ │ │ │ ├── values │ │ │ │ │ ├── strings.xml │ │ │ │ │ └── styles.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ └── drawable │ │ │ │ │ └── rn_edit_text_material.xml │ │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── auth0example │ │ │ │ │ ├── MainApplication.kt │ │ │ │ │ └── MainActivity.kt │ │ │ │ └── AndroidManifest.xml │ │ └── proguard-rules.pro │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── settings.gradle │ ├── build.gradle │ ├── gradle.properties │ └── gradlew.bat ├── ios │ ├── Auth0Example │ │ ├── Images.xcassets │ │ │ ├── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── main.m │ │ ├── PrivacyInfo.xcprivacy │ │ ├── AppDelegate.swift │ │ ├── Info.plist │ │ └── LaunchScreen.storyboard │ ├── Auth0Example.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── .xcode.env │ ├── AppDelegate.swift │ ├── Podfile │ └── Auth0Example.xcodeproj │ │ └── xcshareddata │ │ └── xcschemes │ │ └── Auth0Example.xcscheme ├── src │ ├── auth0-configuration.js │ ├── auth0-configuration.d.ts │ ├── api │ │ └── auth0.ts │ ├── components │ │ ├── Header.tsx │ │ ├── LabeledInput.tsx │ │ ├── Button.tsx │ │ ├── Result.tsx │ │ └── UserInfo.tsx │ ├── App.tsx │ ├── navigation │ │ ├── AuthStackNavigator.tsx │ │ ├── MainTabNavigator.tsx │ │ ├── RootNavigator.tsx │ │ ├── HooksDemoNavigator.tsx │ │ └── ClassDemoNavigator.tsx │ └── screens │ │ ├── hooks │ │ └── Api.tsx │ │ ├── class-based │ │ └── ClassLogin.tsx │ │ └── SelectionScreen.tsx ├── babel.config.js ├── index.js ├── metro.config.js ├── react-native.config.js ├── Gemfile ├── public │ └── index.html ├── README.md ├── .gitignore ├── package.json ├── webpack.config.js └── Gemfile.lock ├── .husky ├── pre-push └── pre-commit ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── Feature Request.yml │ └── Bug Report.yml ├── workflows │ ├── sca_scan.yml │ ├── claude-code-review.yml │ ├── main.yml │ ├── release.yml │ ├── publish-docs.yml │ └── npm-release.yml ├── dependabot.yml ├── actions │ ├── get-version │ │ └── action.yml │ ├── get-prerelease │ │ └── action.yml │ ├── npm-publish │ │ └── action.yml │ ├── tag-exists │ │ └── action.yml │ ├── release-create │ │ └── action.yml │ ├── setup │ │ └── action.yml │ └── get-release-notes │ │ └── action.yml ├── stale.yml └── PULL_REQUEST_TEMPLATE.md ├── app.plugin.js ├── src ├── exports │ ├── hooks.ts │ ├── index.ts │ ├── enums.ts │ ├── interface.ts │ └── classes.ts ├── platforms │ ├── native │ │ ├── index.ts │ │ ├── bridge │ │ │ └── index.ts │ │ └── adapters │ │ │ ├── index.ts │ │ │ └── NativeCredentialsManager.ts │ ├── web │ │ ├── adapters │ │ │ ├── index.ts │ │ │ ├── __tests__ │ │ │ │ └── WebCredentialsManager.errors.spec.ts │ │ │ └── WebAuthenticationProvider.ts │ │ └── index.ts │ └── index.ts ├── core │ ├── interfaces │ │ ├── common.ts │ │ ├── index.ts │ │ ├── IUsersClient.ts │ │ ├── IAuthenticationProvider.ts │ │ ├── IAuth0Client.ts │ │ └── IWebAuthProvider.ts │ ├── services │ │ ├── index.ts │ │ └── ManagementApiOrchestrator.ts │ ├── utils │ │ ├── index.ts │ │ ├── telemetry.ts │ │ ├── deepEqual.ts │ │ ├── scope.ts │ │ ├── fetchWithTimeout.ts │ │ ├── conversion.ts │ │ ├── validation.ts │ │ └── __tests__ │ │ │ ├── scope.spec.ts │ │ │ ├── validation.spec.ts │ │ │ └── conversion.spec.ts │ └── models │ │ ├── index.ts │ │ ├── ApiCredentials.ts │ │ ├── Credentials.ts │ │ ├── AuthError.ts │ │ ├── DPoPError.ts │ │ └── WebAuthError.ts ├── hooks │ ├── index.ts │ ├── useAuth0.ts │ └── reducer.ts ├── factory │ ├── index.ts │ ├── Auth0ClientFactory.web.ts │ ├── Auth0ClientFactory.ts │ └── __tests__ │ │ └── Auth0ClientFactory.spec.ts ├── types │ └── index.ts ├── index.ts ├── plugin │ └── __tests__ │ │ └── fixtures │ │ ├── buildgradle.ts │ │ ├── swiftappdelegate-withoutlinking.ts │ │ └── swiftappdelegate-withlinking.ts └── Auth0.ts ├── opslevel.yml ├── .gitattributes ├── assets ├── ios-sso-alert.png └── android-app-link.png ├── .shiprc ├── babel.config.js ├── .prettierrc.js ├── android ├── gradle.properties ├── src │ └── main │ │ ├── newarch │ │ └── com │ │ │ └── auth0 │ │ │ └── react │ │ │ └── A0Auth0Spec.kt │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── auth0 │ │ │ └── react │ │ │ ├── ApiCredentialsParser.kt │ │ │ ├── A0Auth0Package.kt │ │ │ ├── CredentialsParser.kt │ │ │ └── LocalAuthenticationOptionsParser.kt │ │ └── oldarch │ │ └── com │ │ └── auth0 │ │ └── react │ │ └── A0Auth0Spec.kt └── build.gradle ├── ios ├── A0Auth0-Bridging-Header.h └── A0Auth0.h ├── .semgrepignore ├── tsconfig.build.json ├── .yarnrc.yml ├── scripts ├── replace-telemetry-version.js └── ensure-yarn.js ├── codecov.yml ├── A0Auth0.podspec ├── jest.environment.js ├── tsconfig.json ├── LICENSE ├── .gitignore ├── eslint.config.mjs ├── __mocks__ └── react-native.js ├── typedoc.json ├── REACT_NATIVE_WEB_SETUP.md ├── .snyk └── CONTRIBUTING.md /.npmignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v22.15.0 -------------------------------------------------------------------------------- /.version: -------------------------------------------------------------------------------- 1 | v5.2.1 2 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | npm run prepush 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npm run precommit 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @auth0/project-dx-sdks-engineer-codeowner 2 | -------------------------------------------------------------------------------- /app.plugin.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/commonjs/plugin/withAuth0'); 2 | -------------------------------------------------------------------------------- /src/exports/hooks.ts: -------------------------------------------------------------------------------- 1 | export { Auth0Provider, useAuth0 } from '../hooks'; 2 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@react-native/typescript-config" 3 | } 4 | -------------------------------------------------------------------------------- /opslevel.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 1 3 | repository: 4 | owner: dx_sdks 5 | tags: 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | # specific for windows script files 3 | *.bat text eol=crlf -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Auth0Example", 3 | "displayName": "Auth0Example" 4 | } 5 | -------------------------------------------------------------------------------- /src/platforms/native/index.ts: -------------------------------------------------------------------------------- 1 | export { NativeAuth0Client } from './adapters/NativeAuth0Client'; 2 | -------------------------------------------------------------------------------- /assets/ios-sso-alert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/react-native-auth0/HEAD/assets/ios-sso-alert.png -------------------------------------------------------------------------------- /assets/android-app-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/react-native-auth0/HEAD/assets/android-app-link.png -------------------------------------------------------------------------------- /src/core/interfaces/common.ts: -------------------------------------------------------------------------------- 1 | export type NativeModuleError = { 2 | code: string; 3 | message: string; 4 | }; 5 | -------------------------------------------------------------------------------- /src/platforms/native/bridge/index.ts: -------------------------------------------------------------------------------- 1 | export * from './INativeBridge'; 2 | export * from './NativeBridgeManager'; 3 | -------------------------------------------------------------------------------- /.shiprc: -------------------------------------------------------------------------------- 1 | { 2 | "files": { 3 | "src/core/utils/telemetry.ts": [], 4 | ".version": [] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:@react-native/babel-preset'], 3 | sourceMaps: true, 4 | }; 5 | -------------------------------------------------------------------------------- /src/core/services/index.ts: -------------------------------------------------------------------------------- 1 | export * from './AuthenticationOrchestrator'; 2 | export * from './ManagementApiOrchestrator'; 3 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Auth0Example 3 | 4 | -------------------------------------------------------------------------------- /example/ios/Auth0Example/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "version": 1, 4 | "author": "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bracketSpacing: false, 3 | jsxBracketSameLine: true, 4 | singleQuote: true, 5 | trailingComma: 'all', 6 | }; 7 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/react-native-auth0/HEAD/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/core/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './validation'; 2 | export * from './conversion'; 3 | export * from './fetchWithTimeout'; 4 | export * from './scope'; 5 | -------------------------------------------------------------------------------- /src/platforms/web/adapters/index.ts: -------------------------------------------------------------------------------- 1 | export * from './WebAuth0Client'; 2 | export * from './WebWebAuthProvider'; 3 | export * from './WebCredentialsManager'; 4 | -------------------------------------------------------------------------------- /src/platforms/native/adapters/index.ts: -------------------------------------------------------------------------------- 1 | export * from './NativeAuth0Client'; 2 | export * from './NativeWebAuthProvider'; 3 | export * from './NativeCredentialsManager'; 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/react-native-auth0/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/react-native-auth0/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/react-native-auth0/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export { Auth0Provider } from './Auth0Provider'; 2 | export { useAuth0 } from './useAuth0'; 3 | export type { Auth0ContextInterface } from './Auth0Context'; 4 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | A0Auth0_kotlinVersion=2.0.21 2 | A0Auth0_minSdkVersion=24 3 | A0Auth0_targetSdkVersion=34 4 | A0Auth0_compileSdkVersion=35 5 | A0Auth0_ndkVersion=27.1.12297006 -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/react-native-auth0/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/react-native-auth0/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ios/A0Auth0-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import 6 | -------------------------------------------------------------------------------- /src/exports/index.ts: -------------------------------------------------------------------------------- 1 | export * as Classes from './classes'; 2 | export * as Hooks from './hooks'; 3 | export * as Interface from './interface'; 4 | export * as Enums from './enums'; 5 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/react-native-auth0/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/react-native-auth0/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/react-native-auth0/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/react-native-auth0/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auth0/react-native-auth0/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /src/exports/enums.ts: -------------------------------------------------------------------------------- 1 | export { SafariViewControllerPresentationStyle } from '../index'; 2 | export { 3 | LocalAuthenticationLevel, 4 | LocalAuthenticationStrategy, 5 | } from '../types/platform-specific'; 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Auth0 Community 4 | url: https://community.auth0.com 5 | about: Discuss this SDK in the Auth0 Community forums 6 | -------------------------------------------------------------------------------- /src/exports/interface.ts: -------------------------------------------------------------------------------- 1 | export { type Auth0ContextInterface } from '../hooks/Auth0Context'; 2 | export * from '../core/interfaces'; 3 | export { type AuthState } from '../hooks/reducer'; 4 | export * from '../types'; 5 | -------------------------------------------------------------------------------- /src/core/utils/telemetry.ts: -------------------------------------------------------------------------------- 1 | export const telemetry = { 2 | name: 'react-native-auth0', 3 | version: '5.2.1', 4 | }; 5 | 6 | export type Telemetry = { 7 | name?: string; 8 | version?: string; 9 | env?: any; 10 | }; 11 | -------------------------------------------------------------------------------- /android/src/main/newarch/com/auth0/react/A0Auth0Spec.kt: -------------------------------------------------------------------------------- 1 | package com.auth0.react 2 | 3 | import com.facebook.react.bridge.ReactApplicationContext 4 | 5 | abstract class A0Auth0Spec(context: ReactApplicationContext) : NativeA0Auth0Spec(context) -------------------------------------------------------------------------------- /example/src/auth0-configuration.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | clientId: 'your-client-id', 3 | domain: 'your-domain', 4 | clientIdSecondScreen: 'your-client-id', 5 | domainSecondScreen: 'your-domain', 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /src/core/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './common'; 2 | export * from './IAuth0Client'; 3 | export * from './IAuthenticationProvider'; 4 | export * from './ICredentialsManager'; 5 | export * from './IWebAuthProvider'; 6 | export * from './IUsersClient'; 7 | -------------------------------------------------------------------------------- /src/exports/classes.ts: -------------------------------------------------------------------------------- 1 | export { 2 | AuthError, 3 | CredentialsManagerError, 4 | WebAuthError, 5 | DPoPError, 6 | } from '../core/models'; 7 | export { default as Auth0 } from '../Auth0'; 8 | export { TimeoutError } from '../core/utils/fetchWithTimeout'; 9 | -------------------------------------------------------------------------------- /.semgrepignore: -------------------------------------------------------------------------------- 1 | docs/ 2 | src/auth/__tests__ 3 | src/credentials-manager/__tests__ 4 | src/hooks/__tests__ 5 | src/jwt/__tests__ 6 | src/management/__tests__ 7 | src/networking/__tests__ 8 | src/utils/__tests__ 9 | src/webauth/__mocks__ 10 | src/webauth/__tests__ 11 | -------------------------------------------------------------------------------- /example/ios/Auth0Example/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | @autoreleasepool { 8 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 9 | } 10 | } -------------------------------------------------------------------------------- /example/src/auth0-configuration.d.ts: -------------------------------------------------------------------------------- 1 | interface Auth0Configuration { 2 | domain: string; 3 | clientId: string; 4 | domainSecondScreen: string; 5 | clientIdSecondScreen: string; 6 | } 7 | 8 | declare const config: Auth0Configuration; 9 | export default config; 10 | -------------------------------------------------------------------------------- /ios/A0Auth0.h: -------------------------------------------------------------------------------- 1 | #ifdef RCT_NEW_ARCH_ENABLED 2 | #import "RNAuth0Spec.h" 3 | 4 | @interface A0Auth0 : NSObject 5 | #else 6 | #import 7 | 8 | @interface A0Auth0 : NSObject 9 | #endif 10 | 11 | @end 12 | 13 | @class NativeBridge; 14 | -------------------------------------------------------------------------------- /.github/workflows/sca_scan.yml: -------------------------------------------------------------------------------- 1 | name: SCA 2 | 3 | on: 4 | push: 5 | branches: ['master', 'main', '**'] 6 | 7 | jobs: 8 | snyk-cli: 9 | uses: auth0/devsecops-tooling/.github/workflows/sca-scan.yml@main 10 | with: 11 | additional-arguments: '--exclude=README.md' 12 | secrets: inherit 13 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "declaration": true 6 | }, 7 | "exclude": [ 8 | "example", 9 | "lib", 10 | "docs", 11 | "scripts", 12 | "babel.config.js", 13 | "jest.environment.js" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /example/ios/Auth0Example.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Auth0Example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/factory/index.ts: -------------------------------------------------------------------------------- 1 | // The React Native bundler (Metro) will automatically resolve this import 2 | // to 'Auth0ClientFactory.native.ts' for iOS/Android builds, and bundlers 3 | // like Webpack will resolve it to 'Auth0ClientFactory.web.ts' for web builds 4 | // (with the appropriate resolver configuration). 5 | export { Auth0ClientFactory } from './Auth0ClientFactory'; 6 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | nmHoistingLimits: workspaces 3 | 4 | plugins: 5 | - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs 6 | spec: '@yarnpkg/plugin-interactive-tools' 7 | - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs 8 | spec: '@yarnpkg/plugin-workspace-tools' 9 | 10 | yarnPath: .yarn/releases/yarn-3.6.1.cjs 11 | -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { getConfig } = require('react-native-builder-bob/babel-config'); 3 | const pkg = require('../package.json'); 4 | 5 | const root = path.resolve(__dirname, '..'); 6 | 7 | module.exports = getConfig( 8 | { 9 | presets: ['module:@react-native/babel-preset'], 10 | }, 11 | { root, pkg } 12 | ); 13 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: github-actions 5 | directory: / 6 | schedule: 7 | interval: daily 8 | - package-ecosystem: 'npm' 9 | directory: '/' 10 | schedule: 11 | interval: 'daily' 12 | ignore: 13 | - dependency-name: '*' 14 | update-types: ['version-update:semver-major'] 15 | -------------------------------------------------------------------------------- /src/core/models/index.ts: -------------------------------------------------------------------------------- 1 | export { AuthError } from './AuthError'; 2 | export { Credentials } from './Credentials'; 3 | export { Auth0User } from './Auth0User'; 4 | export { ApiCredentials } from './ApiCredentials'; 5 | export { CredentialsManagerError } from './CredentialsManagerError'; 6 | export { WebAuthError } from './WebAuthError'; 7 | export { DPoPError } from './DPoPError'; 8 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") } 2 | plugins { id("com.facebook.react.settings") } 3 | extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() } 4 | rootProject.name = 'Auth0Example' 5 | include ':app' 6 | includeBuild('../node_modules/@react-native/gradle-plugin') 7 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/platforms/web/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This is the primary entry point for the Web platform module. 3 | * 4 | * It exports the main client class, `WebAuth0Client`, which aggregates all 5 | * web-specific functionality and adapters built on top of `auth0-spa-js`. 6 | * This class is intended to be consumed by the `Auth0ClientFactory`. 7 | */ 8 | 9 | export { WebAuth0Client } from './adapters/WebAuth0Client'; 10 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | import { AppRegistry } from 'react-native'; 2 | import App from './src/App'; 3 | import { name as appName } from './app.json'; 4 | 5 | AppRegistry.registerComponent(appName, () => App); 6 | 7 | // This is the entry point for the React Native web application. 8 | if (typeof document !== 'undefined') { 9 | const rootTag = document.getElementById('root'); 10 | AppRegistry.runApplication(appName, { rootTag }); 11 | } 12 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This is the main entry point for all public-facing types used in the library. 3 | * It re-exports types from other modules for a clean and consolidated API surface. 4 | * 5 | * @example 6 | * ``` 7 | * import { Credentials, User, WebAuthorizeParameters } from 'react-native-auth0'; 8 | * ``` 9 | */ 10 | 11 | export * from './common'; 12 | export * from './parameters'; 13 | export * from './platform-specific'; 14 | -------------------------------------------------------------------------------- /scripts/replace-telemetry-version.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | const version = require('../package.json').version; 5 | const targetFile = path.resolve(__dirname, '../src/core/utils/telemetry.ts'); 6 | 7 | let content = fs.readFileSync(targetFile, 'utf8'); 8 | content = content.replace(/__SDK_VERSION__/g, version); 9 | 10 | fs.writeFileSync(targetFile, content); 11 | console.log(`SDK version set to ${version}`); 12 | -------------------------------------------------------------------------------- /.github/workflows/claude-code-review.yml: -------------------------------------------------------------------------------- 1 | name: Claude Code PR Review 2 | 3 | on: 4 | issue_comment: 5 | types: [created] 6 | pull_request_review_comment: 7 | types: [created] 8 | pull_request_review: 9 | types: [submitted] 10 | 11 | jobs: 12 | claude-review: 13 | permissions: 14 | contents: write 15 | issues: write 16 | pull-requests: write 17 | id-token: write 18 | uses: auth0/auth0-ai-pr-analyzer-gh-action/.github/workflows/claude-code-review.yml@main 19 | -------------------------------------------------------------------------------- /example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /src/platforms/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This module serves as a central registry for all platform-specific client implementations. 3 | * 4 | * The Auth0ClientFactory uses this file to dynamically import the correct client 5 | * based on the runtime environment. Adding a new platform (e.g., for desktop) 6 | * would simply involve creating a new platform module and exporting its client 7 | * class from this file. 8 | */ 9 | 10 | export { NativeAuth0Client } from './native'; 11 | export { WebAuth0Client } from './web'; 12 | -------------------------------------------------------------------------------- /example/metro.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { getDefaultConfig } = require('@react-native/metro-config'); 3 | const { getConfig } = require('react-native-builder-bob/metro-config'); 4 | 5 | const root = path.resolve(__dirname, '..'); 6 | 7 | /** 8 | * Metro configuration 9 | * https://facebook.github.io/metro/docs/configuration 10 | * 11 | * @type {import('metro-config').MetroConfig} 12 | */ 13 | module.exports = getConfig(getDefaultConfig(__dirname), { 14 | root, 15 | project: __dirname, 16 | }); 17 | -------------------------------------------------------------------------------- /.github/actions/get-version/action.yml: -------------------------------------------------------------------------------- 1 | name: Return the version extracted from the branch name 2 | 3 | # 4 | # Returns the version from the .version file. 5 | # 6 | # TODO: Remove once the common repo is public. 7 | # 8 | 9 | outputs: 10 | version: 11 | value: ${{ steps.get_version.outputs.VERSION }} 12 | 13 | runs: 14 | using: composite 15 | 16 | steps: 17 | - id: get_version 18 | shell: bash 19 | run: | 20 | VERSION=$(head -1 .version) 21 | echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT 22 | -------------------------------------------------------------------------------- /example/ios/.xcode.env: -------------------------------------------------------------------------------- 1 | # This `.xcode.env` file is versioned and is used to source the environment 2 | # used when running script phases inside Xcode. 3 | # To customize your local environment, you can create an `.xcode.env.local` 4 | # file that is not versioned. 5 | 6 | # NODE_BINARY variable contains the PATH to the node executable. 7 | # 8 | # Customize the NODE_BINARY variable here. 9 | # For example, to use nvm with brew, add the following line 10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use 11 | export NODE_BINARY=$(command -v node) 12 | -------------------------------------------------------------------------------- /example/src/api/auth0.ts: -------------------------------------------------------------------------------- 1 | import Auth0 from 'react-native-auth0'; 2 | import config from '../auth0-configuration'; 3 | 4 | const AUTH0_DOMAIN = config.domain; 5 | const AUTH0_CLIENT_ID = config.clientId; 6 | 7 | if (!AUTH0_DOMAIN || !AUTH0_CLIENT_ID) { 8 | throw new Error( 9 | 'Missing Auth0 credentials. Please add AUTH0_DOMAIN and AUTH0_CLIENT_ID to your environment variables.' 10 | ); 11 | } 12 | 13 | const auth0 = new Auth0({ 14 | domain: AUTH0_DOMAIN, 15 | clientId: AUTH0_CLIENT_ID, 16 | }); 17 | 18 | export default auth0; 19 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | precision: 2 3 | round: down 4 | range: '70...100' 5 | ignore: 6 | - 'src/networking/telemetry.ts' 7 | status: 8 | project: 9 | default: 10 | enabled: true 11 | target: auto 12 | threshold: 1% 13 | if_no_uploads: error 14 | patch: 15 | default: 16 | enabled: true 17 | target: 80% 18 | threshold: 30% 19 | if_no_uploads: error 20 | changes: 21 | default: 22 | enabled: true 23 | if_no_uploads: error 24 | comment: false 25 | -------------------------------------------------------------------------------- /example/react-native.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const pkg = require('../package.json'); 3 | 4 | module.exports = { 5 | project: { 6 | ios: { 7 | automaticPodsInstallation: true, 8 | }, 9 | }, 10 | dependencies: { 11 | [pkg.name]: { 12 | root: path.join(__dirname, '..'), 13 | platforms: { 14 | // Codegen script incorrectly fails without this 15 | // So we explicitly specify the platforms with empty object 16 | ios: {}, 17 | android: {}, 18 | }, 19 | }, 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /example/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version 4 | ruby ">= 2.6.10" 5 | 6 | # Exclude problematic versions of cocoapods and activesupport that causes build failures. 7 | gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1' 8 | gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0' 9 | gem 'xcodeproj', '< 1.26.0' 10 | gem 'concurrent-ruby', '< 1.3.4' 11 | 12 | # Ruby 3.4.0 has removed some libraries from the standard library. 13 | gem 'bigdecimal' 14 | gem 'logger' 15 | gem 'benchmark' 16 | gem 'mutex_m' 17 | -------------------------------------------------------------------------------- /src/core/utils/deepEqual.ts: -------------------------------------------------------------------------------- 1 | export function deepEqual(x: T, y: T): boolean { 2 | if (x === y) { 3 | return true; 4 | } else if ( 5 | typeof x == 'object' && 6 | x != null && 7 | typeof y == 'object' && 8 | y != null 9 | ) { 10 | if (Object.keys(x).length != Object.keys(y).length) return false; 11 | 12 | for (const prop in x) { 13 | if (Object.prototype.hasOwnProperty.call(y, prop)) { 14 | if (!deepEqual(x[prop], y[prop])) return false; 15 | } else return false; 16 | } 17 | return true; 18 | } else { 19 | return false; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | AuthError, 3 | CredentialsManagerError, 4 | WebAuthError, 5 | DPoPError, 6 | } from './core/models'; 7 | export { TimeoutError } from './core/utils/fetchWithTimeout'; 8 | export { TokenType } from './types/common'; 9 | export { Auth0Provider } from './hooks/Auth0Provider'; 10 | export { useAuth0 } from './hooks/useAuth0'; 11 | export * from './types'; 12 | export type { 13 | LocalAuthenticationLevel, 14 | LocalAuthenticationOptions, 15 | LocalAuthenticationStrategy, 16 | } from './types/platform-specific'; 17 | 18 | // Re-export Auth0 as default 19 | export { default } from './Auth0'; 20 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | buildToolsVersion = "36.0.0" 4 | minSdkVersion = 24 5 | compileSdkVersion = 36 6 | targetSdkVersion = 36 7 | ndkVersion = "27.1.12297006" 8 | kotlinVersion = "2.1.20" 9 | } 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | dependencies { 15 | classpath("com.android.tools.build:gradle") 16 | classpath("com.facebook.react:react-native-gradle-plugin") 17 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin") 18 | } 19 | } 20 | 21 | apply plugin: "com.facebook.react.rootproject" 22 | -------------------------------------------------------------------------------- /example/src/components/Header.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View, Text, StyleSheet } from 'react-native'; 3 | 4 | type Props = { 5 | title: string; 6 | }; 7 | 8 | const Header = ({ title }: Props) => { 9 | return ( 10 | 11 | {title} 12 | 13 | ); 14 | }; 15 | 16 | const styles = StyleSheet.create({ 17 | container: { 18 | padding: 20, 19 | backgroundColor: '#F5F5F5', 20 | borderBottomWidth: 1, 21 | borderBottomColor: '#E0E0E0', 22 | alignItems: 'center', 23 | }, 24 | title: { 25 | fontSize: 22, 26 | fontWeight: 'bold', 27 | color: '#212121', 28 | }, 29 | }); 30 | 31 | export default Header; 32 | -------------------------------------------------------------------------------- /src/hooks/useAuth0.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { Auth0Context, type Auth0ContextInterface } from './Auth0Context'; 3 | 4 | /** 5 | * The primary hook for interacting with the Auth0 SDK in a React component. 6 | * 7 | * It provides access to the authentication state (`user`, `error`, `isLoading`) 8 | * and methods for performing authentication (`authorize`, `clearSession`, etc.). 9 | * 10 | * @example 11 | * ``` 12 | * const { user, authorize, clearSession, isLoading } = useAuth0(); 13 | * ``` 14 | * 15 | * @returns The current authentication state and methods. 16 | */ 17 | export const useAuth0 = (): Auth0ContextInterface => { 18 | const context = useContext(Auth0Context); 19 | return context; 20 | }; 21 | -------------------------------------------------------------------------------- /A0Auth0.podspec: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) 4 | 5 | Pod::Spec.new do |s| 6 | s.name = 'A0Auth0' 7 | s.version = package['version'] 8 | s.summary = package['description'] 9 | s.homepage = package['repository']['baseUrl'] 10 | s.license = package['license'] 11 | s.authors = package['author'] 12 | s.platforms = { :ios => min_ios_version_supported } 13 | s.swift_version = '5.0' 14 | s.source = { :git => 'https://github.com/auth0/react-native-auth0.git', :tag => "v#{s.version}" } 15 | 16 | s.source_files = 'ios/**/*.{h,m,mm,swift}' 17 | s.requires_arc = true 18 | 19 | s.dependency 'Auth0', '2.14' 20 | 21 | install_modules_dependencies(s) 22 | end 23 | -------------------------------------------------------------------------------- /example/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StatusBar } from 'react-native'; 3 | import { NavigationContainer } from '@react-navigation/native'; 4 | import RootNavigator from './navigation/RootNavigator'; 5 | 6 | /** 7 | * The absolute root component of the example application. 8 | * 9 | * It sets up the main navigation container and renders the RootNavigator, 10 | * which then decides which demo flow (Hooks or Class-based) to display. 11 | * The Auth0Provider is now scoped within the Hooks demo flow itself. 12 | */ 13 | function App(): React.JSX.Element { 14 | return ( 15 | 16 | 17 | 18 | 19 | ); 20 | } 21 | 22 | export default App; 23 | -------------------------------------------------------------------------------- /example/src/navigation/AuthStackNavigator.tsx: -------------------------------------------------------------------------------- 1 | // example/src/navigation/AuthStackNavigator.tsx 2 | 3 | import React from 'react'; 4 | import { createStackNavigator } from '@react-navigation/stack'; 5 | import HomeScreen from '../screens/hooks/Home'; 6 | 7 | export type AuthStackParamList = { 8 | Home: undefined; 9 | }; 10 | 11 | const Stack = createStackNavigator(); 12 | 13 | /** 14 | * Navigator for the unauthenticated part of the Hooks-based demo. 15 | * It displays the main login screen. 16 | */ 17 | const AuthStackNavigator = () => { 18 | return ( 19 | 20 | 25 | 26 | ); 27 | }; 28 | 29 | export default AuthStackNavigator; 30 | -------------------------------------------------------------------------------- /src/core/utils/scope.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Ensures that the 'openid' scope is always present, which is required 3 | * for receiving an ID token. It also adds 'profile' and 'email' as 4 | * sensible defaults if no other scopes are requested. 5 | * 6 | * @param inputScopes A space-separated string of scopes provided by the user. 7 | * @returns A finalized, space-separated string of scopes. 8 | */ 9 | export function finalizeScope(inputScopes?: string): string { 10 | const defaultScopes = ['openid', 'profile', 'email']; 11 | 12 | if (!inputScopes?.trim()) { 13 | return defaultScopes.join(' '); 14 | } 15 | 16 | const providedScopes = new Set(inputScopes.split(' ').filter((s) => s)); 17 | 18 | // Ensure 'openid' is always included. 19 | providedScopes.add('openid'); 20 | 21 | return Array.from(providedScopes).join(' '); 22 | } 23 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | types: 6 | - opened 7 | - synchronize 8 | 9 | permissions: {} 10 | 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 13 | cancel-in-progress: ${{ github.ref != 'refs/heads/master' }} 14 | 15 | jobs: 16 | test: 17 | name: Run JS tests 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 23 | 24 | - name: Setup 25 | uses: ./.github/actions/setup 26 | 27 | - name: Run tests 28 | run: yarn test:ci 29 | 30 | - name: Upload coverage report 31 | uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de 32 | with: 33 | directory: coverage 34 | -------------------------------------------------------------------------------- /.github/actions/get-prerelease/action.yml: -------------------------------------------------------------------------------- 1 | name: Return a boolean indicating if the version contains prerelease identifiers 2 | 3 | # 4 | # Returns a simple true/false boolean indicating whether the version indicates it's a prerelease or not. 5 | # 6 | # TODO: Remove once the common repo is public. 7 | # 8 | 9 | inputs: 10 | version: 11 | required: true 12 | 13 | outputs: 14 | prerelease: 15 | value: ${{ steps.get_prerelease.outputs.PRERELEASE }} 16 | 17 | runs: 18 | using: composite 19 | 20 | steps: 21 | - id: get_prerelease 22 | shell: bash 23 | run: | 24 | if [[ "${VERSION}" == *"beta"* || "${VERSION}" == *"alpha"* ]]; then 25 | echo "PRERELEASE=true" >> $GITHUB_OUTPUT 26 | else 27 | echo "PRERELEASE=false" >> $GITHUB_OUTPUT 28 | fi 29 | env: 30 | VERSION: ${{ inputs.version }} 31 | -------------------------------------------------------------------------------- /src/core/utils/fetchWithTimeout.ts: -------------------------------------------------------------------------------- 1 | import { AuthError } from '../models'; 2 | 3 | export class TimeoutError extends AuthError { 4 | constructor(message: string) { 5 | super('TimeoutError', message, { code: 'timeout' }); 6 | } 7 | } 8 | 9 | export function fetchWithTimeout( 10 | url: RequestInfo, 11 | options: RequestInit, 12 | timeoutMs: number 13 | ): Promise { 14 | const controller = new AbortController(); 15 | const signal = controller.signal; 16 | options.signal = signal; 17 | 18 | const timeout = setTimeout(() => controller.abort(), timeoutMs); 19 | 20 | return fetch(url, options) 21 | .catch((err) => { 22 | if (err.name === 'AbortError') { 23 | throw new TimeoutError(`Request timed out after ${timeoutMs}ms`); 24 | } 25 | throw err; 26 | }) 27 | .finally(() => { 28 | clearTimeout(timeout); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /jest.environment.js: -------------------------------------------------------------------------------- 1 | import JSDOMEnvironment from 'jest-environment-jsdom'; 2 | import fetch, { Headers, Request, Response } from 'node-fetch'; 3 | import { TextEncoder, TextDecoder } from 'util'; 4 | 5 | /** 6 | * Custom Jest Environment based on JSDOMEnvironment to support TextEncoder and TextDecoder. 7 | * 8 | * ref: https://github.com/jsdom/jsdom/issues/2524 9 | */ 10 | export default class CustomJSDOMEnvironment extends JSDOMEnvironment { 11 | constructor(config, context) { 12 | super(config, context); 13 | } 14 | 15 | async setup() { 16 | await super.setup(); 17 | 18 | this.global.fetch = fetch; 19 | this.global.Headers = Headers; 20 | this.global.Request = Request; 21 | this.global.Response = Response; 22 | this.global.TextEncoder = TextEncoder; 23 | this.global.TextDecoder = TextDecoder; 24 | } 25 | } 26 | 27 | module.exports = CustomJSDOMEnvironment; 28 | -------------------------------------------------------------------------------- /android/src/main/java/com/auth0/react/ApiCredentialsParser.kt: -------------------------------------------------------------------------------- 1 | package com.auth0.react 2 | 3 | import com.auth0.android.result.APICredentials 4 | import com.facebook.react.bridge.Arguments 5 | import com.facebook.react.bridge.ReadableMap 6 | 7 | object ApiCredentialsParser { 8 | 9 | private const val ACCESS_TOKEN_KEY = "accessToken" 10 | private const val EXPIRES_AT_KEY = "expiresAt" 11 | private const val SCOPE_KEY = "scope" 12 | private const val TOKEN_TYPE_KEY = "tokenType" 13 | 14 | fun toMap(credentials: APICredentials): ReadableMap { 15 | val map = Arguments.createMap() 16 | map.putString(ACCESS_TOKEN_KEY, credentials.accessToken) 17 | map.putDouble(EXPIRES_AT_KEY, credentials.expiresAt.time / 1000.0) 18 | map.putString(SCOPE_KEY, credentials.scope) 19 | map.putString(TOKEN_TYPE_KEY, credentials.type) 20 | return map 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.github/actions/npm-publish/action.yml: -------------------------------------------------------------------------------- 1 | name: Publish release to npm 2 | 3 | inputs: 4 | version: 5 | required: true 6 | release-directory: 7 | default: './' 8 | 9 | runs: 10 | using: composite 11 | 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v4 15 | 16 | - name: Setup 17 | uses: ./.github/actions/setup 18 | 19 | - name: Build package 20 | shell: bash 21 | run: yarn ci 22 | 23 | - name: Publish release to NPM 24 | shell: bash 25 | working-directory: ${{ inputs.release-directory }} 26 | run: | 27 | if [[ "${VERSION}" == *"beta"* ]]; then 28 | TAG="beta" 29 | elif [[ "${VERSION}" == *"alpha"* ]]; then 30 | TAG="alpha" 31 | else 32 | TAG="latest" 33 | fi 34 | npm publish --provenance --tag $TAG 35 | env: 36 | VERSION: ${{ inputs.version }} 37 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/auth0example/MainApplication.kt: -------------------------------------------------------------------------------- 1 | package com.auth0example 2 | 3 | import android.app.Application 4 | import com.facebook.react.PackageList 5 | import com.facebook.react.ReactApplication 6 | import com.facebook.react.ReactHost 7 | import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative 8 | import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost 9 | 10 | class MainApplication : Application(), ReactApplication { 11 | 12 | override val reactHost: ReactHost by lazy { 13 | getDefaultReactHost( 14 | context = applicationContext, 15 | packageList = 16 | PackageList(this).packages.apply { 17 | // Packages that cannot be autolinked yet can be added manually here, for example: 18 | // add(MyReactNativePackage()) 19 | }, 20 | ) 21 | } 22 | 23 | override fun onCreate() { 24 | super.onCreate() 25 | loadReactNative(this) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "rootDir": ".", 4 | "paths": { 5 | "react-native-auth0": ["./src/index"] 6 | }, 7 | "allowUnreachableCode": false, 8 | "allowUnusedLabels": false, 9 | "esModuleInterop": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "jsx": "react-jsx", 12 | "lib": ["ESNext"], 13 | "module": "ESNext", 14 | "moduleResolution": "bundler", 15 | "noEmit": true, 16 | "noFallthroughCasesInSwitch": true, 17 | "noImplicitReturns": true, 18 | "noImplicitUseStrict": false, 19 | "noStrictGenericChecks": false, 20 | "noUncheckedIndexedAccess": true, 21 | "noUnusedLocals": true, 22 | "noUnusedParameters": true, 23 | "resolveJsonModule": true, 24 | "skipLibCheck": true, 25 | "strict": true, 26 | "target": "ESNext", 27 | "verbatimModuleSyntax": true 28 | }, 29 | "exclude": ["example", "node_modules", "lib", "docs", "**/__tests__/**"] 30 | } 31 | -------------------------------------------------------------------------------- /example/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Auth0 React Native Example 9 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/core/interfaces/IUsersClient.ts: -------------------------------------------------------------------------------- 1 | import type { User, GetUserParameters, PatchUserParameters } from '../../types'; 2 | 3 | /** 4 | * Defines the contract for a client that interacts with the Auth0 Management API's 5 | * user endpoints. An instance of this client is typically created with a 6 | * user-specific management token. 7 | */ 8 | export interface IUsersClient { 9 | /** 10 | * Retrieves the full profile of a user from the Management API. 11 | * 12 | * @param parameters The parameters containing the user's ID. 13 | * @returns A promise that resolves with the user's full profile. 14 | */ 15 | getUser(parameters: GetUserParameters): Promise; 16 | 17 | /** 18 | * Updates a user's `user_metadata`. 19 | * 20 | * @param parameters The parameters containing the user's ID and the metadata to update. 21 | * @returns A promise that resolves with the updated user profile. 22 | */ 23 | patchUser(parameters: PatchUserParameters): Promise; 24 | } 25 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/auth0example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.auth0example 2 | 3 | import com.facebook.react.ReactActivity 4 | import com.facebook.react.ReactActivityDelegate 5 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled 6 | import com.facebook.react.defaults.DefaultReactActivityDelegate 7 | 8 | class MainActivity : ReactActivity() { 9 | 10 | /** 11 | * Returns the name of the main component registered from JavaScript. This is used to schedule 12 | * rendering of the component. 13 | */ 14 | override fun getMainComponentName(): String = "Auth0Example" 15 | 16 | /** 17 | * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] 18 | * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] 19 | */ 20 | override fun createReactActivityDelegate(): ReactActivityDelegate = 21 | DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) 22 | } 23 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | 3 | # Number of days of inactivity before an Issue or Pull Request becomes stale 4 | daysUntilStale: 90 5 | 6 | # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. 7 | daysUntilClose: 7 8 | 9 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable 10 | exemptLabels: [] 11 | 12 | # Set to true to ignore issues with an assignee (defaults to false) 13 | exemptAssignees: true 14 | 15 | # Label to use when marking as stale 16 | staleLabel: closed:stale 17 | 18 | # Comment to post when marking as stale. Set to `false` to disable 19 | markComment: > 20 | This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you have not received a response for our team (apologies for the delay) and this is still a blocker, please reply with additional information or just a ping. Thank you for your contribution! 🙇‍♂️ 21 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | ## Running the Example Application 🏃‍♂️ 2 | 3 | The Example application can be used for development purpose of the SDK. To integrate with Auth0, it is better to use the [Quickstart](https://auth0.com/docs/quickstart/native/react-native/interactive) and [Sample App](https://github.com/auth0-samples/auth0-react-native-sample/tree/master/00-Login-Hooks) applications 4 | 5 | To run the example application inside the repository, follow these steps: 6 | 7 | 1. Open a terminal or command prompt. 8 | 2. Run `yarn bootstrap` to set up the project. 9 | 3. Run `yarn ci` to build the project. 10 | 4. To run the application: 11 | For Android, run `yarn example:android`. 12 | For iOS, run `yarn example:ios`. 13 | 14 | The application will be built and launched on the specified platform, allowing you to interact with it. 15 | 16 | ### To run on different Auth0 Application 17 | 18 | 1. Change the `clientId` and `domain` value in `example/src/auth0-configuration.js` 19 | 2. For Android, Change the `android:host` values in `example/android/app/src/main/AndroidManifest.xml` 20 | -------------------------------------------------------------------------------- /src/factory/Auth0ClientFactory.web.ts: -------------------------------------------------------------------------------- 1 | import type { IAuth0Client } from '../core/interfaces'; 2 | import type { Auth0Options } from '../types'; 3 | import type { WebAuth0Options } from '../types/platform-specific'; 4 | import { validateAuth0Options } from '../core/utils'; 5 | 6 | // This file ONLY imports the Web client. 7 | import { WebAuth0Client } from '../platforms/web'; 8 | 9 | /** 10 | * A factory class responsible for creating the Web-specific 11 | * IAuth0Client instance. This file is automatically selected by bundlers 12 | * like Webpack when targeting the web. 13 | */ 14 | export class Auth0ClientFactory { 15 | /** 16 | * Creates and returns a WebAuth0Client instance. 17 | * 18 | * @param options The configuration options for the Auth0 client. 19 | * @returns An instance of WebAuth0Client. 20 | */ 21 | static createClient(options: Auth0Options): IAuth0Client { 22 | validateAuth0Options(options); 23 | 24 | // No platform detection needed. We know we are on the web. 25 | return new WebAuth0Client(options as WebAuth0Options); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/plugin/__tests__/fixtures/buildgradle.ts: -------------------------------------------------------------------------------- 1 | export default ` 2 | android { 3 | compileSdkVersion rootProject.ext.compileSdkVersion 4 | 5 | compileOptions { 6 | sourceCompatibility JavaVersion.VERSION_1_8 7 | targetCompatibility JavaVersion.VERSION_1_8 8 | } 9 | 10 | defaultConfig { 11 | applicationId 'com.example.app' 12 | minSdkVersion rootProject.ext.minSdkVersion 13 | targetSdkVersion rootProject.ext.targetSdkVersion 14 | versionCode 82 15 | versionName "1.0.0" 16 | } 17 | 18 | splits { 19 | abi { 20 | reset() 21 | enable enableSeparateBuildPerCPUArchitecture 22 | universalApk false // If true, also generate a universal APK 23 | include "armeabi-v7a", "x86", "arm64-v8a", "x86_64" 24 | } 25 | } 26 | 27 | signingConfigs { 28 | debug { 29 | storeFile file('debug.keystore') 30 | storePassword 'android' 31 | keyAlias 'androiddebugkey' 32 | keyPassword 'android' 33 | } 34 | } 35 | `; 36 | -------------------------------------------------------------------------------- /example/src/components/LabeledInput.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | View, 4 | Text, 5 | TextInput, 6 | StyleSheet, 7 | TextInputProps, 8 | } from 'react-native'; 9 | 10 | type Props = TextInputProps & { 11 | label: string; 12 | }; 13 | 14 | const LabeledInput = ({ label, ...props }: Props) => { 15 | return ( 16 | 17 | {label} 18 | 23 | 24 | ); 25 | }; 26 | 27 | const styles = StyleSheet.create({ 28 | container: { 29 | width: '100%', 30 | marginBottom: 16, 31 | }, 32 | label: { 33 | marginBottom: 8, 34 | fontSize: 14, 35 | color: '#424242', 36 | fontWeight: '500', 37 | }, 38 | input: { 39 | backgroundColor: '#FAFAFA', 40 | borderWidth: 1, 41 | borderColor: '#E0E0E0', 42 | borderRadius: 8, 43 | padding: 12, 44 | fontSize: 16, 45 | color: '#212121', 46 | }, 47 | }); 48 | 49 | export default LabeledInput; 50 | -------------------------------------------------------------------------------- /src/factory/Auth0ClientFactory.ts: -------------------------------------------------------------------------------- 1 | import type { IAuth0Client } from '../core/interfaces'; 2 | import type { Auth0Options } from '../types'; 3 | import type { NativeAuth0Options } from '../types/platform-specific'; 4 | import { validateAuth0Options } from '../core/utils'; 5 | 6 | // This file ONLY imports the Native client. 7 | import { NativeAuth0Client } from '../platforms/native'; 8 | 9 | /** 10 | * A factory class responsible for creating the Native-specific 11 | * IAuth0Client instance. This file is automatically selected by the Metro 12 | * bundler during a build for iOS or Android. 13 | */ 14 | export class Auth0ClientFactory { 15 | /** 16 | * Creates and returns a NativeAuth0Client instance. 17 | * 18 | * @param options The configuration options for the Auth0 client. 19 | * @returns An instance of NativeAuth0Client. 20 | */ 21 | static createClient(options: Auth0Options): IAuth0Client { 22 | validateAuth0Options(options); 23 | 24 | // No platform detection needed. We know we are on native. 25 | return new NativeAuth0Client(options as NativeAuth0Options); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /example/ios/Auth0Example/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "idiom": "iphone", 5 | "scale": "2x", 6 | "size": "20x20" 7 | }, 8 | { 9 | "idiom": "iphone", 10 | "scale": "3x", 11 | "size": "20x20" 12 | }, 13 | { 14 | "idiom": "iphone", 15 | "scale": "2x", 16 | "size": "29x29" 17 | }, 18 | { 19 | "idiom": "iphone", 20 | "scale": "3x", 21 | "size": "29x29" 22 | }, 23 | { 24 | "idiom": "iphone", 25 | "scale": "2x", 26 | "size": "40x40" 27 | }, 28 | { 29 | "idiom": "iphone", 30 | "scale": "3x", 31 | "size": "40x40" 32 | }, 33 | { 34 | "idiom": "iphone", 35 | "scale": "2x", 36 | "size": "60x60" 37 | }, 38 | { 39 | "idiom": "iphone", 40 | "scale": "3x", 41 | "size": "60x60" 42 | }, 43 | { 44 | "idiom": "ios-marketing", 45 | "scale": "1x", 46 | "size": "1024x1024" 47 | } 48 | ], 49 | "info": { 50 | "author": "xcode", 51 | "version": 1 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /.github/actions/tag-exists/action.yml: -------------------------------------------------------------------------------- 1 | name: Return a boolean indicating if a tag already exists for the repository 2 | 3 | # 4 | # Returns a simple true/false boolean indicating whether the tag exists or not. 5 | # 6 | # TODO: Remove once the common repo is public. 7 | # 8 | 9 | inputs: 10 | token: 11 | required: true 12 | tag: 13 | required: true 14 | 15 | outputs: 16 | exists: 17 | description: 'Whether the tag exists or not' 18 | value: ${{ steps.tag-exists.outputs.EXISTS }} 19 | 20 | runs: 21 | using: composite 22 | 23 | steps: 24 | - id: tag-exists 25 | shell: bash 26 | run: | 27 | GET_API_URL="https://api.github.com/repos/${GITHUB_REPOSITORY}/git/ref/tags/${TAG_NAME}" 28 | http_status_code=$(curl -LI $GET_API_URL -o /dev/null -w '%{http_code}\n' -s -H "Authorization: token ${GITHUB_TOKEN}") 29 | if [ "$http_status_code" -ne "404" ] ; then 30 | echo "EXISTS=true" >> $GITHUB_OUTPUT 31 | else 32 | echo "EXISTS=false" >> $GITHUB_OUTPUT 33 | fi 34 | env: 35 | TAG_NAME: ${{ inputs.tag }} 36 | GITHUB_TOKEN: ${{ inputs.token }} 37 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Create npm and GitHub Release 2 | 3 | on: 4 | pull_request: 5 | types: 6 | - closed 7 | workflow_dispatch: 8 | 9 | permissions: 10 | contents: write 11 | id-token: write # For publishing to npm using --provenance 12 | pages: write # For publishing documentation 13 | 14 | ### TODO: Replace instances of './.github/workflows/' w/ `auth0/dx-sdk-actions/workflows/` and append `@latest` after the common `dx-sdk-actions` repo is made public. 15 | ### TODO: Also remove `get-prerelease`, `get-release-notes`, `get-version`, `npm-publish`, `release-create`, and `tag-exists` actions from this repo's .github/actions folder once the repo is public. 16 | ### TODO: Also remove `npm-release` workflow from this repo's .github/workflows folder once the repo is public. 17 | 18 | jobs: 19 | release: 20 | uses: ./.github/workflows/npm-release.yml 21 | secrets: 22 | github-token: ${{ secrets.GITHUB_TOKEN }} 23 | 24 | publish-docs: 25 | needs: release 26 | if: success() 27 | uses: ./.github/workflows/publish-docs.yml 28 | permissions: 29 | contents: write 30 | pages: write 31 | id-token: write 32 | -------------------------------------------------------------------------------- /example/ios/Auth0Example/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyAccessedAPITypes 6 | 7 | 8 | NSPrivacyAccessedAPIType 9 | NSPrivacyAccessedAPICategoryFileTimestamp 10 | NSPrivacyAccessedAPITypeReasons 11 | 12 | C617.1 13 | 14 | 15 | 16 | NSPrivacyAccessedAPIType 17 | NSPrivacyAccessedAPICategoryUserDefaults 18 | NSPrivacyAccessedAPITypeReasons 19 | 20 | CA92.1 21 | 22 | 23 | 24 | NSPrivacyAccessedAPIType 25 | NSPrivacyAccessedAPICategorySystemBootTime 26 | NSPrivacyAccessedAPITypeReasons 27 | 28 | 35F9.1 29 | 30 | 31 | 32 | NSPrivacyCollectedDataTypes 33 | 34 | NSPrivacyTracking 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Auth0, Inc. (http://auth0.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | package-lock.json # include if you are using npm - don't use both yarn and npm 37 | npm-debug.log 38 | yarn-error.log 39 | yarn.lock # include if you are using yarn - don't use both npm and yarn 40 | 41 | # BUCK 42 | buck-out/ 43 | \.buckd/ 44 | *.keystore 45 | 46 | # Fastlane 47 | # 48 | # It is recommended to not store the screenshots in the git repo. Instead, use Fastlane to re-generate the 49 | # screenshots whenever they are needed. 50 | # For more information about the recommended setup visit: 51 | # https://docs.fastlane.tools/best-practices/source-control/ 52 | 53 | */fastlane/report.xml 54 | */fastlane/Preview.html 55 | */fastlane/screenshots 56 | 57 | # Bundle artifact 58 | *.jsbundle -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 14 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/plugin/__tests__/fixtures/swiftappdelegate-withoutlinking.ts: -------------------------------------------------------------------------------- 1 | export default ` 2 | // Swift AppDelegate template for Expo 53+ 3 | 4 | import UIKit 5 | import React 6 | import React_RCTAppDelegate 7 | import ReactAppDependencyProvider 8 | 9 | @main 10 | class AppDelegate: RCTAppDelegate { 11 | override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { 12 | self.moduleName = "Auth0Example" 13 | self.dependencyProvider = RCTAppDependencyProvider() 14 | 15 | // You can add your custom initial props in the dictionary below. 16 | // They will be passed down to the ViewController used by React Native. 17 | self.initialProps = [:] 18 | 19 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 20 | } 21 | 22 | override func sourceURL(for bridge: RCTBridge) -> URL? { 23 | self.bundleURL() 24 | } 25 | 26 | override func bundleURL() -> URL? { 27 | #if DEBUG 28 | RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") 29 | #else 30 | Bundle.main.url(forResource: "main", withExtension: "jsbundle") 31 | #endif 32 | } 33 | } 34 | `; 35 | -------------------------------------------------------------------------------- /.github/actions/release-create/action.yml: -------------------------------------------------------------------------------- 1 | name: Create a GitHub release 2 | 3 | # 4 | # Creates a GitHub release with the given version. 5 | # 6 | # TODO: Remove once the common repo is public. 7 | # 8 | 9 | inputs: 10 | token: 11 | required: true 12 | files: 13 | required: false 14 | name: 15 | required: true 16 | body: 17 | required: true 18 | tag: 19 | required: true 20 | commit: 21 | required: true 22 | draft: 23 | default: false 24 | required: false 25 | prerelease: 26 | default: false 27 | required: false 28 | fail_on_unmatched_files: 29 | default: true 30 | required: false 31 | 32 | runs: 33 | using: composite 34 | 35 | steps: 36 | - uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 37 | with: 38 | body: ${{ inputs.body }} 39 | name: ${{ inputs.name }} 40 | tag_name: ${{ inputs.tag }} 41 | target_commitish: ${{ inputs.commit }} 42 | draft: ${{ inputs.draft }} 43 | prerelease: ${{ inputs.prerelease }} 44 | fail_on_unmatched_files: ${{ inputs.fail_on_unmatched_files }} 45 | files: ${{ inputs.files }} 46 | env: 47 | GITHUB_TOKEN: ${{ inputs.token }} 48 | -------------------------------------------------------------------------------- /src/core/models/ApiCredentials.ts: -------------------------------------------------------------------------------- 1 | import type { ApiCredentials as IApiCredentials } from '../../types'; 2 | 3 | /** 4 | * A class representation of API-specific user credentials. 5 | * It encapsulates the tokens and provides helper methods for convenience. 6 | */ 7 | export class ApiCredentials implements IApiCredentials { 8 | public accessToken: string; 9 | public tokenType: string; 10 | public expiresAt: number; 11 | public scope?: string; 12 | 13 | /** 14 | * Creates an instance of ApiCredentials. 15 | * 16 | * @param params An object conforming to the ApiCredentials type definition. 17 | */ 18 | constructor(params: IApiCredentials) { 19 | this.accessToken = params.accessToken; 20 | this.tokenType = params.tokenType; 21 | this.expiresAt = params.expiresAt; 22 | this.scope = params.scope; 23 | } 24 | 25 | /** 26 | * Checks if the access token is expired. 27 | * 28 | * @param [leeway=0] - A buffer in seconds to account for clock skew. The token will be 29 | * considered expired if it expires within this buffer. 30 | * @returns `true` if the token is expired, `false` otherwise. 31 | */ 32 | isExpired(leeway: number = 0): boolean { 33 | const nowInSeconds = Math.floor(Date.now() / 1000); 34 | return this.expiresAt <= nowInSeconds + leeway; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /scripts/ensure-yarn.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // scripts/ensure-yarn.js 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | 6 | function deleteFile(filePath) { 7 | if (fs.existsSync(filePath)) { 8 | fs.unlinkSync(filePath); 9 | console.log(`Deleted file: ${filePath}`); 10 | } 11 | } 12 | 13 | function deleteDirectory(directoryPath) { 14 | if (fs.existsSync(directoryPath)) { 15 | fs.rmSync(directoryPath, { recursive: true, force: true }); 16 | console.log(`Deleted directory: ${directoryPath}`); 17 | } 18 | } 19 | 20 | if (!process.env.npm_config_user_agent?.includes('yarn')) { 21 | // Clean forbidden files and directories 22 | deleteFile(path.join(__dirname, '../package-lock.json')); 23 | deleteFile(path.join(__dirname, '../pnpm-lock.yaml')); 24 | deleteDirectory(path.join(__dirname, '../node_modules')); 25 | 26 | const examplePath = path.join(__dirname, '../example'); 27 | deleteFile(path.join(examplePath, 'package-lock.json')); 28 | deleteFile(path.join(examplePath, 'pnpm-lock.yaml')); 29 | deleteDirectory(path.join(examplePath, 'node_modules')); 30 | 31 | console.warn('Warning: We have removed node_modules and lockfiles'); 32 | console.error( 33 | 'Error: This project uses Yarn. Please use "yarn install" instead of npm or pnpm to avoid dependency issues.' 34 | ); 35 | process.exit(1); 36 | } 37 | -------------------------------------------------------------------------------- /.github/actions/setup/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup 2 | description: Setup Node.js and install dependencies 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | - name: Setup Node.js 8 | uses: actions/setup-node@v4 9 | with: 10 | node-version-file: .nvmrc 11 | registry-url: 'https://registry.npmjs.org' 12 | 13 | - name: Install npm 11 # required to use npm OIDC 14 | shell: bash 15 | run: npm install -g npm@11 16 | 17 | - name: Restore dependencies 18 | id: yarn-cache 19 | uses: actions/cache/restore@v4 20 | with: 21 | path: | 22 | **/node_modules 23 | .yarn/install-state.gz 24 | key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}-${{ hashFiles('**/package.json', '!node_modules/**') }} 25 | restore-keys: | 26 | ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} 27 | ${{ runner.os }}-yarn- 28 | 29 | - name: Install dependencies 30 | if: steps.yarn-cache.outputs.cache-hit != 'true' 31 | run: yarn install --immutable 32 | shell: bash 33 | 34 | - name: Cache dependencies 35 | if: steps.yarn-cache.outputs.cache-hit != 'true' 36 | uses: actions/cache/save@v4 37 | with: 38 | path: | 39 | **/node_modules 40 | .yarn/install-state.gz 41 | key: ${{ steps.yarn-cache.outputs.cache-primary-key }} 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # XDE 6 | .expo/ 7 | 8 | # VSCode 9 | .vscode/ 10 | jsconfig.json 11 | 12 | # Xcode 13 | # 14 | build/ 15 | *.pbxuser 16 | !default.pbxuser 17 | *.mode1v3 18 | !default.mode1v3 19 | *.mode2v3 20 | !default.mode2v3 21 | *.perspectivev3 22 | !default.perspectivev3 23 | xcuserdata 24 | *.xccheckout 25 | *.moved-aside 26 | DerivedData 27 | *.hmap 28 | *.ipa 29 | *.xcuserstate 30 | project.xcworkspace 31 | **/.xcode.env.local 32 | 33 | # Android/IJ 34 | # 35 | .classpath 36 | .cxx 37 | .gradle 38 | .idea 39 | .project 40 | .settings 41 | local.properties 42 | android.iml 43 | 44 | # Cocoapods 45 | # 46 | example/ios/Pods 47 | 48 | # Ruby 49 | example/vendor/ 50 | 51 | # Yarn 52 | .yarn/* 53 | !.yarn/patches 54 | !.yarn/plugins 55 | !.yarn/releases 56 | !.yarn/sdks 57 | !.yarn/versions 58 | 59 | # node.js 60 | # 61 | node_modules/ 62 | npm-debug.log 63 | yarn-debug.log 64 | yarn-error.log 65 | 66 | # BUCK 67 | buck-out/ 68 | \.buckd/ 69 | android/app/libs 70 | android/keystores/debug.keystore 71 | .kotlin/ 72 | 73 | # Yarn 74 | .yarn/* 75 | !.yarn/patches 76 | !.yarn/plugins 77 | !.yarn/releases 78 | !.yarn/sdks 79 | !.yarn/versions 80 | 81 | # Expo 82 | .expo/ 83 | 84 | # Turborepo 85 | .turbo/ 86 | 87 | # generated by bob 88 | lib/ 89 | 90 | # React Native Codegen 91 | ios/generated 92 | android/generated 93 | 94 | # Other 95 | dist/ 96 | out/ 97 | coverage/ 98 | docs/ -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Changes 2 | 3 | Please describe both what is changing and why this is important. Include: 4 | 5 | - Endpoints added, deleted, deprecated, or changed 6 | - Classes and methods added, deleted, deprecated, or changed 7 | - Screenshots of new or changed UI, if applicable 8 | - A summary of usage if this is a new feature or change to a public API (this should also be added to relevant documentation once released) 9 | 10 | ### References 11 | 12 | Please include relevant links supporting this change such as a: 13 | 14 | - support ticket 15 | - community post 16 | - StackOverflow post 17 | - support forum thread 18 | 19 | Please note any links that are not publicly accessible. 20 | 21 | ### Testing 22 | 23 | Please describe how this can be tested by reviewers. Be specific about anything not tested and reasons why. If this library has unit and/or integration testing, tests should be added for new functionality and existing tests should complete without errors. 24 | 25 | - [ ] This change adds unit test coverage 26 | - [ ] This change has been tested on the latest version of the platform/language or why not 27 | 28 | ### Checklist 29 | 30 | - [ ] I have read the [Auth0 general contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md) 31 | - [ ] All existing and new tests complete without errors 32 | - [ ] All active GitHub checks have passed 33 | -------------------------------------------------------------------------------- /.github/actions/get-release-notes/action.yml: -------------------------------------------------------------------------------- 1 | name: Return the release notes extracted from the body of the PR associated with the release. 2 | 3 | # 4 | # Returns the release notes from the content of a pull request linked to a release branch. It expects the branch name to be in the format release/vX.Y.Z, release/X.Y.Z, release/vX.Y.Z-beta.N. etc. 5 | # 6 | # TODO: Remove once the common repo is public. 7 | # 8 | inputs: 9 | version: 10 | required: true 11 | repo_name: 12 | required: false 13 | repo_owner: 14 | required: true 15 | token: 16 | required: true 17 | 18 | outputs: 19 | release-notes: 20 | value: ${{ steps.get_release_notes.outputs.RELEASE_NOTES }} 21 | 22 | runs: 23 | using: composite 24 | 25 | steps: 26 | - uses: actions/github-script@v7 27 | id: get_release_notes 28 | with: 29 | result-encoding: string 30 | script: | 31 | const { data: pulls } = await github.rest.pulls.list({ 32 | owner: process.env.REPO_OWNER, 33 | repo: process.env.REPO_NAME, 34 | state: 'all', 35 | head: `${process.env.REPO_OWNER}:release/${process.env.VERSION}`, 36 | }); 37 | core.setOutput('RELEASE_NOTES', pulls[0].body); 38 | env: 39 | GITHUB_TOKEN: ${{ inputs.token }} 40 | REPO_OWNER: ${{ inputs.repo_owner }} 41 | REPO_NAME: ${{ inputs.repo_name }} 42 | VERSION: ${{ inputs.version }} 43 | -------------------------------------------------------------------------------- /src/factory/__tests__/Auth0ClientFactory.spec.ts: -------------------------------------------------------------------------------- 1 | import { Auth0ClientFactory } from '../Auth0ClientFactory'; 2 | import { validateAuth0Options } from '../../core/utils'; 3 | import { NativeAuth0Client } from '../../platforms'; 4 | 5 | // Now we only need to mock the dependencies of the NATIVE factory 6 | jest.mock('../../core/utils/validation'); 7 | jest.mock('../../platforms/native/adapters/NativeAuth0Client'); 8 | 9 | const mockValidateAuth0Options = validateAuth0Options as jest.Mock; 10 | const MockNativeAuth0Client = NativeAuth0Client as jest.MockedClass< 11 | typeof NativeAuth0Client 12 | >; 13 | 14 | describe('Auth0ClientFactory (Native)', () => { 15 | const options = { 16 | domain: 'my-tenant.auth0.com', 17 | clientId: 'MyClientId123', 18 | }; 19 | 20 | beforeEach(() => { 21 | jest.clearAllMocks(); 22 | }); 23 | 24 | it('should call validateAuth0Options with the provided options', () => { 25 | Auth0ClientFactory.createClient(options); 26 | expect(mockValidateAuth0Options).toHaveBeenCalledWith(options); 27 | }); 28 | 29 | // This test is now specific to the native factory 30 | it('should always create a NativeAuth0Client', () => { 31 | const client = Auth0ClientFactory.createClient(options); 32 | expect(MockNativeAuth0Client).toHaveBeenCalledTimes(1); 33 | expect(MockNativeAuth0Client).toHaveBeenCalledWith(options); 34 | expect(client).toBeInstanceOf(MockNativeAuth0Client); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /android/src/main/java/com/auth0/react/A0Auth0Package.kt: -------------------------------------------------------------------------------- 1 | package com.auth0.react 2 | 3 | import com.facebook.react.TurboReactPackage 4 | import com.facebook.react.bridge.ReactApplicationContext 5 | import com.facebook.react.bridge.NativeModule 6 | import com.facebook.react.module.model.ReactModuleInfoProvider 7 | import com.facebook.react.module.model.ReactModuleInfo 8 | 9 | class A0Auth0Package : TurboReactPackage() { 10 | 11 | override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? { 12 | return if (name == A0Auth0Module.NAME) { 13 | A0Auth0Module(reactContext) 14 | } else { 15 | null 16 | } 17 | } 18 | 19 | override fun getReactModuleInfoProvider(): ReactModuleInfoProvider { 20 | return ReactModuleInfoProvider { 21 | val moduleInfos = mutableMapOf() 22 | val isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED 23 | 24 | moduleInfos[A0Auth0Module.NAME] = ReactModuleInfo( 25 | A0Auth0Module.NAME, 26 | A0Auth0Module.NAME, 27 | false, // canOverrideExistingModule 28 | false, // needsEagerInit 29 | true, // hasConstants 30 | false, // isCxxModule 31 | isTurboModule // isTurboModule 32 | ) 33 | 34 | moduleInfos 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /example/src/components/Button.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | TouchableOpacity, 4 | Text, 5 | StyleSheet, 6 | ViewStyle, 7 | TextStyle, 8 | ActivityIndicator, 9 | } from 'react-native'; 10 | 11 | type Props = { 12 | onPress: () => void; 13 | title: string; 14 | disabled?: boolean; 15 | loading?: boolean; 16 | style?: ViewStyle; 17 | textStyle?: TextStyle; 18 | }; 19 | 20 | const Button = ({ 21 | onPress, 22 | title, 23 | disabled, 24 | loading, 25 | style, 26 | textStyle, 27 | }: Props) => { 28 | return ( 29 | 34 | {loading ? ( 35 | 36 | ) : ( 37 | {title} 38 | )} 39 | 40 | ); 41 | }; 42 | 43 | const styles = StyleSheet.create({ 44 | button: { 45 | backgroundColor: '#E53935', // A distinct color for Auth0 46 | paddingVertical: 12, 47 | paddingHorizontal: 24, 48 | borderRadius: 8, 49 | alignItems: 'center', 50 | justifyContent: 'center', 51 | minWidth: 200, 52 | }, 53 | text: { 54 | color: '#FFFFFF', 55 | fontSize: 16, 56 | fontWeight: 'bold', 57 | }, 58 | disabled: { 59 | backgroundColor: '#BDBDBD', 60 | }, 61 | }); 62 | 63 | export default Button; 64 | -------------------------------------------------------------------------------- /example/ios/Auth0Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import React 3 | import React_RCTAppDelegate 4 | import ReactAppDependencyProvider 5 | 6 | @main 7 | class AppDelegate: UIResponder, UIApplicationDelegate { 8 | var window: UIWindow? 9 | 10 | var reactNativeDelegate: ReactNativeDelegate? 11 | var reactNativeFactory: RCTReactNativeFactory? 12 | 13 | func application( 14 | _ application: UIApplication, 15 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil 16 | ) -> Bool { 17 | let delegate = ReactNativeDelegate() 18 | let factory = RCTReactNativeFactory(delegate: delegate) 19 | delegate.dependencyProvider = RCTAppDependencyProvider() 20 | 21 | reactNativeDelegate = delegate 22 | reactNativeFactory = factory 23 | 24 | window = UIWindow(frame: UIScreen.main.bounds) 25 | 26 | factory.startReactNative( 27 | withModuleName: "Auth0Example", 28 | in: window, 29 | launchOptions: launchOptions 30 | ) 31 | 32 | return true 33 | } 34 | } 35 | 36 | class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate { 37 | override func sourceURL(for bridge: RCTBridge) -> URL? { 38 | self.bundleURL() 39 | } 40 | 41 | override func bundleURL() -> URL? { 42 | #if DEBUG 43 | RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") 44 | #else 45 | Bundle.main.url(forResource: "main", withExtension: "jsbundle") 46 | #endif 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /example/src/navigation/MainTabNavigator.tsx: -------------------------------------------------------------------------------- 1 | // example/src/navigation/MainTabNavigator.tsx 2 | 3 | import React from 'react'; 4 | import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; 5 | import ProfileScreen from '../screens/hooks/Profile'; 6 | import ApiScreen from '../screens/hooks/Api'; 7 | import MoreScreen from '../screens/hooks/More'; 8 | import CredentialsScreen from '../screens/hooks/CredentialsScreen'; 9 | 10 | export type MainTabParamList = { 11 | Profile: undefined; 12 | Api: undefined; 13 | More: undefined; 14 | Credentials: undefined; 15 | }; 16 | 17 | const Tab = createBottomTabNavigator(); 18 | 19 | /** 20 | * Navigator for the authenticated part of the Hooks-based demo. 21 | * It provides tab-based navigation to the Profile, API, and other screens. 22 | */ 23 | const MainTabNavigator = () => { 24 | return ( 25 | 31 | 36 | 37 | 38 | 39 | 40 | ); 41 | }; 42 | 43 | export default MainTabNavigator; 44 | -------------------------------------------------------------------------------- /src/plugin/__tests__/fixtures/swiftappdelegate-withlinking.ts: -------------------------------------------------------------------------------- 1 | export default ` 2 | // Swift AppDelegate template for Expo 53+ 3 | 4 | import UIKit 5 | import React 6 | import React_RCTAppDelegate 7 | import ReactAppDependencyProvider 8 | import React_RCTLinking 9 | 10 | @main 11 | class AppDelegate: RCTAppDelegate { 12 | override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { 13 | self.moduleName = "Auth0Example" 14 | self.dependencyProvider = RCTAppDependencyProvider() 15 | 16 | // You can add your custom initial props in the dictionary below. 17 | // They will be passed down to the ViewController used by React Native. 18 | self.initialProps = [:] 19 | 20 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 21 | } 22 | 23 | override func sourceURL(for bridge: RCTBridge) -> URL? { 24 | self.bundleURL() 25 | } 26 | 27 | override func bundleURL() -> URL? { 28 | #if DEBUG 29 | RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") 30 | #else 31 | Bundle.main.url(forResource: "main", withExtension: "jsbundle") 32 | #endif 33 | } 34 | 35 | // Handle URL schemes for Auth0 authentication 36 | override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { 37 | return RCTLinkingManager.application(app, open: url, options: options) 38 | } 39 | } 40 | `; 41 | -------------------------------------------------------------------------------- /src/hooks/reducer.ts: -------------------------------------------------------------------------------- 1 | import type { User } from '../types'; 2 | import type { AuthError } from '../core/models'; 3 | import { deepEqual } from '../core/utils/deepEqual'; 4 | 5 | /** 6 | * The shape of the authentication state managed by the Auth0Provider. 7 | */ 8 | export interface AuthState { 9 | user: User | null; 10 | error: AuthError | null; 11 | isLoading: boolean; 12 | } 13 | 14 | /** 15 | * The possible actions that can be dispatched to update the authentication state. 16 | * @internal 17 | */ 18 | export type AuthAction = 19 | | { type: 'LOGIN_COMPLETE'; user: User } 20 | | { type: 'LOGOUT_COMPLETE' } 21 | | { type: 'ERROR'; error: AuthError } 22 | | { type: 'INITIALIZED'; user: User | null } 23 | | { type: 'SET_USER'; user: User | null }; 24 | 25 | /** 26 | * A pure function that calculates the new state based on the previous state and a dispatched action. 27 | * @internal 28 | */ 29 | export const reducer = (state: AuthState, action: AuthAction): AuthState => { 30 | switch (action.type) { 31 | case 'LOGIN_COMPLETE': 32 | return { ...state, error: null, isLoading: false, user: action.user }; 33 | case 'LOGOUT_COMPLETE': 34 | return { ...state, error: null, user: null }; 35 | case 'ERROR': 36 | return { ...state, isLoading: false, error: action.error }; 37 | case 'INITIALIZED': 38 | return { ...state, isLoading: false, user: action.user }; 39 | case 'SET_USER': 40 | if (deepEqual(state.user, action.user)) { 41 | return state; 42 | } 43 | return { ...state, user: action.user }; 44 | } 45 | }; 46 | -------------------------------------------------------------------------------- /example/src/navigation/RootNavigator.tsx: -------------------------------------------------------------------------------- 1 | // example/src/navigation/RootNavigator.tsx 2 | 3 | import React from 'react'; 4 | import { createStackNavigator } from '@react-navigation/stack'; 5 | import SelectionScreen from '../screens/SelectionScreen'; 6 | import HooksDemoNavigator from './HooksDemoNavigator'; 7 | import ClassDemoNavigator from './ClassDemoNavigator'; 8 | 9 | // Define the parameter list for type safety 10 | export type RootStackParamList = { 11 | Selection: undefined; 12 | HooksDemo: undefined; 13 | ClassDemo: undefined; 14 | }; 15 | 16 | const Stack = createStackNavigator(); 17 | 18 | /** 19 | * The top-level navigator that allows the user to select which 20 | * demo they want to see: the recommended Hooks-based approach or 21 | * the class-based approach. 22 | */ 23 | const RootNavigator = () => { 24 | return ( 25 | 29 | 34 | 39 | 44 | 45 | ); 46 | }; 47 | 48 | export default RootNavigator; 49 | -------------------------------------------------------------------------------- /example/src/components/Result.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View, Text, StyleSheet, Platform } from 'react-native'; 3 | 4 | type Props = { 5 | title: string; 6 | result: object | null | void; 7 | error: Error | null; 8 | }; 9 | 10 | const Result = ({ title, result, error }: Props) => { 11 | if (!result && !error) { 12 | return null; 13 | } 14 | 15 | const isError = !!error; 16 | const content = error ? error.message : JSON.stringify(result, null, 2); 17 | 18 | return ( 19 | 20 | {title} 21 | 22 | 23 | {content} 24 | 25 | 26 | 27 | ); 28 | }; 29 | 30 | const styles = StyleSheet.create({ 31 | container: { 32 | marginVertical: 10, 33 | width: '100%', 34 | }, 35 | title: { 36 | fontSize: 16, 37 | fontWeight: 'bold', 38 | marginBottom: 8, 39 | }, 40 | resultBox: { 41 | backgroundColor: '#E8F5E9', 42 | borderColor: '#A5D6A7', 43 | borderWidth: 1, 44 | borderRadius: 8, 45 | padding: 12, 46 | }, 47 | errorBox: { 48 | backgroundColor: '#FFEBEE', 49 | borderColor: '#EF9A9A', 50 | }, 51 | resultText: { 52 | fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace', 53 | color: '#1B5E20', 54 | }, 55 | errorText: { 56 | fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace', 57 | color: '#B71C1C', 58 | }, 59 | }); 60 | 61 | export default Result; 62 | -------------------------------------------------------------------------------- /example/ios/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import React 3 | import React_RCTAppDelegate 4 | import ReactAppDependencyProvider 5 | 6 | @main 7 | class AppDelegate: UIResponder, UIApplicationDelegate { 8 | var window: UIWindow? 9 | 10 | var reactNativeDelegate: ReactNativeDelegate? 11 | var reactNativeFactory: RCTReactNativeFactory? 12 | 13 | func application( 14 | _ application: UIApplication, 15 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil 16 | ) -> Bool { 17 | let delegate = ReactNativeDelegate() 18 | let factory = RCTReactNativeFactory(delegate: delegate) 19 | delegate.dependencyProvider = RCTAppDependencyProvider() 20 | 21 | reactNativeDelegate = delegate 22 | reactNativeFactory = factory 23 | 24 | window = UIWindow(frame: UIScreen.main.bounds) 25 | 26 | factory.startReactNative( 27 | withModuleName: "Auth0Example", 28 | in: window, 29 | launchOptions: launchOptions 30 | ) 31 | 32 | return true 33 | } 34 | 35 | func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { 36 | return RCTLinkingManager.application(app, open: url, options: options) 37 | } 38 | } 39 | 40 | class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate { 41 | override func sourceURL(for bridge: RCTBridge) -> URL? { 42 | self.bundleURL() 43 | } 44 | 45 | override func bundleURL() -> URL? { 46 | #if DEBUG 47 | RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") 48 | #else 49 | Bundle.main.url(forResource: "main", withExtension: "jsbundle") 50 | #endif 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /example/src/navigation/HooksDemoNavigator.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Auth0Provider, useAuth0 } from 'react-native-auth0'; 3 | import AuthStackNavigator from './AuthStackNavigator'; 4 | import MainTabNavigator from './MainTabNavigator'; 5 | import { ActivityIndicator, View, StyleSheet } from 'react-native'; 6 | import config from '../auth0-configuration'; 7 | 8 | const AUTH0_DOMAIN = config.domain; 9 | const AUTH0_CLIENT_ID = config.clientId; 10 | 11 | /** 12 | * A helper component that contains the logic to switch between the 13 | * authentication stack and the main application stack based on user state. 14 | * It's rendered inside the Auth0Provider so it can use the useAuth0 hook. 15 | */ 16 | const AppContent = () => { 17 | const { user, isLoading } = useAuth0(); 18 | 19 | if (isLoading) { 20 | return ( 21 | 22 | 23 | 24 | ); 25 | } 26 | 27 | // If user is authenticated, show the main app, otherwise show the login screen. 28 | return user ? : ; 29 | }; 30 | 31 | /** 32 | * This component wraps the entire Hooks-based demo flow with the Auth0Provider, 33 | * making the authentication context available to all its child screens. 34 | */ 35 | const HooksDemoNavigator = () => { 36 | return ( 37 | 38 | 39 | 40 | ); 41 | }; 42 | 43 | const styles = StyleSheet.create({ 44 | container: { 45 | flex: 1, 46 | justifyContent: 'center', 47 | alignItems: 'center', 48 | }, 49 | }); 50 | 51 | export default HooksDemoNavigator; 52 | -------------------------------------------------------------------------------- /example/src/components/UserInfo.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View, Text, StyleSheet, Image } from 'react-native'; 3 | import type { User } from 'react-native-auth0'; 4 | 5 | type Props = { 6 | user: User | null; 7 | }; 8 | 9 | const UserInfo = ({ user }: Props) => { 10 | if (!user) { 11 | return null; 12 | } 13 | 14 | return ( 15 | 16 | {user.picture && ( 17 | 18 | )} 19 | {user.name} 20 | {Object.entries(user).map(([key, value]) => ( 21 | 22 | {key} 23 | {String(value)} 24 | 25 | ))} 26 | 27 | ); 28 | }; 29 | 30 | const styles = StyleSheet.create({ 31 | container: { 32 | width: '100%', 33 | padding: 16, 34 | }, 35 | avatar: { 36 | width: 100, 37 | height: 100, 38 | borderRadius: 50, 39 | alignSelf: 'center', 40 | marginBottom: 16, 41 | }, 42 | title: { 43 | fontSize: 24, 44 | fontWeight: 'bold', 45 | textAlign: 'center', 46 | marginBottom: 20, 47 | color: '#212121', 48 | }, 49 | row: { 50 | flexDirection: 'row', 51 | justifyContent: 'space-between', 52 | paddingVertical: 8, 53 | borderBottomWidth: 1, 54 | borderBottomColor: '#EEEEEE', 55 | }, 56 | label: { 57 | fontSize: 16, 58 | color: '#757575', 59 | flex: 1, 60 | }, 61 | value: { 62 | fontSize: 16, 63 | color: '#212121', 64 | fontWeight: '500', 65 | flex: 2, 66 | textAlign: 'right', 67 | }, 68 | }); 69 | 70 | export default UserInfo; 71 | -------------------------------------------------------------------------------- /example/src/navigation/ClassDemoNavigator.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createStackNavigator } from '@react-navigation/stack'; 3 | import ClassLoginScreen from '../screens/class-based/ClassLogin'; 4 | import ClassProfileScreen from '../screens/class-based/ClassProfile'; 5 | import ClassApiTestsScreen from '../screens/class-based/ClassApiTests'; 6 | import type { Credentials } from 'react-native-auth0'; 7 | 8 | /** 9 | * Defines the screens and their parameters for the class-based navigation stack. 10 | * This provides type safety for navigation calls and route props. 11 | */ 12 | export type ClassDemoStackParamList = { 13 | ClassLogin: undefined; 14 | ClassProfile: { credentials: Credentials }; // Expects credentials to be passed after login 15 | ClassApiTests: { accessToken: string }; // Expects an access token for API calls 16 | }; 17 | 18 | const Stack = createStackNavigator(); 19 | 20 | /** 21 | * The navigator for the entire Class-based demo flow. 22 | * 23 | * It does NOT use an Auth0Provider, demonstrating how to use the SDK 24 | * by importing and calling the Auth0 class instance directly. 25 | */ 26 | const ClassDemoNavigator = () => { 27 | return ( 28 | 29 | 34 | 39 | 44 | 45 | ); 46 | }; 47 | 48 | export default ClassDemoNavigator; 49 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Auth0Example", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "android": "react-native run-android", 7 | "ios": "react-native run-ios", 8 | "web": "webpack serve --mode development", 9 | "web:build": "webpack --mode production", 10 | "start": "react-native start", 11 | "build:android": "react-native build-android --extra-params \"--no-daemon --console=plain -PreactNativeArchitectures=arm64-v8a\"", 12 | "build:ios": "react-native build-ios --mode Debug" 13 | }, 14 | "dependencies": { 15 | "@react-navigation/bottom-tabs": "^7.8.4", 16 | "@react-navigation/native": "^7.1.20", 17 | "@react-navigation/stack": "^7.6.4", 18 | "react": "19.1.1", 19 | "react-native": "0.82.1", 20 | "react-native-gesture-handler": "^2.29.1", 21 | "react-native-safe-area-context": "^5.6.2", 22 | "react-native-screens": "^4.18.0", 23 | "react-native-web": "^0.21.2" 24 | }, 25 | "devDependencies": { 26 | "@babel/core": "^7.25.2", 27 | "@babel/preset-env": "^7.25.3", 28 | "@babel/runtime": "^7.25.0", 29 | "@react-native-community/cli": "20.0.2", 30 | "@react-native-community/cli-platform-android": "20.0.2", 31 | "@react-native-community/cli-platform-ios": "20.0.2", 32 | "@react-native/babel-preset": "0.82.1", 33 | "@react-native/metro-config": "0.82.1", 34 | "@react-native/typescript-config": "0.82.1", 35 | "@types/react": "^19.2.2", 36 | "babel-loader": "^10.0.0", 37 | "babel-plugin-react-native-web": "^0.21.2", 38 | "html-webpack-plugin": "^5.6.5", 39 | "react-native-builder-bob": "^0.40.15", 40 | "react-native-monorepo-config": "^0.1.9", 41 | "url-loader": "^4.1.1", 42 | "webpack": "^5.99.9", 43 | "webpack-cli": "^6.0.1", 44 | "webpack-dev-server": "^5.2.2" 45 | }, 46 | "engines": { 47 | "node": ">=20" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /android/src/main/java/com/auth0/react/CredentialsParser.kt: -------------------------------------------------------------------------------- 1 | package com.auth0.react 2 | 3 | import com.auth0.android.result.Credentials 4 | import com.facebook.react.bridge.ReadableMap 5 | import com.facebook.react.bridge.WritableNativeMap 6 | import java.util.Date 7 | 8 | object CredentialsParser { 9 | 10 | private const val ACCESS_TOKEN_KEY = "accessToken" 11 | private const val ID_TOKEN_KEY = "idToken" 12 | private const val EXPIRES_AT_KEY = "expiresAt" 13 | private const val SCOPE = "scope" 14 | private const val REFRESH_TOKEN_KEY = "refreshToken" 15 | private const val TOKEN_TYPE_KEY = "tokenType" 16 | private const val DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" 17 | 18 | fun toMap(credentials: Credentials): ReadableMap { 19 | val map = WritableNativeMap() 20 | map.putString(ACCESS_TOKEN_KEY, credentials.accessToken) 21 | map.putDouble(EXPIRES_AT_KEY, credentials.expiresAt.time / 1000.0) 22 | map.putString(ID_TOKEN_KEY, credentials.idToken) 23 | map.putString(SCOPE, credentials.scope) 24 | map.putString(REFRESH_TOKEN_KEY, credentials.refreshToken) 25 | map.putString(TOKEN_TYPE_KEY, credentials.type) 26 | return map 27 | } 28 | 29 | fun fromMap(map: ReadableMap): Credentials { 30 | val idToken = map.getString(ID_TOKEN_KEY) ?: "" 31 | val accessToken = map.getString(ACCESS_TOKEN_KEY) ?: "" 32 | val tokenType = map.getString(TOKEN_TYPE_KEY) ?: "" 33 | val refreshToken = map.getString(REFRESH_TOKEN_KEY) 34 | val scope = map.getString(SCOPE) 35 | val expiresAtUnix = map.getDouble(EXPIRES_AT_KEY) 36 | val expiresAt = Date((expiresAtUnix * 1000).toLong()) 37 | 38 | return Credentials( 39 | idToken, 40 | accessToken, 41 | tokenType, 42 | refreshToken, 43 | expiresAt, 44 | scope 45 | ) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | def find_and_replace_boost_url 2 | pod_spec = "../node_modules/react-native/third-party-podspecs/boost.podspec" 3 | puts "Debug: Starting boost URL replacement" 4 | if File.exist?(pod_spec) 5 | puts "Debug: Found boost.podspec" 6 | spec_content = File.read(pod_spec) 7 | spec_content.gsub!( 8 | 'https://boostorg.jfrog.io/artifactory/main/release/1.76.0/source/boost_1_76_0.tar.bz2', 9 | 'https://archives.boost.io/release/1.76.0/source/boost_1_76_0.tar.bz2' 10 | ) 11 | File.write(pod_spec, spec_content) 12 | puts "Debug: Updated boost.podspec" 13 | end 14 | end 15 | 16 | ENV['RCT_NEW_ARCH_ENABLED'] = '1' 17 | 18 | 19 | # Let the magic happen! 20 | find_and_replace_boost_url 21 | 22 | # Resolve react_native_pods.rb with node to allow for hoisting 23 | require Pod::Executable.execute_command('node', ['-p', 24 | 'require.resolve( 25 | "react-native/scripts/react_native_pods.rb", 26 | {paths: [process.argv[1]]}, 27 | )', __dir__]).strip 28 | 29 | platform :ios, min_ios_version_supported 30 | prepare_react_native_project! 31 | 32 | linkage = ENV['USE_FRAMEWORKS'] 33 | if linkage != nil 34 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green 35 | use_frameworks! :linkage => linkage.to_sym 36 | end 37 | 38 | target 'Auth0Example' do 39 | config = use_native_modules! 40 | 41 | use_react_native!( 42 | :path => config[:reactNativePath], 43 | # An absolute path to your application root. 44 | :app_path => "#{Pod::Config.instance.installation_root}/.." 45 | ) 46 | 47 | post_install do |installer| 48 | react_native_post_install( 49 | installer, 50 | ) 51 | 52 | installer.pods_project.targets.each do |target| 53 | target.build_configurations.each do |config| 54 | config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', '_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION'] 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { fixupConfigRules } from '@eslint/compat'; 2 | import { FlatCompat } from '@eslint/eslintrc'; 3 | import js from '@eslint/js'; 4 | import prettier from 'eslint-plugin-prettier'; 5 | import tseslint from 'typescript-eslint'; 6 | import { defineConfig } from 'eslint/config'; 7 | import path from 'node:path'; 8 | import { fileURLToPath } from 'node:url'; 9 | 10 | const __filename = fileURLToPath(import.meta.url); 11 | const __dirname = path.dirname(__filename); 12 | const compat = new FlatCompat({ 13 | baseDirectory: __dirname, 14 | recommendedConfig: js.configs.recommended, 15 | allConfig: js.configs.all, 16 | }); 17 | 18 | export default defineConfig([ 19 | { 20 | extends: fixupConfigRules(compat.extends('@react-native', 'prettier')), 21 | plugins: { prettier }, 22 | rules: { 23 | 'react/react-in-jsx-scope': 'off', 24 | 'prettier/prettier': [ 25 | 'error', 26 | { 27 | quoteProps: 'consistent', 28 | singleQuote: true, 29 | tabWidth: 2, 30 | trailingComma: 'es5', 31 | useTabs: false, 32 | }, 33 | ], 34 | }, 35 | }, 36 | // TypeScript-specific configuration for type-checked rules 37 | { 38 | files: ['**/*.ts', '**/*.tsx'], 39 | ignores: [ 40 | '**/__tests__/**', 41 | '**/__mocks__/**', 42 | '**/*.spec.ts', 43 | '**/*.spec.tsx', 44 | '**/*.test.ts', 45 | '**/*.test.tsx', 46 | ], 47 | languageOptions: { 48 | parserOptions: { 49 | project: true, 50 | tsconfigRootDir: import.meta.dirname, 51 | }, 52 | }, 53 | rules: { 54 | // Enable unbound-method rule to catch interface typing issues 55 | // This helps ensure methods in interfaces use arrow function syntax 56 | // instead of method syntax when they don't use 'this' 57 | '@typescript-eslint/unbound-method': 'error', 58 | }, 59 | }, 60 | { 61 | ignores: ['node_modules/', 'lib/', 'docs'], 62 | }, 63 | ]); 64 | -------------------------------------------------------------------------------- /__mocks__/react-native.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file provides a project-wide, self-contained mock for the 'react-native' package. 3 | */ 4 | 5 | const React = require('react'); 6 | 7 | // A fake native module to be returned by TurboModuleRegistry.getEnforcing. 8 | const mockNativeModule = { 9 | // ... (all the mock native methods from before) 10 | initializeAuth0WithConfiguration: jest.fn(() => Promise.resolve()), 11 | webAuth: jest.fn(() => Promise.resolve({})), 12 | webAuthLogout: jest.fn(() => Promise.resolve()), 13 | cancelWebAuth: jest.fn(() => Promise.resolve()), 14 | getBundleIdentifier: jest.fn(() => Promise.resolve('com.my.app')), 15 | resumeWebAuth: jest.fn(() => Promise.resolve(true)), 16 | saveCredentials: jest.fn(() => Promise.resolve()), 17 | getCredentials: jest.fn(() => Promise.resolve({})), 18 | hasValidCredentials: jest.fn(() => Promise.resolve(true)), 19 | clearCredentials: jest.fn(() => Promise.resolve()), 20 | hasValidInstance: jest.fn(() => Promise.resolve(true)), 21 | }; 22 | 23 | // Export the mocked module. 24 | module.exports = { 25 | // Mock the Platform module 26 | Platform: { 27 | OS: 'ios', 28 | }, 29 | // Mock the TurboModuleRegistry 30 | TurboModuleRegistry: { 31 | getEnforcing: () => mockNativeModule, 32 | }, 33 | // Mock the Linking module 34 | Linking: { 35 | addEventListener: jest.fn(() => ({ remove: jest.fn() })), 36 | removeEventListener: jest.fn(), 37 | getInitialURL: jest.fn(() => Promise.resolve(null)), 38 | }, 39 | 40 | // FIX: Add mocks for core UI components used in tests. 41 | // We can use a simple functional component that just renders its children 42 | // and passes along any props. 43 | View: (props) => React.createElement('View', props, props.children), 44 | Text: (props) => React.createElement('Text', props, props.children), 45 | Button: (props) => React.createElement('Button', props, props.children), 46 | // Add any other components your tests might use, e.g., StyleSheet 47 | StyleSheet: { 48 | create: (styles) => styles, 49 | }, 50 | }; 51 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/rn_edit_text_material.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 22 | 23 | 24 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/src/screens/hooks/Api.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { SafeAreaView, ScrollView, StyleSheet } from 'react-native'; 3 | import Auth0, { useAuth0, User } from 'react-native-auth0'; 4 | import Button from '../../components/Button'; 5 | import Header from '../../components/Header'; 6 | import Result from '../../components/Result'; 7 | import config from '../../auth0-configuration'; 8 | 9 | const AUTH0_DOMAIN = config.domain; 10 | const AUTH0_CLIENT_ID = config.clientId; 11 | 12 | const auth0 = new Auth0({ domain: AUTH0_DOMAIN, clientId: AUTH0_CLIENT_ID }); 13 | 14 | const ApiScreen = () => { 15 | const { user, getCredentials } = useAuth0(); 16 | const [apiResult, setApiResult] = useState(null); 17 | const [apiError, setApiError] = useState(null); 18 | 19 | const onCallApi = async () => { 20 | try { 21 | const credentials = await getCredentials( 22 | 'openid profile email read:current_user' 23 | ); 24 | if (!credentials || !user?.sub) { 25 | throw new Error('Could not get credentials or user ID.'); 26 | } 27 | 28 | const managementClient = auth0.users(credentials.accessToken); 29 | const fullProfile = await managementClient.getUser({ id: user.sub }); 30 | 31 | setApiResult(fullProfile); 32 | setApiError(null); 33 | } catch (e) { 34 | setApiError(e as Error); 35 | } 36 | }; 37 | 38 | return ( 39 | 40 |
41 | 42 | 47 |