Secure Your Single-Page Angular Apps Using App ID

4 min read

As a cloud developer, securing a single-page application (SPA) can be an intricate process with many different security protocols to consider. 

With IBM Cloud App ID in your toolbox, however, you can easily secure your Angular single-page application by using the App ID JavaScript SDK to add authentication and authorization.

With just a few lines of code, you can easily add a level of security to your SPA by requiring that users sign in to gain access to protected resources. The App ID SDK gives you the ability to do the following:

  • Enforce social or enterprise sign-in (such as Facebook, Google, and SAML 2.0 Federation) as identity provider options to your apps by using APIs, SDKs, prebuilt UIs, or your own branded UIs through verification of authentication tokens. 
  • Personalize your app experiences by storing user data, such as preferences and information that is returned by public social profiles.
  • Manage a user registry that you can easily scale as your user base grows.
  • Enforce policy-driven security for your apps by integrating with IBM Cloud Kubernetes Service at the cluster level.

Securing Angular SPAs

In this blog post, I’m going to walk you through configuring an Angular single-page application (SPA) to work with App ID by using the JavaScript SDK. 

Working with another frontend framework? No problem, follow along for the concepts but use your code. However, if your app has a backend that you manage or needs to be refreshed to load content, try working with the web app flow instead.

Understanding the flow

Before we get started, it is important to get the context of what we are about to do. The figure below shows how to securely authenticate and authorize single-page application users with the Authorization Code with Proof Key for Code Exchange (PKCE):

Figure 1: The authorization flow of single-page applications.

Figure 1: The authorization flow of single-page applications.

  1. A user attempts to log in to your single-page application.
  2. The App ID SDK creates a code verifier for the authorization request, which is the plain text version of the code challenge. 
  3. Along with the authorization request, the client sends the code challenge and the challenge method that is used to encode the challenge.
  4. The authentication flow is started by App ID in a new window.
  5. The user chooses an identity provider to authenticate with and completes the sign-in process.
  6. The App ID SDK on the application receives the grant code.
  7. The SDK then makes an XHR request to the App ID token endpoint along with the grant code and the code verifier.
  8. App ID returns access and identity tokens.

Note that the App ID JavaScript SDK is one of very few on the market that uses the Authorization Code with Proof Key for Code Exchange (PKCE) flow instead of the implicit flow to secure your SPAs. Although it is currently the industry standard, the OAuth working group no longer recommends using the implicit flow due to several security concerns. For more information about why we chose PKCE over implicit, see the docs.

Prerequisites

Before you get started with your Angular SPA, be sure that you have the following prerequisites:

Note: You can skip the steps below by checking out the repository of our Angular sample SPA which you can clone and have running quickly to see how App ID can easily secure your application.

The steps below will take about 12 minutes to complete.

Prepare your workspace

  1. In your terminal, install the Angular CLI:
    npm install -g @angular/cli
  2. Create a workspace and initial application:
    ng new angular-sample-spa
    There will be follow-up questions after running the command above, go ahead and select your preferences as shown below:
    • Would you like to add Angular routing? y
    • Which stylesheet format would you like to use? CSS
  3. Navigate to the workspace folder:
    cd angular-sample-spa
  4. Install the IBM Cloud AppID SDK dependency using npm:
    npm install ibmcloud-appid-js

