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 Angular application with Capacitor. You’ll build a mobile-ready app with login, logout, and user profile features using the Auth0 Angular SDK and Capacitor’s native browser plugins.
1

Create a new project

Create a new Ionic Angular project with Capacitor
npx ionic start auth0-ionic-angular tabs --type=angular-standalone --capacitor
Open the project
cd auth0-ionic-angular
2

Install the Auth0 Angular SDK and Capacitor plugins

Install the Auth0 Angular SDK along with Capacitor’s Browser and App plugins:
npm install @auth0/auth0-angular @capacitor/browser @capacitor/app
Capacitor’s Browser plugin on iOS uses SFSafariViewController, which on iOS 11+ does not share cookies with Safari. 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:
4

Configure the Auth0 module

With your environment file in place from the previous step, configure the Auth0 module in your app:
src/main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { RouteReuseStrategy, provideRouter } from '@angular/router';
import { IonicRouteStrategy, provideIonicAngular } from '@ionic/angular/standalone';
import { provideAuth0 } from '@auth0/auth0-angular';
import { environment } from './environments/environment';
import config from '../capacitor.config';
import { routes } from './app/app.routes';
import { AppComponent } from './app/app.component';

const redirect_uri = `${config.appId}://${environment.auth0.domain}/capacitor/${config.appId}/callback`;

bootstrapApplication(AppComponent, {
  providers: [
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
    provideIonicAngular(),
    provideRouter(routes),
    provideAuth0({
      domain: environment.auth0.domain,
      clientId: environment.auth0.clientId,
      useRefreshTokens: true,
      useRefreshTokensFallback: false,
      authorizationParams: {
        redirect_uri,
      },
    }),
  ],
});
The provideAuth0 configuration includes:
  • useRefreshTokens: truerequired for mobile. Capacitor apps cannot use iframe-based silent authentication, so refresh tokens are used to renew sessions.
  • useRefreshTokensFallback: falserequired for mobile. Prevents the SDK from falling back to iframe-based silent auth, which is unavailable in native apps.
  • authorizationParams.redirect_uri — uses your app’s custom URL scheme to redirect back after authentication.
To persist authentication after closing and reopening the application, you may want to set cacheLocation to localstorage, but please be aware of the risks of storing tokens in localstorage. Also, localstorage should be treated as transient in Capacitor apps. Please read the guidance on storage in the Capacitor docs.
5

Create Login, Logout and Profile components

Create the component files
touch src/app/login-button.component.ts && touch src/app/logout-button.component.ts && touch src/app/profile.component.ts
Add the following code to each component:Now update the App Component to handle Auth0 callbacks, and the home page to use your components:
Replace the contents of src/app/app.component.ts:
src/app/app.component.ts
import { Component, OnInit, NgZone, inject } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
import { mergeMap } from 'rxjs/operators';
import { Browser } from '@capacitor/browser';
import { App } from '@capacitor/app';
import { IonApp, IonRouterOutlet } from '@ionic/angular/standalone';
import { environment } from '../environments/environment';
import config from '../../capacitor.config';

const callbackUri = `${config.appId}://${environment.auth0.domain}/capacitor/${config.appId}/callback`;

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [IonApp, IonRouterOutlet],
  template: '<ion-app><ion-router-outlet></ion-router-outlet></ion-app>',
})
export class AppComponent implements OnInit {
  private auth = inject(AuthService);
  private ngZone = inject(NgZone);

  ngOnInit(): void {
    App.addListener('appUrlOpen', ({ url }) => {
      this.ngZone.run(() => {
        if (url?.startsWith(callbackUri)) {
          if (
            url.includes('state=') &&
            (url.includes('error=') || url.includes('code='))
          ) {
            this.auth
              .handleRedirectCallback(url)
              .pipe(mergeMap(() => Browser.close()))
              .subscribe();
          } else {
            Browser.close();
          }
        }
      });
    });
  }
}
The appUrlOpen event callback in the App Component is wrapped in this.ngZone.run(). This is required because Capacitor plugin callbacks execute outside Angular’s zone, and without it Angular won’t detect the auth state changes after login. See Using Angular with Capacitor for details.
6

Run your app

ionic serve
If port 8100 is in use, run: ionic serve --port 8101. When testing on a device, build first with ionic build && npx cap sync && npx cap open ios (or android). Make sure your custom URL scheme is registered (see Advanced Usage below).
CheckpointYou should now have a fully functional Auth0 login page running on your localhost

Advanced Usage

If you created your project with --type=angular (instead of --type=angular-standalone) or prefer using NgModules, configure the SDK with AuthModule.forRoot:
src/app/app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { AuthModule } from '@auth0/auth0-angular';
import { environment } from '../environments/environment';
import config from '../../capacitor.config';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

const redirect_uri = `${config.appId}://${environment.auth0.domain}/capacitor/${config.appId}/callback`;

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    IonicModule.forRoot(),
    AppRoutingModule,
    AuthModule.forRoot({
      domain: environment.auth0.domain,
      clientId: environment.auth0.clientId,
      useRefreshTokens: true,
      useRefreshTokensFallback: false,
      authorizationParams: {
        redirect_uri,
      },
    }),
  ],
  providers: [{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }],
  bootstrap: [AppComponent],
})
export class AppModule {}
When using NgModules, inject AuthService via the constructor (constructor(public auth: AuthService)) instead of inject(). Use *ngIf="auth.user$ | async as user" in templates instead of @if control flow syntax. Components should be declared in the module rather than marked as standalone: true.
To test authentication on a real device, register your custom URL scheme for each platform.

