ionic logo
typescript logo

Ionic Angular Authentication By Example

Published on May 31, 2023

This guide will help you learn how to secure an Ionic Angular with Capacitor mobile application using token-based authentication. You'll learn how to use the Ionic and the Angular frameworks to implement the following security features:

  • How to add user login, sign-up, and logout to Ionic Angular Applications.
  • How to create route guards to protect Ionic Angular application routes.
  • How to make API calls from Ionic Angular to request data from a protected API.
  • How to get user profile information to personalize an Ionic Angular user interface.

This guide uses the Auth0 Angular SDK, which provides developers with a high-level API to handle many authentication implementation details. You can now secure your Ionic Angular applications following security best practices while writing less code.

If you want to learn how to integrate Auth0 by Okta to an Ionic Angular application targeting the web, follow the Angular Authentication By Example guide.

Quick Ionic Angular Setup

With the help of Auth0 by Okta, you don't need to be an expert on identity protocols, such as OAuth 2.0 or OpenID Connect, to understand how to secure your web application stack.

You first integrate your Ionic Angular application with Auth0. Your application will then redirect users to an Auth0 customizable login page when they need to log in. Once your users log in successfully, Auth0 redirects them back to your Ionic Angular application, returning JSON Web Tokens (JWTs) with their authentication and user information.

Get the Ionic Angular Starter Application

We have created a starter project using the Ionic CLI to help you learn Ionic Angular security concepts through hands-on practice. You can focus on building the components and services to secure your application.

Start by cloning the mobile_ionic_typescript_hello-world_angular repository on its starter branch:

COMMAND
git clone -b starter [email protected]:auth0-developer-hub/mobile_ionic_typescript_hello-world_angular.git

Then, make mobile_ionic_typescript_hello-world_angular your current directory:

COMMAND
cd mobile_ionic_typescript_hello-world_angular

Install the project dependencies as follows:

COMMAND
npm install

This starter Ionic Angular project offers a functional application that consumes data from an external API to hydrate the user interface. For simplicity and convenience, the starter project simulates the external API locally using json-server. Later, you'll integrate this Ionic Angular application with a real API server using a backend technology of your choice.

The compatible API server runs on http://localhost:6060 by default. As such, to connect your Ionic Angular application with that API server, create a .env file under the root project directory:

COMMAND
touch .env

Populate .env with the following environment variables:

.env
API_SERVER_URL=http://localhost:6060 # Or http://10.0.2.2:6060 if you are running the Ionic Angular app on Android
When running on the Android emulator. Make sure to update the API_SERVER_URL to http://10.0.2.2:6060. 10.0.2.2 is a special alias to your host loopback interface (127.0.0.1 or localhost on your development machine).

This project uses an npm script to integrate the content of the .env file with the Angular framework. Check out the set-env.ts file:

set-env.ts
const { writeFile, openSync, closeSync } = require('fs');
const { promisify } = require('util');
const dotenv = require('dotenv');
dotenv.config();
const writeFilePromisified = promisify(writeFile);
const targetPath = './src/environments/environment.ts';
const prodTargetPath = './src/environments/environment.prod.ts';
const envConfigFile = `export const environment = {
production: false,
api: {
serverUrl: '${process.env['API_SERVER_URL']}',
},
};
`;
(async () => {
try {
openSync(targetPath, 'w');
await writeFilePromisified(targetPath, envConfigFile);
openSync(prodTargetPath, 'w');
await writeFilePromisified(prodTargetPath, envConfigFile);
} catch (err) {
console.error(err);
throw err;
}
})();

This script uses the dotenv package to load environment variables from a .env file into process.env. The script then uses a string template to create the content of the ./src/environments/environment.ts file. It then writes that file with the prescribed content into the Angular project.

The env npm script defined in package.json runs the set-env.ts script using ts-node. However, you don't have to execute npm run env directly. The start and build npm scripts will run that for you.

Execute the the following command to build the application:

COMMAND
npm run build

Execute the following command to run the application using the appropriate command for iOS and Android:

IOS
ANDROID
npx cap run ios

Next, execute the following command to run the JSON server API:

COMMAND
npm run api

You are ready to start implementing user authentication in this Ionic Angular project. First, you must configure the application to connect successfully to Auth0. Afterward, you'll use the Auth0 Angular SDK to protect routes, display user profile information, and request protected data from an external API server to hydrate some application pages.

Configure Ionic Angular with Auth0

Follow these steps to get started with the Auth0 Identity Platform quickly:

Sign up and create an Auth0 Application

A free account also offers you:

During the sign-up process, you create something called an Auth0 Tenant, representing the product or service to which you are adding authentication.

Once you sign in, Auth0 takes you to the Dashboard. In the left sidebar menu, click on "Applications".

Then, click the "Create Application" button. A modal opens up with a form to provide a name for the application and choose its type. Use the following values:

Name
Auth0 Ionic Angular Code Sample
Application Type
Single Page Web Applications
Single Page Web Applications

Click the "Create" button to complete the process. Your Auth0 application page loads up.

Get the Auth0 Domain and Auth0 Client ID

From the Auth0 Application Settings page, you need the Auth0 Domain and Client ID values to allow your Ionic Angular application communicate with Auth0.

What exactly is an Auth0 Domain and an Auth0 Client ID?

Domain

When you created a new Auth0 account, Auth0 asked you to pick a name for your tenant. This name, appended with auth0.com, is your Auth0 Domain. It's the base URL that you will use to access the Auth0 APIs and the URL where you'll redirect users to log in.

You can also use custom domains to allow Auth0 to do the authentication heavy lifting for you without compromising your branding experience.

Client ID

Each application is assigned a Client ID upon creation, which is an alphanumeric string, and it's the unique identifier for your application (such as q8fij2iug0CmgPLfTfG1tZGdTQyGaTUA). You cannot modify the Client ID. You will use the Client ID to identify the Auth0 Application to which the Auth0 SPA SDK needs to connect.

Warning: Another critical piece of information present in the "Settings" is the Client Secret. This secret protects your resources by only granting tokens to requestors if they're authorized. Think of it as your application's password, which must be kept confidential at all times. If anyone gains access to your Client Secret, they can impersonate your application and access protected resources.

In the Auth0 application page click on the "Settings" tab.

Locate the "Basic Information" section and follow these steps to get the Auth0 Domain and Auth0 Client ID values:

Auth0 application settings to enable user authentication

When you enter a value in the input fields present on this page, any code snippet that uses such value updates to reflect it. Using the input fields makes it easy to copy and paste code as you follow along.

As such, enter the "Domain" and "Client ID" values in the following fields to set up your single-page application in the next section:

For security, these configuration values are stored in memory and only used locally. They are gone as soon as you refresh the page! As an extra precaution, you should use values from an Auth0 test application instead of a production one.