Create your application

  1. In your terminal, navigate to the app folder of your application and create two Angular components—welcome and home:
    cd src/app
    ng generate component welcome
    ng generate component home
  2. First, we will update the welcome component. In your code editor, go to the welcome folder, open welcome.component.html, and replace its contents with the code below:
    <div id="welcome" [ngClass]="style">
      <div class="welcome-display">
        <div>
          <img alt="App ID Logo" class="logo-icon" src="https://appid-management.eu-gb.bluemix.net/swagger-ui/images/app_id_icon.svg">
          <p>
            Welcome to the<br>
            IBM Cloud App ID SPA SDK<br>
            Sample App
          </p>
        </div>
        <button id="login" [ngClass]='buttonDisplay' (click)="onLoginClick()">Login</button>
        <div [ngClass]="errorStyle" id="error">{{errorMessage}}</div>
      </div>
    </div>
  3. Open the welcome.component.ts file and replace its contents with the code below. This file will contain the code necessary to add authentication and authorization to the application using the App ID SDK. Be sure to add your own SPA's client ID and discovery endpoint:
    import { Component, Output, EventEmitter, OnInit } from '@angular/core';
    import AppID from 'ibmcloud-appid-js';
    @Component({
      selector: 'app-welcome',
      templateUrl: './welcome.component.html',
      styleUrls: ['./welcome.component.css']
    })
    export class WelcomeComponent implements OnInit {
      style = 'show';
      buttonDisplay = 'show';
      errorStyle = 'hide';
      errorMessage = '';
      appid = new AppID();
      @Output() changeState = new EventEmitter();
      async ngOnInit() {
        try {
          await this.appid.init({
            clientId: '<CLIENT_ID>',
            discoveryEndpoint: '<WELL_KNOWN_ENDPOINT>'
          });
        } catch (e) {
          this.errorMessage = e.message;
          this.errorStyle = 'show';
        }
      }
      async onLoginClick() {
        try {
          this.buttonDisplay = 'hide';
          const tokens = await this.appid.signin();
          const decodeIDTokens = tokens.idTokenPayload;
          const userName = 'Hi ' + decodeIDTokens.name + ', Congratulations!';
          this.style = 'hide';
          this.changeState.emit({userName});
        } catch (e) {
          this.errorMessage = e.message;
          this.errorStyle = 'show';
          this.buttonDisplay = 'show';
        }
      }
    }
  4. Now, we will update the home component. In the home folder, open the home.component.html file and replace its contents with the code below:
    <div [ngClass]="displayState">
      <div class="welcome-display">
        <img alt="App ID Logo" class="logo-icon" src="https://appid-management.eu-gb.bluemix.net/swagger-ui/images/app_id_icon.svg">
        <p>{{userName}}</p>
        <p>You've made your first authentication.</p>
      </div>
     </div>
  5. Go to the home.component.ts file in the home component, clear all the code present, and add the code below:
    import { Component, Input} from '@angular/core';
    
    
    @Component({
      selector: 'app-home',
      templateUrl: './home.component.html',
      styleUrls: ['./home.component.css']
    })
    export class HomeComponent {
      @Input() displayState: string;
      @Input() userName: string;
    }
  6. Navigate to the app folder and replace the contents of app.component.html with the following code:
    <app-welcome (changeState)="onChangeState($event)"></app-welcome>
    <app-home [displayState]="displayStateStatus" [userName]="userNameStatus"></app-home>
  7. Go to the app.component.ts file, clear all the code present, and add the code below:
    import { Component } from '@angular/core';
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent {
      displayStateStatus = 'hide';
      userNameStatus = '';
      onChangeState(value) {
        this.displayStateStatus = 'show';
        this.userNameStatus = value.userName;
      }
    }
  8. Go to the src folder and add the following global styles to the styles.css file:
    body {margin: 0;padding: 0;max-width: 100%;overflow-x: hidden;}
    .hide {display: none;}
    .show {display: block;}
    .welcome-display {display: block;font-family: 'IBMPlexSans-Light', sans-serif;font-size: 1.5vw;text-align: center;margin: 10% auto 0;}
    .logo-icon {width: 70px;height: 70px;}
    #login {font-family: 'IBMPlexSans-Medium', sans-serif;font-size: 14px;color: #FFFFFF;text-align: center;background-color: #4178BE;border: none;cursor: pointer;width: 158px;height: 40px;margin: 20px auto 0;}
    #error {padding-top: 20px;font-size: 14px;color: red;}
  9. Save all the files and in your terminal, run the following command to view your app in the browser at  http://localhost:4200:
    ng serve
  10. Make sure you register your redirect_uri (in this case http://localhost:4200/*) with App ID to ensure that only authorized clients are allowed to participate in the authorization workflow. This can be done on the App ID dashboard under the Manage Authentication tab in the Authentication Settings. See "Adding redirect URIs" for more details.

Well done! You successfully integrated IBM Cloud App ID's SDK for SPA into an Angular application.

Tips and things to note

  • You can use the App ID client SDK to automatically obtain a new pair of tokens without requiring that the user explicitly sign in. For more information, see "Configuring silent login."
  • To learn more about security vulnerabilities with the implicit grant type and authorization code flow, visit OAuth 2.0 Security Best Current Practice.
  • For more information about how PKCE and the authorization code flow work together, see the spec.

Questions and feedback

  • If you have technical questions about App ID, post your question on Stack Overflow and tag your question with ibm-appid.
  • For questions about the service and getting started instructions, use the IBM Developer Answers forum. Include the appid tag.
  • Check out the App ID tutorials on our YouTube channel.
  • Open a support ticket in the IBM Cloud menu.
  • Reach out directly to the development team on Slack!

To get started with App ID, check it out in the IBM Cloud Catalog.

Be the first to hear about news, product updates, and innovation from IBM Cloud