Auth Connect Angular
This tutorial follows on from the Installation and auth provider guides (Auth0, AWS Cognito, Azure AD B2C or Okta) and integrates functionality for login, logout and the user's authenticated state in your Ionic Angular application.
Build Time Options
The configuration settings (such as redirectUri
) of your auth provider will change depending on your target environment (e.g. production, staging or development). Settings should not be hard coded in your application and a good practice is to use Angular's Environment files to allow these settings to be set at build time.
Revisiting our AuthenticationService
class, the IonicAuth object has been moved to environment.ts
:
_10import { IonicAuth, IonicAuthOptions } from '@ionic-enterprise/auth';_10import { authOptions } from '../../environments/environment';_10_10export class AuthenticationService extends IonicAuth {_10_10constructor() { _10 super(authOptions);_10 }_10}
In the projects environment.ts
file we add:
_12import { IonicAuthOptions } from "@ionic-enterprise/auth";_12_12export const authOptions: IonicAuthOptions = {_12 authConfig: 'FILL_IN',_12 platform: 'FILL_IN',_12 clientID: 'FILL_IN',_12 discoveryUrl: 'FILL_IN',_12 redirectUri: 'FILL_IN',_12 scope: 'FILL_IN',_12 logoutUrl: 'FILL_IN',_12 iosWebView: 'FILL_IN'_12};
To keep development settings separate from production you should apply similar code to your environment.prod.ts
file using the settings for the production instance of your auth provider.
Run Time Options
Authentication options will also change at runtime depending on where your application is run (eg Native vs Web). In the AuthenticationService
class we can dynamically initialize options in the constructor:
_10import { Platform } from '@ionic/angular';_10import { nativeIonicAuthOptions, webIonicAuthOptions } from '../../environments/environment';_10..._10constructor(platform: Platform) {_10 super(platform.is('hybrid') ? nativeIonicAuthOptions : webIonicAuthOptions);_10 }
Here we have taken our authOptions object and refactored to provide 2 objects for native and web.
Login
Your application is now ready to trigger a login flow. Lets create a button for this:
_10<ion-button (click)="login()">Sign In</ion-button>
And call the login method of IonicAuth:
_10import { AuthenticationService } from '../authentication/authentication.service';_10..._10constructor(private authenticationService: AuthenticationService) { }_10_10async login(): Promise<void> {_10 await this.authenticationService.login();_10}
You can now run your application and test the login flow. It is worth noting that the login page for your auth provider will open in its own window (and if already logged in will open and close immediately) and once a successful login is achieved will return a token using the redirectUri property back to your application.
A common mistake is an incorrectly set redirectUri so be sure to test for development (such as when run via ionic serve
) and production (where Auth Connect depends on the deep linking setup in your project) to ensure the login page closes successfully.
Logout
Similar to login, test your logout flow by adding a button:
_10<ion-button (click)="logout()">Sign Out</ion-button>
Followed by calling the logout method of IonicAuth:
_10 async logout(): Promise<void> {_10 this.authenticationService.logout();_10 }
Authenticated?
Whilst Auth Connect provides a isAuthenticated method to determine whether a user is authenticated or not, your application will likely have multiple places that need to reactively change based on this state. We can extend our authenticationService
to emit events whenever the authentication state changes:
_33import { Injectable, NgZone } from '@angular/core';_33import { IonicAuth } from '@ionic-enterprise/auth';_33import { Platform } from '@ionic/angular';_33import { BehaviorSubject, Observable } from 'rxjs';_33import { nativeIonicAuthOptions, webIonicAuthOptions } from '../../environments/environment';_33_33@Injectable({_33 providedIn: 'root'_33})_33export class AuthenticationService extends IonicAuth {_33 private authenticationChange: BehaviorSubject<boolean> = new BehaviorSubject(false);_33 public authenticationChange$: Observable<boolean>;_33_33 constructor(platform: Platform, private ngZone: NgZone) {_33 super(platform.is('hybrid') ? nativeIonicAuthOptions : webIonicAuthOptions);_33 this.authenticationChange$ = this.authenticationChange.asObservable();_33 this.isAuthenticated().then((authenticated) => { this.onAuthChange(authenticated); });_33 }_33_33 public async onLoginSuccess(): Promise<void> {_33 this.onAuthChange(true);_33 }_33_33 public async onLogout(): Promise<void> {_33 this.onAuthChange(false);_33 }_33_33 private async onAuthChange(isAuthenticated: boolean): Promise<void> {_33 this.ngZone.run(() => {_33 this.authenticationChange.next(isAuthenticated);_33 });_33 }_33}
The events from IonicAuth run outside of Angular's change detection system so to get correct behavior when binding to a view we need to ensure that NgZone
is run.
We can now update our buttons to show/hide based on our authenticated state:
_10<ion-button [hidden]="(authenticationChange$ | async)" (click)="login()">Sign In</ion-button>_10<ion-button [hidden]="!(authenticationChange$ | async)" (click)="logout()">Sign Out</ion-button>
In the code behind the view:
_10import { Observable } from 'rxjs';_10..._10 public authenticationChange$: Observable<boolean>;_10_10 constructor(private authenticationService: AuthenticationService) {_10 this.authenticationChange$ = authenticationService.authenticationChange$;_10 }
Next Steps
Your application is now functional but there are some recommended next steps such as:
- Familiarizing yourself with the IonicAuth methods and events.
- Ensuring your token information is stored securely (see TokenStorageProvider).
- Using Identity Vault and Secure Storage for security best practices and biometric authentication.