In the next step, you'll learn how to help Angular and Auth0 communicate.

What's the relationship between Auth0 Tenants and Auth0 Applications?

Let's say that you have a photo-sharing Angular app called "NG-Gram". You then would create an Auth0 tenant called ng-gram. From a customer perspective, NG-Gram is that customer's product or service.

Now, say that NG-Gram is available on three platforms: web as a single-page application and Android and iOS as a native mobile application. If each platform needs authentication, you need to create three Auth0 applications to provide the product with everything it needs to authenticate users through that platform.

NG-Gram users belong to the Auth0 NG-Gram tenant, which shares them across its Auth0 applications.

Create a communication bridge between Angular and Auth0

When using the Auth0 Identity Platform, you don't have to build login forms. Auth0 offers a Universal Login Page to reduce the overhead of adding and managing authentication.

How does Universal Login work?

Your Ionic Angular application will redirect users to Auth0 whenever they trigger an authentication request. Auth0 will present them with a login page. Once they log in, Auth0 will redirect them back to your Ionic Angular application. For that redirecting to happen securely, you must specify in your Auth0 Application Settings the URLs to which Auth0 can redirect users once it authenticates them.

As such, locate the "Application URIs" section of your Auth0 Application page, and fill in the following values:

Allowed Callback URLs
io.ionic.starter://AUTH0-DOMAIN/capacitor/io.ionic.starter/callback

The above value is the URL that Auth0 can use to redirect your users after they successfully log in.

Allowed Logout URLs
io.ionic.starter://AUTH0-DOMAIN/capacitor/io.ionic.starter/callback

The above value is the URL that Auth0 can use to redirect your users after they log out.

Notice that the allowed callback and logout URLs follow the PACKAGE_ID://AUTH0_DOMAIN/capacitor/PACKAGE_ID/callback pattern. In this case, io.ionic.starter is your application's package ID. You can find and configure the appId field in your capacitor.config.ts file. See Capacitor's Config schema for more information.
Allowed Web Origins
capacitor://localhost, http://localhost

Using the Auth0 Angular SDK, your Ionic Angular application will make requests under the hood to an Auth0 URL to handle authentication requests. As such, you need to add your Ionic Angular application origin URL to avoid Cross-Origin Resource Sharing (CORS) issues.

Allowed Origins (CORS)
capacitor://localhost, http://localhost

Scroll down and click the "Save Changes" button.

Add the Auth0 configuration variables to Ionic Angular

These variables let your Ionic Angular application identify itself as an authorized party to interact with the Auth0 authentication server.

Now, update the .env file under the Angular project directory as follows:

.env
API_SERVER_URL=http://localhost:6060 # Or http://10.0.2.2:6060 if you are running the Ionic Angular app on Android
AUTH0_DOMAIN=AUTH0-DOMAIN
AUTH0_CLIENT_ID=AUTH0-CLIENT-ID
AUTH0_CALLBACK_URL=io.ionic.starter://AUTH0-DOMAIN/capacitor/io.ionic.starter/callback

Once you reach the "Call a Protected API from Ionic Angular" section of this guide, you'll learn how to use API_SERVER_URL along with an Auth0 Audience value to request protected resources from an external API that is also protected by Auth0. For now, the application is using json-server to mock the API.

Update the set-env.ts script file to integrate these new Auth0 environment variables from .env into your Angular src/environments/environment.ts file:

set-env.ts
const { writeFile, openSync, closeSync } = require('fs');
const { promisify } = require('util');
const dotenv = require('dotenv');
dotenv.config();
const writeFilePromisified = promisify(writeFile);
const targetPath = './src/environments/environment.ts';
const prodTargetPath = './src/environments/environment.prod.ts';
const envConfigFile = `export const environment = {
production: false,
auth0: {
domain: '${process.env['AUTH0_DOMAIN']}',
clientId: '${process.env['AUTH0_CLIENT_ID']}',
authorizationParams: {
redirect_uri: '${process.env['AUTH0_CALLBACK_URL']}',
},
},
api: {
serverUrl: '${process.env['API_SERVER_URL']}',
},
};
`;
(async () => {
try {
openSync(targetPath, 'w');
await writeFilePromisified(targetPath, envConfigFile);
openSync(prodTargetPath, 'w');
await writeFilePromisified(prodTargetPath, envConfigFile);
} catch (err) {
console.error(err);
throw err;
}
})();

You're creating an auth0 object using the configuration values from the Auth0 application you created in the Auth0 Dashboard: Auth0 Domain and Client ID.

Additionally, you use the authorizationParams configuration object to define the query parameters that your Ionic Angular application needs to include on its calls to the Auth0 /authorize endpoint. You define the redirect_uri property within this object to specify the URL from your Ionic Angular application to where Auth0 should redirect your users after they successfully log in.

Later, you'll use the auth0 object properties to configure the AuthModule from the Auth0 Angular SDK using the forRoot() pattern.

Run the following command to re-build the application with the latest src/environments/environment.ts file:

COMMAND
npm run build

Install and Set Up the Auth0 Angular SDK

Execute the following command to install the Auth0 Angular SDK:

COMMAND
npm install @auth0/auth0-angular

The Auth0 Angular SDK exposes several methods, variables, and types that help you integrate Auth0 with your Ionic Angular application idiomatically, including an authentication module and service.

Update the src/app/app.module.ts file as follows to import the AuthModule from the Auth0 Angular SDK into your AppModule and configure it using data from your environment module:

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 { HttpClientModule } from '@angular/common/http';
import { environment as env } from '../environments/environment';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { FEATURE_COMPONENTS } from './features';
import { SharedModule } from './shared';
@NgModule({
declarations: [AppComponent, ...FEATURE_COMPONENTS],
imports: [
BrowserModule,
HttpClientModule,
IonicModule.forRoot(),
AuthModule.forRoot({
...env.auth0,
}),
AppRoutingModule,
SharedModule
],
providers: [
{
provide: RouteReuseStrategy,
useClass: IonicRouteStrategy
},
],
bootstrap: [AppComponent],
})
export class AppModule {}

You use the forRoot() pattern to configure AuthModule, which takes an object with the domain and clientId properties. You create that configuration object by spreading the env.auth object.

User authentication is a mechanism to control who can access your application. You can integrate your Ionic Angular application with Auth0 to prevent users who have not logged in from accessing a /profile or /admin route.

If users want to access a guarded route from your application, Auth0 will stop them and ask them to present their credentials. If Auth0 can verify who they are and that they are supposed to go in there, Auth0 will let them in.

The authentication process won't happen within your Ionic Angular application layer when using Auth0. Your Ionic Angular application will redirect your users to the Auth0 Universal Login page, where Auth0 asks them for credentials and redirects them back to your application with the result of the authentication process.

