An example Ionic Angular application that integrates Auth0 authentication and runs on the web, iOS, and Android using Capacitor. It uses Angular 20 standalone APIs and Ionic 8 UI components.
- Angular 20 + Ionic 8 standalone components
- Auth0 login/logout with custom Browser.open handler
- Conditional UI for authenticated vs unauthenticated users
- Capacitor 7 for native builds (iOS/Android)
- Xcode (for iOS) and Android Studio (for Android)
- Auth0 tenant with an application configured
Caution
After login, the response from Auth0 will include sensitive data like your access token. It's imperative that this is stored in a secure manner. Please refer Auth0's Token Best Practices Documentation
- Install dependencies
npm install
ionic cap run [`android` or `ios`]
- Install the Auth0 Angular SDK (already in package.json)
- The bare minimum values needed are:
domain
: Your Auth0 account's domainclientId
: The Client ID found on your Application settings pageuseRefreshTokens: true,
Per Auth0's docs, this is recommended for native appsuseRefreshTokensFallback: false,
Per Auth0's docs, this is recommended for native appsauthorizationParams.redirect_uri
: The default URL where Auth0 will redirect your browser to with the authentication result. It must be whitelisted in the "Allowed Callback URLs" field in your Auth0 Application's settings. See theRedirect Configuration
section for more information
Warning
For Hybrid applications like this, its strongly recommended that you also implement an ICache that can handle data persistance for your app securely. This can be passed via the AuthConfig
as config
src/environments/environment.base.ts
//
import { Capacitor } from '@capacitor/core';
// redirectUri is set based on whether the app is deployed to the web, or running in a native app
const redirectUri = Capacitor.isNativePlatform() ? 'io.ionic.starter://callback' : 'http://localhost:8100/home';
export const environment = {
production: false,
auth: {
domain: '<YOUR_AUTH0_DOMAIN>',
clientId: '<YOUR_AUTH0_CLIENT_ID>',
useRefreshTokens: true,
useRefreshTokensFallback: false,
authorizationParams: {
redirect_uri: redirectUri
}
}
};
- Register Allowed Callback URLs in Auth0:
- Web:
http://localhost:8100/home
- iOS/Android (Capacitor scheme):
io.ionic.starter://callback
- Web:
- Register Allowed Logout URLs:
http://localhost:8100/home
io.ionic.starter://callback
- Wire up the Auth0 provider
Add the initialization to your app bootstrap (e.g.,
main.ts
) or root provider configuration using Angular standalone providers:
import { bootstrapApplication } from '@angular/platform-browser';
import { RouteReuseStrategy, provideRouter, withPreloading, PreloadAllModules } from '@angular/router';
import { IonicRouteStrategy, provideIonicAngular } from '@ionic/angular/standalone';
import { provideAuth0, AuthService } from '@auth0/auth0-angular';
import { mergeMap } from 'rxjs/operators';
import { Browser } from '@capacitor/browser';
import { App } from '@capacitor/app';
import { routes } from './app/app.routes';
import { AppComponent } from './app/app.component';
import { inject, NgZone, provideAppInitializer } from '@angular/core';
import { provideHttpClient } from '@angular/common/http';
import { environment } from './environments/environment';
bootstrapApplication(AppComponent, {
providers: [
...
provideAuth0(environment.authConfig),
provideIonicAngular(),
provideHttpClient(),
provideRouter(routes, withPreloading(PreloadAllModules)),
// AppInitializer: sets up a Capacitor deep-link listener to process Auth0 redirects on iOS/Android.
// Why: On native, Auth0 sends users back to the app using a custom scheme (e.g., io.ionic.starter://callback).
// This listener catches that URL, hands it to Auth0 SDK for parsing (code/error/state), and then closes the in-app browser.
// see: https://angular.dev/api/core/provideAppInitializer
provideAppInitializer(() => {
// Lazy DI within initializer scope
const authService = inject(AuthService);
const ngZone = inject(NgZone);
// The redirect URI configured for Auth0; used to detect callback URLs
const redirectUri = environment.authConfig?.authorizationParams?.redirect_uri;
// Subscribe to the OS-level URL open event (e.g., app resumes via custom scheme)
App.addListener('appUrlOpen', ({ url }) => {
// Must run inside NgZone so Angular change detection sees the auth state updates
// See: https://capacitorjs.com/docs/guides/angular
ngZone.run(() => {
// Ignore unrelated deep links and only handle Auth0 callback URLs
if (redirectUri && url?.startsWith(redirectUri)) {
// If the URL is an authentication callback URL, it should include state and either code or error
if (
url.includes('state=') &&
(url.includes('error=') || url.includes('code='))
) {
// Hand the full URL to the Auth0 SDK; then close the in-app browser (SafariViewController/Custom Tabs)
authService
.handleRedirectCallback(url)
.pipe(mergeMap(() => Browser.close()))
.subscribe();
} else {
// Not an auth callback (or missing expected params) — still close the browser UI
Browser.close();
}
}
});
});
})
...
]
});
We need to establish an appUrlOpen
handler via the Capcitor App plugin. This will allow your app to process Auth0's authentication result. In this example, we're using provideAppInitializer to ensure it's executed during app initialization.
npm start
App runs at http://localhost:8100 by default; Ionic apps often proxy to 8100. This project expects the logout returnTo http://localhost:8100/home
for web, so ensure your dev server matches (you can also update the URL in home.page.ts
).
ionic build
The production build emits to www/
per capacitor.config.ts
.
Sync web assets and open native projects:
npx cap sync
npx cap run ios
npx cap run android
- iOS/Android deep link scheme is
io.ionic.starter://callback
(seecapacitor.config.ts
and code inhome.page.ts
). Update the scheme if you changeappId
. - Ensure the same callback is allowed in your Auth0 tenant.
You'll need to make sure that you make the necessary changes in your Android and iOS native apps to ensure that your app is properly configured handle this. Generally speaking, a solid approach would be to define a custom URL scheme that aligns with your application's identifer.
- Android: This is done via an
<intent-filter>
in your Android project'sAndroidManifest.xml
. See the example below. See the Android Documentation for more information<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="@string/custom_url_scheme" /> </intent-filter>
- iOS: Defining Custom URLs can be done directly in your app's
Info.plist
, or in Xcode. See the iOS Documentation for more information
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLSchemes</key>
<array>
<string>io.ionic.starter</string>
</array>
</dict>
</array>
HomePage.login()
usesAuthService.loginWithRedirect()
with a customopenUrl
to route through Capacitor Browser and stay in-app (windowName: '_self'
).HomePage.logout()
setsreturnTo
differently for native vs web:- Native:
io.ionic.starter://callback
- Web:
http://localhost:8100/home
- Native: