Skip to main content

Documentation Index

Fetch the complete documentation index at: https://auth0-feat-ionic-capacitor-quickstart-modernization.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Get Started

This quickstart demonstrates how to add Auth0 authentication to an Ionic React application with Capacitor. You’ll build a mobile-ready app with login, logout, and user profile features using the Auth0 React SDK and Capacitor’s native browser integration.
1

Create a new project

Create a new Ionic React app with Capacitor
npx ionic start auth0-ionic-react tabs --type=react --capacitor
Open the project
cd auth0-ionic-react
If you already have an Ionic React app, make sure Capacitor is enabled. You can add it with ionic integrations enable capacitor and then npx cap init.
2

Install the Auth0 React SDK and Capacitor plugins

npm install @auth0/auth0-react @capacitor/browser @capacitor/app && npx cap sync
Capacitor’s Browser plugin on iOS uses SFSafariViewController, which on iOS 11+ does not share cookies with Safari on the device. This means SSO will not work on those devices. If you need SSO, use a compatible plugin that uses ASWebAuthenticationSession.
3

Setup your Auth0 App

Next up, you need to create a new app on your Auth0 tenant and add the environment variables to your project.You have three options to set up your Auth0 app: use the Quick Setup tool (recommended), run a CLI command, or configure manually via the Dashboard.
Throughout this quickstart, YOUR_PACKAGE_ID is your application’s package ID. This is the appId field in your capacitor.config.ts file (e.g. io.ionic.starter). See Capacitor’s Config schema for more info.
Verify your .env file exists: cat .env (Mac/Linux) or type .env (Windows)
4

Configure the Auth0Provider

Open src/main.tsx and wrap the App component with the Auth0Provider. The mobile-specific settings useRefreshTokens and useRefreshTokensFallback are required for Ionic apps on iOS and Android.
src/main.tsx
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
import { Auth0Provider } from '@auth0/auth0-react';

// Must match the appId in your capacitor.config.ts
const appId = 'io.ionic.starter';

createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <Auth0Provider
      domain={import.meta.env.VITE_AUTH0_DOMAIN}
      clientId={import.meta.env.VITE_AUTH0_CLIENT_ID}
      useRefreshTokens={true}
      useRefreshTokensFallback={false}
      authorizationParams={{
        redirect_uri: `${appId}://${import.meta.env.VITE_AUTH0_DOMAIN}/capacitor/${appId}/callback`
      }}
    >
      <App />
    </Auth0Provider>
  </React.StrictMode>
);
  • useRefreshTokens: Required for Ionic on Android and iOS. Mobile browsers block third-party cookies, so the SDK uses refresh tokens instead of iframe-based silent authentication.
  • useRefreshTokensFallback: Must be false to prevent the SDK from attempting iframe-based silent auth, which is not available on mobile.
  • authorizationParams.redirect_uri: Uses your package ID as a custom URL scheme so the OS can route the Auth0 callback back to your app.
To persist authentication after closing and reopening the application, you may want to set cacheLocation to localstorage, but be aware of the risks of storing tokens in localstorage. On Capacitor, localstorage should be treated as transient — the OS may clear it unexpectedly. See Capacitor’s guidance on storage.We recommend against using Capacitor’s Preferences plugin to store tokens, as it is backed by UserDefaults (iOS) and SharedPreferences (Android), which are not encrypted and could be synced to the cloud. The SDK supports custom cache implementations if you need a more secure and persistent storage mechanism.
5

Create Login, Logout, Profile, and Callback Handler

Create files
touch src/LoginButton.tsx && touch src/LogoutButton.tsx && touch src/Profile.tsx
And add the following code snippetsThe openUrl callback in LoginButton and LogoutButton uses Capacitor’s Browser plugin to open the Auth0 login and logout pages in the device’s system browser component, rather than navigating away from the app entirely.The App component listens for the appUrlOpen event, which fires when Auth0 redirects back to your app using the custom URL scheme. It calls handleRedirectCallback to exchange the authorization code for tokens, then closes the browser.
By default, the SDK’s loginWithRedirect method uses window.location.href to navigate to the login page, which opens the device’s default browser application. Setting openUrl to use Browser.open keeps the authentication flow within your app’s context for a better user experience.
6