Install Capacitor plugins

Execute the following command to install the necessary Capacitor plugins:

COMMAND
npm install @capacitor/browser @capacitor/app --legacy-peer-deps

The @capacitor/browser plugin allows you to interact with the device's system browser. You'll use it to perform a redirect to the Auth0 Universal Login Page using the device's system browser component, providing a better user experience.

The @capacitor/app plugin allows you to subscribe to high-level app events. You'll use @capacitor/app to listen to appUrlOpen events to handle the post-login and logout behavior (closing the browser and redirecting the users to the appropriate pages).

Persist authentication after closing and reopening the application

To persist authentication after closing and reopening the application, you may want to set cacheLocation to localstorage when configuring the SDK, but please be aware of the risks of storing tokens in localstorage. Also, local storage should be treated as transient in the Capacitor app, as the data might be recovered unexpectedly in certain circumstances. Please read the guidance on storage in the Capacitor docs.

Additionally, the SDK has the ability to use a custom cache implementation to store tokens if you have a requirement to use a more secure and persistent storage mechanism.

For security, we recommend against using Capacitor's Storage plugin to store tokens, as this is backed by UserDefaults and SharedPreferences on iOS and Android respectively. Data stored using these APIs is not encrypted, or secure and could also be synced to the cloud.

Setup iOS CFBundleURLTypes

iOS requires additional setup to enable the app to respond to callback and logout URL calls. Update your Info.plist to include the CFBundleURLTypes property with the following configurations:

ios/App/App/Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>mobile_ionic_typescript_hello-world_angular</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>None</string>
<key>CFBundleURLName</key>
<string>auth0</string>
<key>CFBundleURLSchemes</key>
<array>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
</array>
</dict>
</array>
</dict>
</plist>

Setup Android intent-filter

Similar to iOS, Android also requires additional setup to enable the app to respond to callback and logout URL calls. Update your AndroidManifest.xml to include the following intent-filter properties:

android/app/src/main/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8" ?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.ionic.starter">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:networkSecurityConfig="@xml/network_security_config">
<activity
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
android:name="io.ionic.starter.MainActivity"
android:label="@string/title_activity_main"
android:theme="@style/AppTheme.NoActionBarLaunch"
android:launchMode="singleTask"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<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>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" />
</provider>
</application>
<!-- Permissions -->
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

Handle the Auth0 post-login behavior

Once users log in with the Universal Login Page, they are redirected back to the Ionic Angular application via a URL with a custom URL scheme. You'll need to handle the appUrlOpen event for this Ionic Angular application.

So, let's open the file src/app/app.component.ts as follows to do so:

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 { environment } from 'src/environments/environment';
import { Router } from '@angular/router';
import { Capacitor } from '@capacitor/core';
import { asyncScheduler } from 'rxjs';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.scss'],
})
export class AppComponent implements OnInit {
auth = inject(AuthService);
ngZone = inject(NgZone);
router = inject(Router);
ngOnInit(): void {
// Use Capacitor's App plugin to subscribe to the `appUrlOpen` event
App.addListener('appUrlOpen', ({ url }) => {
// Must run inside an NgZone for Angular to pick up the changes
// https://capacitorjs.com/docs/guides/angular
this.ngZone.run(() => {
if (url?.startsWith(environment.auth0.authorizationParams.redirect_uri)) {
// If the URL is an authentication callback URL.
if (
url.includes('state=') &&
(url.includes('error=') || url.includes('code='))
) {
// Call handleRedirectCallback and close the browser
this.auth
.handleRedirectCallback(url)
.pipe(
mergeMap(() => {
// browser close only works on iOS right now
if (Capacitor.getPlatform() === 'ios') {
// add promise rejection handler to account for app being opened not via the in-app browser
return Browser.close().catch(() => {
return Promise.resolve();
})
}
return Promise.resolve();
})
)
.subscribe(() => {
// wait for next tick
asyncScheduler.schedule(() => {
// redirect to profile when logging in
this.router.navigate(['/profile']);
})
});
} else {
// browser close only works on iOS right now
if (Capacitor.getPlatform() === 'ios') {
// add promise rejection handler to account for app being opened not via the in-app browser
Browser.close().catch(() => {
return Promise.resolve();
})
}
// redirect to home when logging out
this.router.navigate(['/']);
}
}
});
});
}
}

Let's recap what is happening in the above code:

You start by checking if the URL matches the redirect_uri from the Auth0 config to ensure you're only processing Auth0 redirects. Next, you check the presence of the state and code or error parameters in the URL to verify whether the authentication call succeeded or failed. Next, you call the Browser.close() method to close the browser and return to the application. Finally, you redirect users to the profile.

Note that the appUrlOpen event callback is wrapped in ngZone.run to ensure that Angular picks up the changes from the handleRedirectCallback method. Without the ngZone.run, the UI won't be updated after the observable emits to show the authenticated state after logging in.

Auth0 and Angular connection set

You have completed setting up an authentication module that your Ionic Angular application can consume. All that is left is for you to continue building up the starter project throughout this guide by implementing Angular components that trigger and manage the authentication flow.

Feel free to dive deeper into the Auth0 Documentation to learn more about how Auth0 helps you save time implementing and managing identity.

Add User Login to Ionic Angular

The steps on how to build an Angular login form or login page are complex. You can save development time by using a login page hosted by Auth0 that has a built-in login form that supports different types of user authentication: username and password, social login, and Multi-Factor Authentication (MFA). You just need to create a button that takes users from your Ionic Angular application to the login page.

Start by creating a buttons directory under the src/app/shared/components directory:

COMMAND
mkdir src/app/shared/components/buttons

Create a login-button.component.ts file under the src/app/shared/components/buttons directory:

COMMAND
touch src/app/shared/components/buttons/login-button.component.ts

Populate the login-button.component.ts file like so:

src/app/shared/components/buttons/login-button.component.ts
import { Component, inject } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
import { Browser } from '@capacitor/browser';
@Component({
selector: 'app-login-button',
template: `
<ion-button class="button__login" expand="block" fill="solid" mode="ios" (click)="handleLogin()">Log In</ion-button>
`,
})
export class LoginButtonComponent {
auth = inject(AuthService);
handleLogin(): void {
this.auth
.loginWithRedirect({
authorizationParams: {
prompt: 'login',
},
openUrl: (url) => Browser.open({ url, windowName: '_self' })
}).subscribe();
}
}

Within the LoginButtonComponent definition, this.auth.loginWithRedirect() is a method exposed by AuthService that performs a redirect to the Auth0 /authorize endpoint to kickstart the authentication process. You can pass a configuration object to this method to customize the login experience.

By default, the loginWithRedirect method uses window.location.href to navigate to the login page in the default browser application on the user's device rather than the system browser component appropriate for the platform.

To provide a better user experience using the browser component, you are setting the openUrl parameter with a function to call the Browser.open method from the Capacitor's Browser plugin so that the redirect to the Auth0 Universal Login Page is done using the device's system browser component (SFSafariViewController on iOS, and Chrome Custom Tabs on Android).

In the next section, you'll configure this method to create a button that your users can click on to sign up for your application.

Add User Sign-Up to Ionic Angular

The process on how to build an Angular sign-up form is much more complex. However, you can use a sign-up form hosted by Auth0 that has a built-in password strength verification.

You can create a button that takes users from your Ionic Angular application to the sign-up page by specifying the screen_hint=signup property in the authorizationParams configuration object of the loginWithRedirect() method:

authorizationParams: {
screen_hint: "signup",
}

This loginWithRedirect() method is a wrapper from the Auth0 SPA SDK method of the same name. As such, you can use the RedirectLoginOptions document from the Auth0 SPA SDK to learn more details on these configuration options.

To see this in practice, create a signup-button.component.ts file under the src/app/shared/components/buttons directory:

COMMAND
touch src/app/shared/components/buttons/signup-button.component.ts

Populate the signup-button.component.ts file like so to define a sign-up button component:

src/app/shared/components/buttons/signup-button.component.ts
import { Component, inject } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
import { Browser } from '@capacitor/browser';
@Component({
selector: 'app-signup-button',
template: `
<ion-button class="button__sign-up" expand="block" fill="clear" mode="ios" (click)="handleSignUp()">Sign Up</ion-button>
`,
})
export class SignupButtonComponent {
auth = inject(AuthService);
handleSignUp(): void {
this.auth
.loginWithRedirect({
authorizationParams: {
prompt: 'login',
screen_hint: 'signup'
},
openUrl: (url) => Browser.open({ url, windowName: '_self' })
}).subscribe();
}
}

Using the Auth0 Signup feature requires you to enable the Auth0 New Universal Login Experience in your tenant.

Open the Universal Login section of the Auth0 Dashboard and choose the "New" option under the "Experience" subsection.

Auth0 Universal Login Experience options

Scroll down and click on the "Save Changes" button.

The difference between the login and sign-up user experience will be more evident once you integrate those components with your Ionic Angular application and see them in action. You'll do that in the following sections.

Add User Logout to Ionic Angular

You can log out users from your Ionic Angular application by logging them out of their Auth0 sessions using the logout() method from the Auth0 Angular SDK.

To see this in practice, create a logout-button.component.ts file under the src/app/shared/components/buttons directory:

COMMAND
touch src/app/shared/components/buttons/logout-button.component.ts

Populate the logout-button.component.ts file as follows:

src/app/shared/components/buttons/logout-button.component.ts
import { Component, inject } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
import { Browser } from '@capacitor/browser';
import { environment } from 'src/environments/environment';
@Component({
selector: 'app-logout-button',
template: `
<ion-button class="button__logout" expand="block" fill="solid" mode="ios" (click)="handleLogout()">Log Out</ion-button>
`,
})
export class LogoutButtonComponent {
auth = inject(AuthService);
handleLogout(): void {
this.auth
.logout({
logoutParams: {
returnTo: environment.auth0.authorizationParams.redirect_uri
},
openUrl: (url) => Browser.open({ url })
})
.subscribe();
}
}

When using the logout() method, the Auth0 Angular SDK clears the application session and redirects to the Auth0 /v2/logout endpoint to clear the Auth0 session under the hood.

By default, the SDK's logout() method opens up the default browser application on the user's device to perform a logout. Similar to the login sequence, you can use Capacitor's Browser plugin to perform this redirect which uses the device's system browser component within the application instead of launching the default browser application.

As with the login method, you can pass an object argument to logout() to customize the logout behavior of the Ionic Angular application. You can define a logoutParams property on that configuration object to define parameters for the /v2/logout call. This process is fairly invisible to the user. Here, you pass the logoutParams.returnTo option to specify the URL where Auth0 should redirect your users after they log out. See logoutParams for more details on the parameters available.

Render Components Conditionally

In this section, you'll learn how to render Angular components conditionally based on the status of the Auth0 Angular SDK or the authentication status of your users.

Render the authentication buttons conditionally

The Ionic Angular starter application features a desktop and mobile navigation experience.

When using your Ionic Angular application on a viewport large enough to fix a desktop or tablet experience, you'll see a navigation bar at the top of the page.

When using a viewport that fits the screen constraints of a mobile device, you'll see a menu button at the top-right corner of the page. Tapping or clicking on the menu button opens a modal that shows you the different pages that you can access in the application.

In this section, you'll expose the button components that trigger the login, sign-up, and logout events through these page navigation elements.

Add the LoginButtonComponent, SignupButtonComponent, and LogoutButtonComponent to the declarations and exports array of the SharedModule like so:

src/app/shared/shared.module.ts
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { IonicModule } from '@ionic/angular';
import { COMPONENTS } from './components';
import { LoginButtonComponent } from './components/buttons/login-button.component';
import { LogoutButtonComponent } from './components/buttons/logout-button.component';
import { SignupButtonComponent } from './components/buttons/signup-button.component';
@NgModule({
declarations: [
...COMPONENTS,
LoginButtonComponent,
SignupButtonComponent,
LogoutButtonComponent
],
imports: [CommonModule, RouterModule, IonicModule],
exports: [
...COMPONENTS,
LoginButtonComponent,
SignupButtonComponent,
LogoutButtonComponent
],
})
export class SharedModule {}

Let's start with the desktop navigation user experience. You'll show both the login and sign-up buttons on the navigation bar when the user is not logged in. Naturally, you'll show the logout button when when the user is logged in.

Create an isAuthenticated$ variable in the NavBarButtonsComponent to implement the user experience defined above:

src/app/shared/components/navigation/desktop/nav-bar-buttons.component.ts
import { Component, inject } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
@Component({
selector: 'app-nav-bar-buttons',
templateUrl: './nav-bar-buttons.component.html',
})
export class NavBarButtonsComponent {
auth = inject(AuthService);
isAuthenticated$ = this.auth.isAuthenticated$;
}

Next, update the src/app/shared/components/navigation/desktop/nav-bar-buttons.component.html as follows to conditionally show and hide login, sign-up, and logout buttons:

src/app/shared/components/navigation/desktop/nav-bar-buttons.component.html
<div class="nav-bar__buttons">
<ng-container *ngIf="isAuthenticated$ | async; else loggedOut">
<app-logout-button></app-logout-button>
</ng-container>
<ng-template #loggedOut>
<app-signup-button></app-signup-button>
<app-login-button></app-login-button>
</ng-template>
</div>

Auth0's isAuthenticated$ value reflects the authentication state of your users as tracked by the Auth0 Angular SDK plugin. This value is true when the user has been authenticated and false when not. As such, you can use the isAuthenticated$ observable to render UI elements conditionally depending on the authentication state of your users, as you did above.

The mobile navigation experience works in the same fashion, except that the authentication-related buttons are tucked into the mobile menu modal.

Update src/app/shared/components/navigation/mobile/mobile-nav-bar-buttons.component.ts as follows:

src/app/shared/components/navigation/mobile/mobile-nav-bar-buttons.component.ts
import { Component, inject } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
@Component({
selector: 'app-mobile-nav-bar-buttons',
templateUrl: './mobile-nav-bar-buttons.component.html',
})
export class MobileNavBarButtonsComponent {
auth = inject(AuthService);
isAuthenticated$ = this.auth.isAuthenticated$;
}

Next, update the src/app/shared/components/navigation/mobile/mobile-nav-bar-buttons.component.html as follows to conditionally show and hide login, sign-up, and logout buttons:

src/app/shared/components/navigation/mobile/mobile-nav-bar-buttons.component.html
<div class="mobile-nav-bar__buttons">
<app-logout-button
*ngIf="isAuthenticated$ | async; else loggedOut"
></app-logout-button>
<ng-template #loggedOut>
<app-signup-button></app-signup-button>
<app-login-button></app-login-button>
</ng-template>
</div>

Go ahead and try to log in, don't forget to build and restart the application:

COMMAND
npm run build

Restart your Ionic Angular application using the appropriate command for iOS and Android:

IOS
ANDROID
npx cap run ios

Your Ionic Angular application redirects you to the Auth0 Universal Login page. You can use the form to log in with a username and password or a social identity provider like Google. Notice that this login page also gives you the option to sign up.

New Auth0 Universal Login Experience Form

However, when you click the sign-up button from your application directly, Angular takes you to the Signup page, where your users can sign up for the Angular application. Try it out!

New Auth0 Universal Login Experience Signup Page
You can customize the appearance of New Universal Login pages. You can also override any text in the New Experience using the Text Customization API.

Notice that when you finish logging in or signing up, Auth0 redirects you to your Ionic Angular app, but the login and sign-up buttons may briefly show up before the logout button renders. You'll fix that next.

Render a loader conditionally

The user interface flashes because your Ionic Angular app doesn't know if Auth0 has authenticated the user yet when navigating from the browser back to the app.

To fix that UI flashing, create a isAuth0Loading$ BehaviorSubject to render a loader that above our application's content. The isAuth0Loading$ variable is set to true when the appUrlOpen event is triggered and set back to false after the app is done processing the Auth0 related redirects.

Start by creating the isAuth0Loading$ BehaviorSubject and updating it's value inside the appUrlOpen event listener:

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 { environment } from 'src/environments/environment';
import { Router } from '@angular/router';
import { Capacitor } from '@capacitor/core';
import { asyncScheduler, BehaviorSubject } from 'rxjs';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.scss'],
})
export class AppComponent implements OnInit {
auth = inject(AuthService);
ngZone = inject(NgZone);
router = inject(Router);
// handle this manually as the loader should be displayed immediately once the app
// is opened via the auth0 redirect uri
isAuth0Loading$ = new BehaviorSubject<boolean>(false);
ngOnInit(): void {
// Use Capacitor's App plugin to subscribe to the `appUrlOpen` event
App.addListener('appUrlOpen', ({ url }) => {
// Must run inside an NgZone for Angular to pick up the changes
// https://capacitorjs.com/docs/guides/angular
this.ngZone.run(() => {
if (url?.startsWith(environment.auth0.authorizationParams.redirect_uri)) {
this.isAuth0Loading$.next(true);
// If the URL is an authentication callback URL.
if (
url.includes('state=') &&
(url.includes('error=') || url.includes('code='))
) {
// Call handleRedirectCallback and close the browser
this.auth
.handleRedirectCallback(url)
.pipe(
mergeMap(() => {
// browser close only works on iOS right now
if (Capacitor.getPlatform() === 'ios') {
// add promise rejection handler to account for app being opened not via the in-app browser
return Browser.close().catch(() => {
return Promise.resolve();
})
}
return Promise.resolve();
})
)
.subscribe(() => {
// wait for next tick
asyncScheduler.schedule(() => {
// redirect to profile when logging in
this.router.navigate(['/profile']);
this.isAuth0Loading$.next(false);
})
});
} else {
// browser close only works on iOS right now
if (Capacitor.getPlatform() === 'ios') {
// add promise rejection handler to account for app being opened not via the in-app browser
Browser.close().catch(() => {
return Promise.resolve();
})
}
// redirect to home when logging out
this.router.navigate(['/']);
this.isAuth0Loading$.next(false);
}
} else {
this.isAuth0Loading$.next(false);
}
});
});
}
}

Open src/app/app.component.html and update it as follows:

src/app/app.component.html
<ion-app>
<div class="page-layout">
<app-nav-bar></app-nav-bar>
<app-mobile-nav-bar></app-mobile-nav-bar>
<ion-router-outlet mode="ios" id="main-content"></ion-router-outlet>
</div>
<ng-container *ngIf="isAuth0Loading$ | async">
<app-page-loader></app-page-loader>
</ng-container>
</ion-app>

Log out and log back in to see this in action. A loader should be displayed, and no more UI flashing should happen.

Render navigation tabs conditionally

There may be use cases where you want to hide user interface elements from users who have not logged in to your application. For this starter application, only authenticated users should see the navigation tabs to access the /protected and /admin pages.

To implement this use case, you'll rely once again on the isAuthenticated$ Observable from the AuthService.

Open the src/app/shared/components/navigation/desktop/nav-bar-tabs.component.ts component file that defines your desktop navigation tabs and update it like so:

src/components/navigation/desktop/nav-bar-tabs.component.ts
import { Component, inject } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
@Component({
selector: 'app-nav-bar-tabs',
templateUrl: './nav-bar-tabs.component.html',
})
export class NavBarTabsComponent {
auth = inject(AuthService);
isAuthenticated$ = this.auth.isAuthenticated$;
}

Open the src/app/shared/components/navigation/desktop/nav-bar-tabs.component.html component file and update it as follows:

src/app/shared/components/navigation/desktop/nav-bar-tabs.component.html
<div class="nav-bar__tabs">
<app-nav-bar-tab path="/profile" label="Profile"></app-nav-bar-tab>
<app-nav-bar-tab path="/public" label="Public"></app-nav-bar-tab>
<ng-container *ngIf="isAuthenticated$ | async">
<app-nav-bar-tab path="/protected" label="Protected"></app-nav-bar-tab>
<app-nav-bar-tab path="/admin" label="Admin"></app-nav-bar-tab>
</ng-container>
</div>

