npx skills add ...
npx skills add auth0/agent-skills --skill auth0-ionic-react
npx skills add auth0/agent-skills --skill auth0-ionic-react
Use when adding Auth0 login, logout, or deep linking to an Ionic React app with Capacitor. Integrates @auth0/auth0-react with Capacitor Browser and App plugins for native iOS/Android.
Add Auth0 authentication to Ionic React applications using Capacitor. This skill covers native mobile authentication using the @auth0/auth0-react SDK combined with @capacitor/browser and @capacitor/app plugins for deep link handling on iOS and Android.
npm install -g @ionic/cli)brew install auth0/auth0-cli/auth0| Use Case | Recommended Skill |
|---|---|
| React SPA (no Capacitor/Ionic) | auth0-react |
| React Native (bare CLI) | auth0-react-native |
| Expo (React Native) | auth0-expo |
| Ionic + Angular + Capacitor | auth0-ionic-angular |
| Ionic + Vue + Capacitor | auth0-ionic-vue |
| Next.js (server-side) | auth0-nextjs |
| iOS native (Swift) | auth0-swift |
| Android native (Kotlin) | auth0-android |
For automated setup with Auth0 CLI, see Setup Guide for complete scripts.
For manual setup, configure a Native application in the Auth0 Dashboard and note your Domain and Client ID.
Wrap the app root with Auth0Provider, configuring it for Capacitor. In src/main.tsx:
Agent instruction: After integration, verify the build:
For iOS:
npx cap open iosthen build in Xcode. For Android:npx cap open androidthen build in Android Studio. If the build fails, iterate up to 5-6 times to fix issues. If still failing, useAskUserQuestionto request help.
| Mistake | Fix |
|---|---|
| App type not set to Native in Auth0 Dashboard | Change application type to "Native" in Dashboard settings |
| Missing or incorrect callback URL format | Use YOUR_PACKAGE_ID://YOUR_DOMAIN/capacitor/YOUR_PACKAGE_ID/callback — must match exactly |
| Not enabling refresh tokens | Set useRefreshTokens={true} and useRefreshTokensFallback={false} on Auth0Provider |
Missing @capacitor/browser or @capacitor/app | Install both: npm install @capacitor/browser @capacitor/app && npx cap sync |
| Not handling deep link callback | Add CapApp.addListener('appUrlOpen', ...) to process Auth0 redirect |
Forgetting npx cap sync after install | Always run npx cap sync after installing Capacitor plugins |
Using window.location.origin as redirect URI | Use the custom URL scheme (packageId://domain/...), not http://localhost |
| Missing Allowed Origins in Dashboard | Add capacitor://localhost, http://localhost to Allowed Origins |
| localStorage treated as persistent on mobile | Use refresh tokens (useRefreshTokens={true}) for reliable token persistence |
| iOS SSO not working | SFSafariViewController doesn't share cookies with Safari on iOS 11+; this is expected |
| Not testing on physical device | Always test auth flows on a physical device; simulators may not handle deep links correctly |
This SDK uses Auth0's Universal Login (WebAuth) via the Capacitor Browser plugin. The loginWithRedirect() method opens the Auth0 authorization endpoint in a system browser (SFSafariViewController on iOS, Chrome Custom Tabs on Android). After authentication, Auth0 redirects back to the app using a native callback URL with a custom scheme: {packageId}://{domain}/capacitor/{packageId}/callback. The @capacitor/app plugin captures this deep link, and handleRedirectCallback(url) processes the authorization code exchange.
Unlike standard native SDKs that use https://{domain}/android/{packageId}/callback or https://{domain}/ios/{bundleId}/callback, Ionic Capacitor apps use the Capacitor-specific callback path with the package ID as the URL scheme.
| API | Description |
|---|---|
Auth0Provider | Context provider — wraps app root with Auth0 config |
useAuth0() | Hook — returns { isLoading, isAuthenticated, user, loginWithRedirect, logout, getAccessTokenSilently, handleRedirectCallback } |
loginWithRedirect({ openUrl }) | Login via Universal Login — use Browser.open() in openUrl callback |
logout({ logoutParams, openUrl }) | Logout — use Browser.open() in openUrl callback |
handleRedirectCallback(url) | Process Auth0 callback URL from deep link |
getAccessTokenSilently() | Get access token (uses refresh tokens on mobile) |
withAuthenticationRequired(Component) | HOC to protect routes |
Browser.open({ url }) | Capacitor — opens URL in system browser (SFSafariViewController / Chrome Custom Tabs) |
CapApp.addListener('appUrlOpen', cb) | Capacitor — listens for deep link events |
Browser.close() | Capacitor — closes the in-app browser after callback |
import { useAuth0 } from '@auth0/auth0-react';
import { Browser } from '@capacitor/browser';
const { loginWithRedirect } = useAuth0();
const login = async () => {
await loginWithRedirect({
async openUrl(url) {
await Browser.open({ url, windowName: "_self" });
}
});
};import { useEffect } from 'react';
import { App as CapApp } from '@capacitor/app';
import { Browser } from '@capacitor/browser';
import { useAuth0 } from '@auth0/auth0-react';
const { handleRedirectCallback } = useAuth0();
useEffect(() => {
const listener = CapApp.addListener('appUrlOpen', async ({ url }) => {
if (url.includes('state') && (url.includes('code') || url.includes('error'))) {
await handleRedirectCallback(url);
}
await Browser.close();
});
return () => {
listener.then(l => l.remove());
};
}, [handleRedirectCallback]);const doLogout = async () => {
await logout({
logoutParams: {
returnTo: `${packageId}://${domain}/capacitor/${packageId}/callback`
},
async openUrl(url) {
await Browser.open({ url, windowName: "_self" });
}
});
};