Run your app

Test in the browser first
ionic serve
When running in the browser with ionic serve, the custom URL scheme redirect won’t work. For browser testing, you can temporarily set redirect_uri to http://localhost:8100 and add it to your Auth0 app’s Allowed Callback URLs and Allowed Logout URLs. For full native testing, use npx cap run ios or npx cap run android.
To run on a device or simulator
npx cap run ios
CheckpointYou should now have a fully functional Auth0 login running in your Ionic app. When running on a device, tapping “Log in” opens the Auth0 Universal Login Page in the system browser, and after authenticating, you’re redirected back to your app with the user’s profile displayed.

Advanced Usage

For Auth0 callbacks to work on devices, register your package ID as a custom URL scheme on each platform.iOS — add to ios/App/App/Info.plist:
ios/App/App/Info.plist
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>io.ionic.starter</string>
    </array>
  </dict>
</array>
Android — add an intent filter inside the main <activity> in android/app/src/main/AndroidManifest.xml:
android/app/src/main/AndroidManifest.xml
<intent-filter>
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data android:scheme="io.ionic.starter" />
</intent-filter>
Replace io.ionic.starter with your actual appId from capacitor.config.ts.
To learn more, read Defining a Custom URL Scheme for iOS, or Create Deep Links to App Content for Android.
Use Auth0’s authentication state to protect specific routes in your Ionic application:
src/App.tsx
import { useAuth0, withAuthenticationRequired } from '@auth0/auth0-react';
import { IonReactRouter } from '@ionic/react-router';
import { IonRouterOutlet } from '@ionic/react';
import { Route, Redirect } from 'react-router-dom';
import HomePage from './pages/Home';
import DashboardPage from './pages/Dashboard';

const ProtectedRoute = ({ component, ...args }: any) => {
  const Component = withAuthenticationRequired(component, {
    onRedirecting: () => <div className="ion-padding">Loading...</div>,
  });
  return <Route {...args} render={() => <Component />} />;
};

const App: React.FC = () => {
  // ... callback handler from previous step

  return (
    <IonApp>
      <IonReactRouter>
        <IonRouterOutlet>
          <Route exact path="/home" component={HomePage} />
          <ProtectedRoute exact path="/dashboard" component={DashboardPage} />
          <Route exact path="/">
            <Redirect to="/home" />
          </Route>
        </IonRouterOutlet>
      </IonReactRouter>
    </IonApp>
  );
};
The withAuthenticationRequired HOC automatically redirects unauthenticated users to the Auth0 login page when they try to access a protected route.
Configure your Auth0Provider to include an API audience and use the getAccessTokenSilently method to get access tokens for your backend:
src/main.tsx
<Auth0Provider
  domain={import.meta.env.VITE_AUTH0_DOMAIN}
  clientId={import.meta.env.VITE_AUTH0_CLIENT_ID}
  useRefreshTokens={true}
  useRefreshTokensFallback={false}
  authorizationParams={{
    redirect_uri: `${appId}://${import.meta.env.VITE_AUTH0_DOMAIN}/capacitor/${appId}/callback`,
    audience: "YOUR_API_IDENTIFIER"
  }}
>
  <App />
</Auth0Provider>
Then make authenticated API calls from your components:
src/ApiCall.tsx
import { useState } from 'react';
import { useAuth0 } from '@auth0/auth0-react';
import { IonButton, IonText } from '@ionic/react';

const ApiCall: React.FC = () => {
  const { getAccessTokenSilently } = useAuth0();
  const [result, setResult] = useState<string | null>(null);

  const callApi = async () => {
    try {
      const token = await getAccessTokenSilently();

      const response = await fetch('https://your-api.example.com/protected', {
        headers: {
          Authorization: `Bearer ${token}`
        }
      });

      const data = await response.json();
      setResult(JSON.stringify(data, null, 2));
    } catch (error) {
      console.error('API call failed:', error);
    }
  };

  return (
    <div className="ion-padding">
      <IonButton onClick={callApi}>Call Protected API</IonButton>
      {result && (
        <IonText>
          <pre>{result}</pre>
        </IonText>
      )}
    </div>
  );
};

export default ApiCall;