Next, open the src/app/shared/components/navigation/mobile/mobile-nav-bar-tabs.component.ts component file that defines your mobile navigation tabs and update it like so:

src/app/shared/components/navigation/mobile/mobile-nav-bar-tabs.component.ts
import { Component, EventEmitter, inject, Output } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
@Component({
selector: 'app-mobile-nav-bar-tabs',
templateUrl: './mobile-nav-bar-tabs.component.html',
})
export class MobileNavBarTabsComponent {
@Output() mobileNavBarTabClick = new EventEmitter<string>();
auth = inject(AuthService);
isAuthenticated$ = this.auth.isAuthenticated$;
onMobileNavBarTabClick(path: string): void {
this.mobileNavBarTabClick.emit(path);
}
}

Open the src/app/shared/components/navigation/mobile/mobile-nav-bar-tabs.component.html component file and update it as follows:

src/app/shared/components/navigation/mobile/mobile-nav-bar-tabs.component.html
<div class="mobile-nav-bar__tabs">
<app-mobile-nav-bar-tab
path="/profile"
label="Profile"
(mobileNavBarTabClick)="onMobileNavBarTabClick($event)"
></app-mobile-nav-bar-tab>
<app-mobile-nav-bar-tab
path="/public"
label="Public"
(mobileNavBarTabClick)="onMobileNavBarTabClick($event)"
></app-mobile-nav-bar-tab>
<ng-container *ngIf="isAuthenticated$ | async">
<app-mobile-nav-bar-tab
path="/protected"
label="Protected"
(mobileNavBarTabClick)="onMobileNavBarTabClick($event)"
></app-mobile-nav-bar-tab>
<app-mobile-nav-bar-tab
path="/admin"
label="Admin"
(mobileNavBarTabClick)="onMobileNavBarTabClick($event)"
></app-mobile-nav-bar-tab>
</ng-container>
</div>

Log out from your Ionic Angular application and notice how now you can only see the tabs for the /profile and /public pages in the navigation bar, along with the login and sign-up buttons. Log in and then see the rest of the navigation bar show up.

Keep in mind that this does not restrict access to the /admin and /protected pages at all. You'll learn how to use the Auth0 Angular SDK to protect Angular routes in the next section.

Add Route Guards to Ionic Angular

You can create an authentication route guard to protect Angular routes. Angular will ask users who visit the route to log in if they haven't already. Once they log in, Angular takes them to the route they tried to access.

You can apply a guard to any route defined in the Angular router module by updating src/app/app-routing.module.ts as follows:

src/app/app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from '@auth0/auth0-angular';
import { AdminComponent, HomeComponent, NotFoundComponent, ProfileComponent, ProtectedComponent, PublicComponent } from './features';
const routes: Routes = [
{
path: '',
pathMatch: 'full',
component: HomeComponent
},
{
path: 'profile',
component: ProfileComponent,
canActivate: [AuthGuard]
},
{
path: 'public',
component: PublicComponent
},
{
path: 'protected',
component: ProtectedComponent,
canActivate: [AuthGuard]
},
{
path: 'admin',
component: AdminComponent,
canActivate: [AuthGuard]
},
{
path: '**',
component: NotFoundComponent
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}

You use the AuthGuard from the Auth0 Angular SDK to protect the /profile, /protected, and /admin routes by adding it as the value of the canActivate route configuration property.

If the conditions defined by AuthGuard pass, the component renders. Otherwise, AuthGuard instructs Angular to take you to the Auth0 Universal Login Page to authenticate.

You can now test that these guarded paths require users to log in before accessing them. Log out and try to access the Profile page by clicking on the "Profile" button in the navigation bar. If it works, Angular redirects you to log in with Auth0.

Once you log in, Angular should take you to the /profile page as specified by the appUrlOpen event handler.

Client-side guards improve the user experience of your Ionic Angular application, not its security.

In Security StackExchange, Conor Mancone explains that server-side guards are about protecting data while client-side guards are about improving user experience.

The main takeaways from his response are:

  • You can't rely on client-side restrictions, such as navigation guards and protected routes, to protect sensitive information.
    • Attackers can potentially get around client-side restrictions.
  • Your server should not return any data that a user should not access. The wrong approach is to return all the user data from the server and let the front-end framework decide what to display and what to hide based on the user authentication status.
    • Anyone can open the browser's developer tools and inspect the network requests to view all the data.
  • The use of navigation guards helps improve user experience, not user security.
    • Without guards, a user who has not logged in may wander into a page with restricted information and see an error like "Access Denied".
    • With guards that match the server permissions, you can prevent users from seeing errors by preventing them from visiting the restricted page.

Get User Profile Information in Ionic Angular

After a user successfully logs in, Auth0 sends an ID token to your Ionic Angular application. Authentication systems, such as Auth0, use ID Tokens in token-based authentication to cache user profile information and provide it to a client application. The caching of ID tokens can improve the performance and responsiveness of your Ionic Angular application.

You can use the data from the ID token to personalize the user interface of your Ionic Angular application. The Auth0 Angular SDK decodes the ID token and stores its data in the user$ Observable exposed via the AuthService. Some of the ID token information includes the name, nickname, picture, and email of the logged-in user.

How can you use the ID token to create a profile page for your users?

Update src/app/features/profile/profile.component.ts as follows:

src/app/features/profile/profile.component.ts
import { Component, inject } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
import { map } from 'rxjs/operators';
@Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
})
export class ProfileComponent {
auth = inject(AuthService);
title = 'Decoded ID Token';
user$ = this.auth.user$;
code$ = this.user$.pipe(map((user) => JSON.stringify(user, null, 2)));
}

Next, update src/app/features/profile/profile.component.html as follows:

src/app/features/profile/profile.component.html
<app-page-layout>
<div class="content-layout">
<ion-text><h1 id="page-title" class="content__title">Profile Page</h1></ion-text>
<div class="content__body">
<ion-text>
<p id="page-description">
<span>
You can use the <strong>ID Token</strong> to get the profile
information of an authenticated user.
</span>
<span>
<strong>Only authenticated users can access this page.</strong>
</span>
</p>
</ion-text>
<ng-container *ngIf="user$ | async as user">
<div class="profile-grid">
<div class="profile__header">
<ion-img [src]="user.picture" alt="Profile" class="profile__avatar"></ion-img>
<div class="profile__headline">
<ion-text><h2 class="profile__title">{{ user.name }}</h2></ion-text>
<ion-text><span class="profile__description">{{ user.email }}</span></ion-text>
</div>
</div>
<ng-container *ngIf="code$ | async as code">
<div class="profile__details">
<app-code-snippet
[title]="title"
[code]="code"
></app-code-snippet>
</div>
</ng-container>
</div>
</ng-container>
</div>
</div>
</app-page-layout>