iOS

Register your custom URL scheme in ios/App/App/Info.plist:
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>YOUR_PACKAGE_ID</string>
    </array>
  </dict>
</array>
Replace YOUR_PACKAGE_ID with your appId from capacitor.config.ts. To learn more, read Defining a Custom URL Scheme.

Android

Add an intent filter to android/app/src/main/AndroidManifest.xml inside the <activity> tag:
<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="YOUR_PACKAGE_ID" />
</intent-filter>
Replace YOUR_PACKAGE_ID with your appId from capacitor.config.ts. To learn more, read Create Deep Links to App Content.

Build and run on device

ionic build && npx cap sync && npx cap open ios
Or for Android:
ionic build && npx cap sync && npx cap open android
Use the functional guard to protect routes that require authentication:
src/app/app.routes.ts
import { Routes } from '@angular/router';
import { authGuardFn } from '@auth0/auth0-angular';

export const routes: Routes = [
  {
    path: 'tabs',
    loadComponent: () => import('./tabs/tabs.page').then((m) => m.TabsPage),
    children: [
      {
        path: 'tab1',
        loadComponent: () => import('./tab1/tab1.page').then((m) => m.Tab1Page),
      },
      {
        path: 'tab2',
        loadComponent: () => import('./tab2/tab2.page').then((m) => m.Tab2Page),
        canActivate: [authGuardFn],
      },
      {
        path: 'tab3',
        loadComponent: () => import('./tab3/tab3.page').then((m) => m.Tab3Page),
        canActivate: [authGuardFn],
      },
      {
        path: '',
        redirectTo: '/tabs/tab1',
        pathMatch: 'full',
      },
    ],
  },
  {
    path: '',
    redirectTo: '/tabs/tab1',
    pathMatch: 'full',
  },
];
When unauthenticated users navigate to a protected route, authGuardFn automatically redirects them to the Auth0 login page.
Configure the HTTP interceptor to automatically attach access tokens to API calls:
src/main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { RouteReuseStrategy, provideRouter } from '@angular/router';
import { IonicRouteStrategy, provideIonicAngular } from '@ionic/angular/standalone';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { provideAuth0, authHttpInterceptorFn } from '@auth0/auth0-angular';
import { environment } from './environments/environment';
import config from '../capacitor.config';
import { routes } from './app/app.routes';
import { AppComponent } from './app/app.component';

const redirect_uri = `${config.appId}://${environment.auth0.domain}/capacitor/${config.appId}/callback`;

bootstrapApplication(AppComponent, {
  providers: [
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
    provideIonicAngular(),
    provideRouter(routes),
    provideAuth0({
      domain: environment.auth0.domain,
      clientId: environment.auth0.clientId,
      useRefreshTokens: true,
      useRefreshTokensFallback: false,
      authorizationParams: {
        redirect_uri,
        audience: 'YOUR_API_IDENTIFIER',
      },
      httpInterceptor: {
        allowedList: [
          'http://localhost:3001/api/*',
        ],
      },
    }),
    provideHttpClient(
      withInterceptors([authHttpInterceptorFn])
    ),
  ],
});
Then make API calls using Angular’s HttpClient — the interceptor automatically attaches the Bearer token:
src/app/api.service.ts
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({ providedIn: 'root' })
export class ApiService {
  private http = inject(HttpClient);

  getData() {
    return this.http.get('http://localhost:3001/api/data');
  }
}
The httpInterceptor.allowedList determines which API endpoints receive access tokens. Include the audience parameter to request an access token for your API. Replace YOUR_API_IDENTIFIER with the identifier from Auth0 Dashboard > APIs.

Callback URL mismatch error

Solution: Verify the callback URL in your Auth0 Dashboard matches exactly with the URL constructed in your app. Ensure YOUR_PACKAGE_ID matches the appId field in your capacitor.config.ts.

Screen does not update after login

Solution: Ensure the appUrlOpen event callback is wrapped in this.ngZone.run(). Without this, Angular won’t detect the state changes from handleRedirectCallback. See Using Angular with Capacitor.

”PKCE not allowed” error

Fix:
  1. Go to Auth0 Dashboard > Applications > Your Application
  2. Change the Application Type to Native
  3. Set Token Endpoint Authentication Method to None
  4. Save changes and try again

SSO not working on iOS

Capacitor’s Browser plugin uses SFSafariViewController, which does not share cookies with Safari on iOS 11+. If you need SSO, use a compatible plugin that uses ASWebAuthenticationSession.

Login works but user stays unauthenticated after app restart

Enable cacheLocation: 'localstorage' in the provideAuth0 configuration to persist tokens across app restarts. Be aware of the security implications and Capacitor storage limitations.

Observable never executes

All AuthService methods return cold Observables. You must call .subscribe() for them to execute. If loginWithRedirect() or logout() appears to do nothing, check that .subscribe() is chained at the end.

Next Steps

Check out the following resources to learn more: