Configuring a single-page application (SPA) to work with App ID.

New to IBM Cloud App ID? Welcome!

With App ID, you can easily add authentication, authorization, and user profile services to web apps, mobile apps, and APIs with minimal to zero code changes, without redeploying your app. By using App ID, you no longer have to worry about setting up infrastructure, ensuring geo-availability, or trying to understand complex compliance regulations when it comes to managing user identity. 

With the SDKs, you can get a sign-in flow going in minutes with Facebook, SAML 2.0, and more. You can also choose to use App ID’s scalable user registry (called Cloud Directory) to let your users manage their own account. Additionally, the App ID user profiles feature can be used to store information about your users—such as their preferences—to make their experience of your app personalized and, most importantly, secure.

In this blog, we’re going to walk you through configuring a single-page application (SPA) to work with App ID. With the client-side JavaScript SDK, you can easily secure your Angular, React, or other frontend framework. If your application has a backend that you manage or needs to be refreshed to load content, try the web app flow instead.

Before getting started, we should 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.

Understanding the flow

The figure below shows how to securely authenticate and authorize single-page application (SPA) users with the Authorization Code + PKCE flow.

  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.

We’re going to walk you through the steps that you can take to use the App ID JavaScript SDK to secure a single-page application. The sample app that we’re using is based on React, which is a JavaScript library that you can utilize to build user interfaces by using components to efficiently update and render a UI.

Prerequisites

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