What's happening within the ProfileComponent?

  • You display three properties from the user object in the user interface: name, picture, and email.

  • Since the data comes from an Observable, you can subscribe to it directly in the template via the async pipe.

  • Finally, you display the full content of the decoded ID token within a code box. You can now see all the other properties available for you to use. The properties are known as "token claims".

The ProfileComponent renders user information that you could consider private or sensitive. Additionally, the user property is null if there is no logged-in user. So either way, this component should only render if Auth0 has authenticated the user. You are already restricting access to this page component by using the authGuard in the /profile route definition of your Angular routes, src/app/routes.ts.

If you are logged in to your application, visit the Profile Page to see your user profile details.

Authentication Beyond Passwords: Try Passkeys Today

So far, you have seen how a user can sign up or log in to your application with a username and password. However, you can free your users from having to remember yet another password by allowing them to use passkeys as a new way to log in.

Passkeys are a phishing-resistant alternative to traditional authentication factors, such as the username/password combo, that offer an easier and more secure login experience to users.

You don't have to write any new code to start using passkeys in your application. You can follow the "Authentication with Passkeys" lab to learn how to enable passkeys in your Auth0 tenant and learn more about this emerging technology. Once you complete that optional lab, you can come back to this guide to continue learning about how to access protected API resources on behalf of a user from your application.

A form modal giving you information on how a passkey works and the option to create a passkey

Integrate Ionic Angular with an API Server

This section focuses on showing you how to get an access token in your Ionic Angular application and how to use it to make API calls to protected API endpoints.

When you use Auth0, you delegate the authentication process to a centralized service. Auth0 provides you with functionality to log in and log out users from your Ionic Angular application. However, your application may need to access protected resources from an API.

You can also protect an API with Auth0. There are multiple API quickstarts to help you integrate Auth0 with your backend platform.

When you use Auth0 to protect your API, you also delegate the authorization process to a centralized service that ensures only approved client applications can access protected resources on behalf of a user.

How can you make secure API calls from Angular?

Your Angular application authenticates the user and receives an access token from Auth0. The application can then pass that access token to your API as a credential. In turn, your API can use Auth0 libraries to verify the access token it receives from the calling application and issue a response with the desired data.

Instead of creating an API from scratch to test the authentication and authorization flow between the client and the server, you can pair this client application with an API server that matches the technology stack you use at work. The Angular "Hello World" client application that you have been building up can interact with any of the "Hello World" API server samples from the Auth0 Developer Hub.

The "Hello World" API server samples run on http://localhost:6060 by default, which is the same origin URL and port where the mocked JSON server is running. As such, before you set up the "Hello World" API server, locate the tab where you are running the npm run api command and stop the mocked JSON server.

The Hello World API server samples only allow HTTP and HTTPS requests from the http://localhost:4040 origin. To call the external API from your Ionic Angular application, you must allow requests from capacitor://localhost for iOS and http://localhost for Android in the CORS API server settings. For more information on troubleshooting CORS errors in Ionic applications, visit Ionic CORS Errors documentation.

Pick an API code sample in your preferred backend framework and language from the list below and follow the instructions on the code sample page to set it up. Once you complete the sample API server setup, please return to this page to learn how to integrate that API server with your Ionic Angular application.

actix-web
rust
Actix Web/Rust API:Authorization Code Sample
Code sample of a simple Actix Web server that implements token-based authorization using Auth0.
aspnet-core
csharp
ASP.NET Core Code Sample:Web API Authorization
Code sample of a simple ASP.NET Core server that implements token-based authorization using Auth0.
aspnet-core
csharp
ASP.NET Core v5 Code Sample:Web API Authorization
Code sample of a simple ASP.NET Core v5.0 server that implements token-based authorization using Auth0.
django
python
Django/Python API:Authorization Code Sample
Code sample of a simple Django server that implements token-based authorization using Auth0.
express
javascript
Express.js Code Sample:Basic API Authorization
Code sample of a simple Express.js server that implements token-based authorization using Auth0.
express
typescript
Express.js/TypeScript Code Sample:Basic API Authorization
Code sample of a simple Express.js server built with TypeScript that implements token-based authorization using Auth0.
fastapi
python
FastAPI/Python Code Sample:Basic API Authorization
Code sample of a simple FastAPI server that implements token-based authorization using Auth0.
flask
python
Flask/Python API:Authorization Code Sample
Code sample of a simple Flask server that implements token-based authorization using Auth0.
laravel
php
Laravel/PHP Code Sample:Basic API Authorization with Auth0 Laravel SDK
Code sample of a simple Laravel server that implements token-based authorization using the Auth0 Laravel SDK.
laravel
php
Laravel/PHP Code Sample:Basic API Authorization with Auth0 PHP SDK
Code sample of a simple Laravel server that implements token-based authorization using the Auth0 PHP SDK.
lumen
php
Lumen Code Sample:Basic API Authorization
Code sample of a simple Lumen server that implements token-based authorization using Auth0.
nestjs
typescript
NestJS Code Sample:Basic API Authorization
Code sample of a simple NestJS server that implements token-based authorization using Auth0.
phoenix
elixir
Phoenix/Elixir API:Authorization Code Sample
Code sample of a simple Phoenix server that implements token-based authorization using Auth0.
rails
ruby
Ruby on Rails API:Authorization Code Sample
Code sample of a simple Rails server that implements authorization using Auth0.
spring
java
Spring Code Sample:Basic API Authorization
Java code sample that implements token-based authorization in a Spring Web API server to protect API endpoints, using Spring Security and the Okta Spring Boot Starter.
spring
java
Spring Functional Code Sample:Basic API Authorization
Java code sample that implements token-based authorization in a Spring Web API server to protect API endpoints, following a functional approach.
spring-webflux
java
Spring WebFlux Code Sample:Basic API Authorization
Java code sample that implements token-based authorization in a Spring WebFlux API server to protect API endpoints, using Spring Security and the Okta Spring Boot Starter.
standard-library
golang
Golang Code Sample:Basic API Authorization
Code sample of a simple Golang server that implements token-based authorization using Auth0.
symfony
php
Symfony Code Sample:Basic API Authorization
Code sample of a simple Symfony server that implements token-based authorization using Auth0.

Call a Protected API from Ionic Angular

Once you have set up the API server code sample, you should have created an Auth0 Audience value. Store that value in the following field so that you can use it throughout the instructions presented on this page easily:

Now, update the .env file under the Ionic Angular project directory as follows:

.env
API_SERVER_URL=http://localhost:6060 # Or http://10.0.2.2:6060 if you are running the Ionic Angular app on Android
AUTH0_DOMAIN=AUTH0-DOMAIN
AUTH0_CLIENT_ID=AUTH0-CLIENT-ID
AUTH0_CALLBACK_URL=io.ionic.starter://AUTH0-DOMAIN/capacitor/io.ionic.starter/callback
AUTH0_AUDIENCE=AUTH0-AUDIENCE

You are using AUTH0_AUDIENCE to add the value of your Auth0 API Audience so that your Ionic Angular application can request resources from the API that such audience value represents.

Let's understand better what the AUTH0_AUDIENCE and API_SERVER_URL values represent.

The API_SERVER_URL is simply the URL where your sample API server listens for requests. In production, you'll change this value to the URL of your live server.

Your Ionic Angular application must pass an access token when it calls a target API to access protected resources. You can request an access token in a format that the API can verify by passing the audience to the Auth0 Angular SDK.

The value of the Auth0 Audience must be the same for both the Ionic Angular application and the API server you decided to set up.

Why is the Auth0 Audience value the same for both apps? Auth0 uses the value of the audience prop to determine which resource server (API) the user is authorizing your Ionic Angular application to access. It's like a phone number. You want to ensure that your Ionic Angular application "texts the right API".

Update the set-env.ts script file to integrate these new Auth0 environment variables from .env into your Angular src/environments/environment.ts file:

set-env.ts
const { writeFile, openSync, closeSync } = require('fs');
const { promisify } = require('util');
const dotenv = require('dotenv');
dotenv.config();
const writeFilePromisified = promisify(writeFile);
const targetPath = './src/environments/environment.ts';
const prodTargetPath = './src/environments/environment.prod.ts';
const envConfigFile = `export const environment = {
production: false,
auth0: {
domain: '${process.env['AUTH0_DOMAIN']}',
clientId: '${process.env['AUTH0_CLIENT_ID']}',
authorizationParams: {
redirect_uri: '${process.env['AUTH0_CALLBACK_URL']}',
audience: '${process.env['AUTH0_AUDIENCE']}',
},
},
api: {
serverUrl: '${process.env['API_SERVER_URL']}',
},
};
`;
(async () => {
try {
openSync(targetPath, 'w');
await writeFilePromisified(targetPath, envConfigFile);
openSync(prodTargetPath, 'w');
await writeFilePromisified(prodTargetPath, envConfigFile);
} catch (err) {
console.error(err);
throw err;
}
})();

Run the following command to re-generate the src/environments/environment.ts file:

COMMAND
npm run env

You are now including an audience property in the authorizationParams configuration object you pass to the AuthModule.forRoot() method. Recall that the AuthModule method initializes the authentication module system.

What about using scopes?

A property that you are not configuring directly in the AuthModule.forRoot() method is the scope property. When you don't pass a scope option to Auth0 Angular SDK, which powers Auth0Plugin, the SDK defaults to using the OpenID Connect Scopes: openid profile email.

  • openid: This scope informs the Auth0 Authorization Server that the Client is making an OpenID Connect (OIDC) request to verify the user's identity. OpenID Connect is an authentication protocol.

  • profile: This scope value requests access to the user's default profile information, such as name, nickname, and picture.

  • email: This scope value requests access to the email and email_verified information.

The details of the OpenID Connect Scopes go into the ID Token. However, you can define custom API scopes to implement access control. You'll identify those custom scopes in the calls that your client applications make to that API. Auth0 includes API scopes in the access token as the scope claim value.

The concepts about API scopes or permissions are better covered in an Auth0 API tutorial such as "Use TypeScript to Create a Secure API with Node.js and Express: Role-Based Access Control".

The Auth0 Angular SDK provides an HttpInjector that automatically attaches access tokens to outgoing requests when using the built-in Angular HttpClient module. However, you must configure the injector to know to which requests it needs to attach access tokens.

Update the configuration of the AuthModule present in the imports array of AppModule and add the AuthHttpInterceptor to the providers array as follows:

src/app/app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { AuthHttpInterceptor, AuthModule } from '@auth0/auth0-angular';
import { environment as env } from '../environments/environment';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { FEATURE_COMPONENTS } from './features';
import { SharedModule } from './shared';
@NgModule({
declarations: [AppComponent, ...FEATURE_COMPONENTS],
imports: [
BrowserModule,
HttpClientModule,
IonicModule.forRoot(),
AuthModule.forRoot({
...env.auth0,
httpInterceptor: {
allowedList: [`${env.api.serverUrl}/api/messages/admin`, `${env.api.serverUrl}/api/messages/protected`],
},
}),
AppRoutingModule,
SharedModule
],
providers: [
{
provide: RouteReuseStrategy,
useClass: IonicRouteStrategy
},
{
provide: HTTP_INTERCEPTORS,
useClass: AuthHttpInterceptor,
multi: true,
},
],
bootstrap: [AppComponent],
})
export class AppModule {}

Let's break down what is happening in the above code:

First, you are importing AuthHttpInterceptor from @auth0/auth0-angular along with HTTP_INTERCEPTORS from @angular/common/http. HTTP_INTERCEPTORS is a multi-provider token that represents the array of registered HttpInterceptor objects.

Next, you register the AuthHttpInterceptor injector as a provider:

providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: AuthHttpInterceptor,
multi: true,
},
],

This completes the wiring needed to connect the AuthHttpInterceptor with your Angular application request cycle.

Now, you need to tell the SDK to which requests to attach access tokens by further configuring AuthModule.forRoot(). Based on that configuration, Angular will match the URL of any request that you make using HttpClient against an allowed list of URLs:

httpInterceptor: {
allowedList: [`${env.api.serverUrl}/api/messages/admin`, `${env.api.serverUrl}/api/messages/protected`],
},

If there's a match, Angular attaches an access token to the authorization header of the request. You can use a string or a regular expression for the URL matching. For now, you are allowing Angular to attach an access token to requests it makes to http://localhost:6060/api/messages/protected and http://localhost:6060/api/messages/admin.

That's all it takes to integrate Ionic Angular with an external API server that is also secured by Auth0 and to use an access token to consume protected server resources from your Angular client application.

Conclusion

You have implemented user authentication in an Ionic Angular application to identify your users, get user profile information, and control the content that your users can access by protecting routes and API resources.

This guide covered the most common authentication use case for an Ionic Angular application: simple login and logout. However, Auth0 is an extensible and flexible identity platform that can help you achieve even more. If you have a more complex use case, check out the Auth0 Architecture Scenarios to learn more about the typical architecture scenarios we have identified when working with customers on implementing Auth0.

We'll cover advanced authentication patterns and tooling in future guides, such as using a pop-up instead of redirecting users to log in, adding permissions to ID tokens, using metadata to enhance user profiles and much more.