Creating your app

  1. Set up a frontend build pipelines using create-react-app. Then, move into the project directory:
    npx create-react-app my-app
    cd my-app
  2. Install the IBM Cloud App ID SDK:
    npm install ibmcloud-appid-js
  3. In your code editor in the src directory of the application, open the App.js file. Import App ID by adding the following code:
    import AppID from 'ibmcloud-appid-js';
  4. In the main App() function, declare a new App ID instance with useMemo, which recomputes a memorized value when a dependency changes:
    const appID = React.useMemo(() => {
        return new AppID()
      }, []);
  5. Initialize App ID and add error handling. Add your client ID and discovery endpoint, which can be found in the Applications tab of the App ID dashboard:
    const [errorState, setErrorState] = React.useState(false);
    const [errorMessage, setErrorMessage] = React.useState('');
    (async () => {
        try {
          await appID.init({
            clientId: '<SPA_CLIENT_ID>',
            discoveryEndpoint: '<WELL_KNOWN_ENDPOINT>'
          });
        } catch (e) {
          setErrorState(true);
          setErrorMessage(e.message);
        }
      })();
  6. Create a login action that will execute when the login button is clicked. After a successful authentication, the welcomeDisplayState will be set to true and the userName will be set to the name returned in the App ID token:
    const [welcomeDisplayState, setWelcomeDisplayState] = React.useState(false);
    const [loginButtonDisplayState, setLoginButtonDisplayState] = React.useState(true);
    const [userName, setUserName] = React.useState('');
    
    
    const loginAction = async () => {
      try {
        const tokens = await appID.signin();
        setErrorState(false);
        setLoginButtonDisplayState(false);
        setWelcomeDisplayState(true);
        setUserName(tokens.idTokenPayload.name);
      } catch (e) {
        setErrorState(true);
        setErrorMessage(e.message);
      }
    };
  7. Add a welcome div, the login button that calls the login action, and an error div:
    {welcomeDisplayState && <div> Welcome {userName}! You are now authenticated.</div>}
    {loginButtonDisplayState && 
       <button style={{fontSize: '24px', backgroundColor: 'skyblue', border: 'none', cursor: 'pointer'}} id='login' onClick={loginAction}>Login</button>}
    {errorState && <div style={{color: 'red'}}>{errorMessage}</div>}
  8. Save all of the files. Your entire App.js file should look like this:
    import React from 'react';
    import logo from './logo.svg';
    import './App.css';
    import AppID from 'ibmcloud-appid-js';
    function App() {
      const appID = React.useMemo(() => {
        return new AppID()
      }, []);
      const [errorState, setErrorState] = React.useState(false);
      const [errorMessage, setErrorMessage] = React.useState('');
      (async () => {
        try {
          await appID.init({
            clientId: '<SPA_CLIENT_ID>',
            discoveryEndpoint: '<WELL_KNOWN_ENDPOINT>'
          });
        } catch (e) {
          setErrorState(true);
          setErrorMessage(e.message);
        }
      })();
      const [welcomeDisplayState, setWelcomeDisplayState] = React.useState(false);
      const [loginButtonDisplayState, setLoginButtonDisplayState] = React.useState(true);
      const [userName, setUserName] = React.useState('');
      const loginAction = async () => {
        try {
          const tokens = await appID.signin();
          setErrorState(false);
          setLoginButtonDisplayState(false);
          setWelcomeDisplayState(true);
          setUserName(tokens.idTokenPayload.name);
        } catch (e) {
          setErrorState(true);
          setErrorMessage(e.message);
        }
      };
      return (
        <div className='App'>
        <header className='App-header'>
          <img src={logo} className='App-logo' alt='logo' />
            {welcomeDisplayState && <div> Welcome {userName}! You are now authenticated.</div>}
            {loginButtonDisplayState && <button style={{fontSize: '24px', backgroundColor: 'skyblue', 
              border: 'none', cursor: 'pointer'}} id='login' onClick={loginAction}>Login</button>}
            {errorState && <div style={{color: 'red'}}>{errorMessage}</div>}
        </header>
        </div>
      );
    }
    export default App;
  9. Open your terminal. Run the following command to access your app from http://localhost:3000:
    npm start
  10. Make sure you register your redirect_uri (in this case http://localhost:3000/*) 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 the docs for more details.

Well done! You successfully integrated IBM Cloud App ID’s SDK for SPA into a React application.

Note: You can view the code repository for our React sample app here.

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 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.
  • 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.

Was this article helpful?
YesNo

More from Cloud

New 4th Gen Intel Xeon profiles and dynamic network bandwidth shake up the IBM Cloud Bare Metal Servers for VPC portfolio

3 min read - We’re pleased to announce that 4th Gen Intel® Xeon® processors on IBM Cloud Bare Metal Servers for VPC are available on IBM Cloud. Our customers can now provision Intel’s newest microarchitecture inside their own virtual private cloud and gain access to a host of performance enhancements, including more core-to-memory ratios (21 new server profiles/) and dynamic network bandwidth exclusive to IBM Cloud VPC. For anyone keeping track, that’s 3x as many provisioning options than our current 2nd Gen Intel Xeon…

IBM and AWS: Driving the next-gen SAP transformation  

5 min read - SAP is the epicenter of business operations for companies around the world. In fact, 77% of the world’s transactional revenue touches an SAP system, and 92% of the Forbes Global 2000 companies use SAP, according to Frost & Sullivan.   Global challenges related to profitability, supply chains and sustainability are creating economic uncertainty for many companies. Modernizing SAP systems and embracing cloud environments like AWS can provide these companies with a real-time view of their business operations, fueling growth and increasing…

Experience unmatched data resilience with IBM Storage Defender and IBM Storage FlashSystem

3 min read - IBM Storage Defender is a purpose-built end-to-end data resilience solution designed to help businesses rapidly restart essential operations in the event of a cyberattack or other unforeseen events. It simplifies and orchestrates business recovery processes by providing a comprehensive view of data resilience and recoverability across primary and  auxiliary storage in a single interface. IBM Storage Defender deploys AI-powered sensors to quickly detect threats and anomalies. Signals from all available sensors are aggregated by IBM Storage Defender, whether they come…

IBM Newsletters

Get our newsletters and topic updates that deliver the latest thought leadership and insights on emerging trends.
Subscribe now More newsletters