Learn how to invoke a REST API deployed in WSO2 API Manager from a React based Single Page Application (SPA) with OAuth2 Implicit Grant type.

In this post, you’ll build a very simple web application with React. After that, you’ll deploy the sample Pizzashack API in WSO2 API Manager and invoke it from the React app using OAuth2 Implicit Grant Type.

So, roll your sleeves up and get ready to dive deeper into the code level ;)

TL;DR (for the impatient)

I must say that this post is going to be pretty long and full of code snippets. You can try out the code snippets while reading. But for those who need to skip the post and get into the source code, here’s the GitHub repo URL.

dunithd/react-pizzashack-client

Before you begin

I assume you have a basic knowledge of Javascript, REST APIs, React and OAuth2 fundamentals. If not, I recommend you to refer to below resources and come back.

What are we going to build?

We’ll end up building a simple React app to fetch and render a pizza menu from a REST API that is deployed inside WSO2 API Manager. Below screenshot shows how it would look like when we are done.

The final shape of our application.

Use case scenario

Let’s assume customers have already registered with PizzaShack and they are using that account credentials to access the pizza menu.

The main use case scenario and the flow of user interactions would be like below.

  1. When a typical user visits the client application we are going to build (http://localhost:3000), they’ll be presented with a link to login.

  2. Login link redirects the user to API Gateway, asking them to provide the user name and password.

  3. API Gateway authenticates the user on behalf of the client application.

  4. Upon successful authentication, API Gateway redirects the user back to the client application.

  5. Then the user may access the menu page by clicking on the Menu link (in the navigational bar) of the client application.

Above is a very high-level flow and it only focuses on the user interactions. But we’ll be digging into the detail of each step very soon and I’ll show you how to realize each step technically.

So let’s get into work…

API Gateway configurations

For this post, We are going to use WSO2 API Manager as our API management platform. It is an open source approach that addresses full API lifecycle management, monetization, and policy enforcement.

What really attracted me to that product is the flexibility it offers for the customizations and experimentation while maintaining the ease of use and manageability. Above all, it’s available free of charge. So head out to the downloads page and download any distribution/form-factor you are comfortable with.

Once you download distribution of your choice, I’d recommend you to go through the quick start guide to get it up and running.

From here onwards, let’s refer to WSO2 API Manager as APIM for short :)

Deploy PizzaShack API sample

APIM comes with a sample API implementation called PizzaShack. It mimics a fictitious pizza shop and provides several resources to browse the pizza menu and manage orders over REST. You don’t have to worry about deploying a backend since the sample comes with its own embedded backend service. PizzaShack sample is not deployed in the Publisher by default. So let’s go ahead and deploy it.

Start the APIM and login to the Publisher component using this URL pattern.

https://<APIM_HOST>:/publisher E.g If you are running APIM in your local laptop, the URL would be https://localhost:9443/publisher

The Publisher is the place where you author APIs. Here, as a developer, you can design APIs, impose policies on them, change their lifecycle state, create a new version, and view usage metrics/statistics. Once you are done, you will publish the APIs so that they’ll appear in the API Store and API Gateway for the consumption.

When the login screen is prompted, log in with the default admin credentials; use “admin” for both username and the password.

Publisher component of APIM

Instead of creating an API from the scratch, let’s simply deploy a sample API that is being shipped with the APIM distribution.

When you log in to Publisher for the first time, you’ll see a button called Deploy Sample API in the API list. Click on it and follow the on-screen instructions as follows.

Click on the highlighted button to deploy the sample

Create an OAuth application and obtain application keys

Once deployed, you’ll see PizzaShack API in the Developer Portal. It is the place where all the published APIs are listed so that API consumers (developers mostly) can search, try out and subscribe. Use the below URL pattern to access the Developer Portal

https://<APIM_HOST>:/store E.g If you are running APIM in your local laptop, the URL would be https://localhost:9443/store

PizzaShack sample deployed to Developer Portal

Now let’s create an Application to subscribe for this sample API.

  1. Sign In into the developer portal with default admin credentials (admin for both username and password).
  2. Click theApplications menu and click Add Application to create a new application.
  3. Enter the name as ReactApp and leave other settings as it is.

In the next screen, select Production Keys tab and enter http://localhost:3000/callback as the callback URL. Once you complete the entry, Implicit and Code grant type checkboxes will be activated. Go ahead and check both of them. Finally, click on Generate keys button to generate application keys for your application.

In the next screen, you’ll be presented with the application keys. At a later stage, you’ll be needing the consumer key to generate the token from the client app. Let’s explore that later.

OAuth client Id and client secret.

Finally, go to APIs, select PizzaShackAPI. Subscribe to it with the ReactApp application you just created.

Select ReactApp as the application upon subscription

Now you have deployed the sample API and created an OAuth application for your React app. Those are the only configurations required from the APIM side. So now its time look at the client app.

Implementing the client application

Single Page Applications and React

A Single Page Application (SPA) is a web application that loads a single HTML page initially and dynamically update that page as the user interacts with the application. A SPA works inside a browser and does not require page reloading during use. The apps you use on a daily basis including Gmail, Facebook, Google Docs and Google Maps are SPAs.

Reactis a Javascript UI library created by Facebook. Developers use React to build component-based modular SPAs and it’s very popular among frontend developers.

Scope of our client application

Our client application is a simple React based SPA. It’ll invoke the /menu resource of sample PizzaShack API deployed in the APIM and renders the menu items received in the response.

But we have a problem here. The PizzaShack API has been protected with OAuth2 by default. So any consumer/client application that wishes to access that API must obtain an OAuth2 access token from APIM prior to invoking the API.

So we’ll be adding OAuth2 based API authentication and authorization logic to the React app as well.

Creating the React app

With our sample PizzaShack API up and running, now the time has come to start developing the React app. Creating a React app used to be a bit messy process in the past. But fortunately, Facebook came up with a nifty tool called create-react-appto scaffold a React application from scratch, which will dramatically decrease the workload of the developer.

To get things started, open a terminal and move to a directory where you usually create projects. Then issue below command.

1
2
#create a directory for the project
npx create-react-app react-pizzashack-client

What happens here is NPM downloads and runs the create-react-app snippet in one go. It’ll take the provided argument react-pizzashack-client as the application name and creates a new directory inside the current working directory. This process might take a few seconds depending on your environment and the network connectivity.

Once the above command returns, move into the generated directory and issue below command to run the React app.

1
2
3
4
5
#move into the application directory
cd react-pizzashack-client

#run it. If you have Yarn installed in your system, tool will use it during the application generation process. If so, you can use 'yarn start'
npm start

npm start command will open a development server on port 3000 and listens for incoming connections. Also, it’ll open the application in a new browser tab. Or else, just navigate to http://localhost:3000 to see you freshly brewed app in action :)

The default screen you’ll see with create-react-app

The development server will take care of automatically deploying the changes you do to the code and they will be immediately reflected. Press Ctrl + C to shut down the development server.

Once the server is stopped, you can configure your favorite IDE to edit the React application. Mine, it’s always been the VS Code. In the same terminal, you can optionally issue below command to start a new VS Code project with the generated React app.

1
code .

Get rid of unwanted default files

The React app you just generated contains a few files that you won’t be needed in the future. So remove below files from your React project.

  • ./src/logo.svg
  • ./src/App.css
  • ./src/App.test.js

Then open up ./src/App.js and remove below lines. Otherwise, the project will not get compiled.

1
2
import logo from ‘./logo.svg’;
import ‘./App.css’;

Finally, locate the below line in the same file and remove that as well.

1
<img src={logo} className=”App-logo” alt=”logo” />

The whole point of removing unnecessary files at early stages is to keep the app simple and refactored so that you know what you are doing. That will help you to avoid unnecessary compilation failures and focus on the actual problem instead.

Add Bootstrap into React app

Now we have a basic React app with its default look and feel. But I need to walk up the extra mile and spice up the UI with some fine looking styles and widgets. So I’m gonna add the Bootstrap library to the project.

There are many ways to do this. You could use the react-bootstrap library if you intend to develop a fully pledged UI with user interaction, and animations, etc. But for the sake of simplicity, I’ll just add the Bootstrap CSS file hosted in their CDN. It is just a matter of adding the link to the CSS file in index.html.

To do that, open up the public/index.htmlfile and copy-paste the below stylesheet <link> into the <head> section.

1
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

Create React Components: Home and NavBar

Let’s quickly create two React components; Homeand NavBar. Purpose of the Home component is to act as the landing page and display a message to the user. NavBar component acts as the navigational bar for the application.

Create a directory called components inside /src directory to put all our components. Then create /src/components/Home.js file and insert below code into that:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import React from 'react';

const Home = () => {
    return (
        <main role="main" class="container">
            <div class="jumbotron">
                <h1>Welcome to PizzaShack!</h1>
                <p class="lead">We speak the good food language.</p>
            </div>
        </main>
    );
}

export default Home;

The Home component is a simple stateless functional component that simply displays a Bootstrap Jumbotron with a greeting message.

Next, create src/components/NavBar.js and insert the below code into that:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import React from 'react';

const NavBar = () => {
    return (
        <nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
            <a class="navbar-brand" href="#">PizzaShack</a>
            <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>

            <div class="collapse navbar-collapse" id="navbarsExampleDefault">
                <ul class="navbar-nav mr-auto">
                    <li class="nav-item active">
                        <a class="nav-link" href="/">Home</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="/menu">Menu</a>
                    </li>
                </ul>
                <button class="btn btn-outline-success my-2 my-sm-0" type="button">Sign In</button>
            </div>
        </nav>
    );
}

export default NavBar;

The NavBar component is a simple Bootstrap navigational bar which displays the name of the app, menu link, and Sign In/Sign Out links. We’ll be rendering the Menu link and Sign In/Out buttons conditionally, depending on the user’s status in a later section. But for now, let’s leave the navbar like this.

Finally, open /src/index.css file and add below entry inside body {} declaration.

1
margin-top: 100px;

Now we have two components in place. Let’s configure routes to them and ask React to render them.

Configure React Routes

Before getting our hands dirty with React Routes, let me breakdown the behavior of this React app for an ordinary user.

There will be two scenarios when a user navigates to the root context of the app. That means, simply http://localhost:3000

  • For anonymous users They should only see the home component.
  • For authenticated users Users who have been successfully authenticated should be able to see the home component and the menu component, which will render a grid of Pizza menu items (We’ll be creating this component at a later section in this post. So hang tight!)

That’s it! pretty simple, right?

The reason I chose to display the pizza menu only for logged in users because of the API resource that provides the menu data is protected with end user’s credentials. So our React app should obtain an access token on behalf of the logged in user. Hence the menu page is protected.

Let’s discuss that in detail very soon. Until then, allow me to install router dependencies and set up a route to render the anonymous view.

So, stop the development server by pressing Ctrl + C, if it is running already. Get back to the same terminal and type the below command.

1
npm i react-router react-router-dom

Above command will install two libraries; react-router which provides the navigational capabilities to the application while react-router-dom provides the required DOM bindings for the React Router implementation.

Now we have the required dependencies installed. So let’s start the routing configurations.

First, open the ./src/App.js file and replace its content with this.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import React, { Component } from 'react';
import NavBar from './components/NavBar';
import Home from './components/Home';
import {BrowserRouter, Route} from 'react-router-dom';

class App extends Component {
  render() {
    return (
      <BrowserRouter>
         <NavBar/>
         <Route exact path="/" component={Home}/>
      </BrowserRouter>
    );
  }
}

export default App;

In the above code, you just imported BrowserRouter from the react-router-dom library and wrapped all components with it. In order to enable routing within your application, this is all you have to do. You can refer this guide to get more familiar with React Routers and their various behaviors.

Finally, let’s add a route to the exact path “/”. So whenever a user lands on the applications root context (http://<host>:<port>/), this is the route that’s going to get triggered and it simply renders the Home component.

Regardless of any route, NavBar will always be rendered at the top of the page.

When you run the app with npm start, you’ll see the below screen which marks our progress up to this point.

Securing your React app

Up to now, your app is functional with a sleek navigational bar, a home page and a menu page that displays just two menu items. React routes are working as expected and they’ll land your users into correct spots. Now the time has come to take things to the next level and add some security features to your React app to perform the following.

  • Implement the Sign In functionality for your end users.
  • Implement the Sign Out functionality.
  • Invoke the real PizzaShack API with an access token and render the results in the menu page.

But before we start the implementations, let’s have a closer look at how PizzaShack API has been protected inside WSO2 API Manager.

A brief primer on Implicit Grant type

Usually, Single Page Applications use OAuth2 Implicit grant type to get an access token without an intermediate code exchange step. It’s pretty much similar to the Authorization code grant type but there’s no server-side component involved. The Implicit Grant starts out by building a link and directing the user’s browser to that URL. At a high level, the flow has the following steps:

  • The application opens a browser to send the user to the OAuth server
  • The user sees the authorization prompt and approves the app’s request
  • The user is redirected back to the application with an access token in the URL fragment

Implicit grant flow can be visualized like below.

Implicit grant flow

In the above diagram, Client will be your React app and the authorization server is WSO2 APIM.

How PizzaShack API is secured?

All APIs deployed in WSO2 APIM is protected with OAuth2 security protocol by default. For PizzaShack API, it follows the same rule and we need to obtain an access token under Implicit grant type before invoking it.

Our plan to obtain an access token

Our plan is pretty simple and has 2 steps.

  1. Add a click handler to Sign In button in NavBar component so that it’ll redirect the user to the /authorize endpoint of APIM and will be presented with a login form.
  2. Upon successful authentication, APIM redirects to the callback URL we have provided with the access token in the URL fragment.
  3. We need to extract the access token from the URL hash, validate it and save it to the localStorageto be used later.

Now let’s start this plan by creating the Config.js file inside /src directory. This file holds all necessary OAuth configurations related to your React app.

https://localhost:8243/authorize/'http://localhost:3000/callback'

Note: Make sure to replace clientId with the consumer key, you received for your client application (ReactApp) in WSO2 APIM.

Here, the authUrl parameter is the endpoint where WSO2 APIM provides the login page. callbackUrl is the URL we have provided when registering our client application with WSO2 APIM. Upon successful user authentication, APIM redirects the user to this URL.

Create Auth.js to keep all authentication related logic

Then let’s create a new class called Authto put all authentication and authorization logic so that you can re-use it from other components.

Create Auth.js file inside /src/components directory and insert the following code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import { AUTH_CONFIG } from '../Config';

class Auth {
    accessToken;
    expiresAt;

    constructor() {
        this.login = this.login.bind(this);
    }

    login() {
        var url = AUTH_CONFIG.authUrl + "?response_type=id_token token&client_id=" 
        + AUTH_CONFIG.clientId + "&scope=openid&nonce=13e2312637dg136e1&";
        var redirectUrl = "redirect_uri=" + AUTH_CONFIG.callbackUrl;
        url = url + redirectUrl;
        window.location.href = url;
    }

}

export default Auth;

As of now, this class has only two instance methods; login() and the constructor(). login() method initiates the authentication workflow and directs your users to the login page provided by WSO2 APIM. It reads configuration parameters from Config.js we just created and builds up the redirect URL.

In addition to that, we have two properties, accessToken and expiresAt which is keep track of the access token and the expiry time of the token. We’ll be using these properties in a later section of this post.

Enable users to sign in with NavBar

Now let’s update our NavBar component to attach a click handler to Sign In button so that users will be redirected to the login page upon a button click event.

Replace the content of NavBar.js with below code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import React from 'react';
import {Link, withRouter} from 'react-router-dom';

const NavBar = (props) => {

    function login() {
        props.auth.login();
    }

    return (
        <nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
            <a class="navbar-brand" href="#">PizzaShack</a>
            <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>

            <div class="collapse navbar-collapse" id="navbarsExampleDefault">
                <ul class="navbar-nav mr-auto">
                    <li class="nav-item active">
                        <Link className="nav-link" to="/">Home</Link>
                    </li>
                    <li class="nav-item">
                        <Link className="nav-link" to="/menu">Menu</Link>
                    </li>
                </ul>
                <button class="btn btn-outline-success my-2 my-sm-0" type="button" onClick={login.bind(this)}>Sign In</button>
            </div>
        </nav>
    );
}

export default withRouter(NavBar);

In the above file, the Sign In NavBarcomponent receives the Authobject passed from the Router, via props. In order to tell the router to pass that object for us, we’ll have to wrap our component with withRouter higher order component.

Also, you’ll see a click handler called login() is attached to the Sign In button which in turn calls the login() method of the Authobject passed via the props.

Now if you run the application, you’ll see the below screen when you click on the Sign In button. That is the default login form provided by WSO2 APIM.

For the moment, provide adminfor both username and password. Once you have users registered in APIM, you can use their credentials. Then you’ll be presented with the default Open ID consent page that asks for your permission to grant access to your personal attributes to the React app. Select Approve Always and Select All as per the screenshot.

When logging in for the first time, APIM asks for the OpenID consent

After successful authentication, you’ll be redirected back to the React app. But you won’t notice anything special as of now. So let’s create a new component to extract the access token issued from APIM.

Handling the response received from WSO2 APIM

Okay, now we have APIM redirecting to the callback URL of your React app, with an access token attached to the URL hash. So let’s create a component to catch that URL hash, parse it and store in the localStorage.But before that, we need to install a third-party dependency to our project.

Open up a terminal and navigate back to you React project’s root directory. Run below command to install query-string npm package which will be helpful with parsing the URL hash coming from the APIM.

npm i query-string

Then add below method into Auth.js:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
handleAuthentication() {
    const authResult = queryString.parse(window.location.hash);
    console.log(authResult);
    if(authResult && authResult.access_token) {
        // Set isLoggedIn flag in localStorage
        localStorage.setItem('isLoggedIn', 'true');

        // Set the time that the access token will expire at
        let expiresAt = (authResult.expires_in * 1000) + new Date().getTime();
        this.accessToken = authResult.access_token;
        this.expiresAt = expiresAt;

        localStorage.setItem('accessToken', this.accessToken);
    } else {
        console.log("An error occurred while authentication.");
        alert(`Error: Check the console for further details.`);
    }
}

This handleAuthentication() method parses the URL hash fragment received from WSO2 APIM. It uses the query-string package for that and assigns the parsed response into a JSON object. If the response is valid and has an access token in it, we populate variables and set isLoggedIn flag in localStorage. Also, we save the received access token to localStorage as well.

Moreover, let’s add a few more methods to Auth.js; logout() , getAccessToken(), and isAuthenticated() which we’ll be using very soon.

Now your Auth.js should look like below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import queryString from 'query-string';
import { AUTH_CONFIG } from '../Config';

export default class Auth {
    accessToken;
    expiresAt;

    constructor() {
        this.isAuthenticated = this.isAuthenticated.bind(this);
        this.login = this.login.bind(this);
        this.handleAuthentication = this.handleAuthentication.bind(this);
        this.logout = this.logout.bind(this);
    }

    login() {
        var url = AUTH_CONFIG.authUrl + "?response_type=id_token token&client_id=" + AUTH_CONFIG.clientId + "&scope=openid&nonce=13e2312637dg136e1&";
        var redirectUrl = "redirect_uri=" + AUTH_CONFIG.callbackUrl;
        url = url + redirectUrl;
        window.location.href = url;
    }

    handleAuthentication() {
        const authResult = queryString.parse(window.location.hash);
        console.log(authResult);
        if(authResult && authResult.access_token) {
            // Set isLoggedIn flag in localStorage
            localStorage.setItem('isLoggedIn', 'true');

            // Set the time that the access token will expire at
            let expiresAt = (authResult.expires_in * 1000) + new Date().getTime();
            this.accessToken = authResult.access_token;
            this.expiresAt = expiresAt;
            localStorage.setItem('accessToken', this.accessToken);
        } else {
            console.log("An error occurred while authentication.");
            alert(`Error: Check the console for further details.`);
        }
    }

    logout() {
        // Remove tokens and expiry time
        this.accessToken = null;
        this.expiresAt = 0;
    
        // Remove isLoggedIn flag and other token flags from localStorage
        localStorage.removeItem('isLoggedIn');
        localStorage.removeItem('accessToken');
    }

    isAuthenticated() {
        // Check whether the current time is past the
        // access token's expiry time
        let expiresAt = this.expiresAt;
        return new Date().getTime() < expiresAt;
    }
    
    getAccessToken() {
        return localStorage.getItem("accessToken");
    }

}

Create the Callback component

Let’s create a new component to handle the redirection route from APIM.

Create a file named Callback.js inside /src/components directory and insert the below code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import React, { Component } from 'react';
import {withRouter} from 'react-router-dom';

class Callback extends Component {

  constructor(props) {
    super(props);
  }

  componentDidMount() {
    this.props.auth.handleAuthentication();
    this.props.history.replace('/');
  }

  render() {
    return (
      <p>Loading...</p>
    );
  }

}

export default withRouter(Callback);

Inside componentDidMount() method, we are invoking the handleAuthentication() of Authclass passed via props. If the authentication succeeds, we redirect the user to the root context (http://localhost:3000) of our React app using the history object. Since we need to get access to Authobject via props and need to use historyobject for navigation, we wrap this with withRouter.

Then go to App.js and add this import:

import Callback from ‘./components/Callback’;

Also, add this route as well

<Route exact path=’/callback’ render={(props) => <Callback {…props} auth={auth} />}/>

You can see the way we are passing the initialized Authobject into the Callbackcomponent in this route definition.

Adding auth checks and sign out functionality

Now our app can sign in users and extract the access tokens coming from APIM. Now let’s try to use that token and logged-in state to do some access control within the app.

First, let’s change the NavBar code to render the Sign In button if the user is not logged in. Otherwise, let’s render a ‘Sign Out’ button.

Replace the content of NavBar.js with below code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import React from 'react';
import {Link, withRouter} from 'react-router-dom';

const NavBar = (props) => {

    function login() {
        props.auth.login();
    }
    
    function logout() {
        props.auth.logout();
        props.history.replace('/');
    }

    const { isAuthenticated } = props.auth;

    return (
        <nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
            <a class="navbar-brand" href="#">PizzaShack</a>
            <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>

            <div class="collapse navbar-collapse" id="navbarsExampleDefault">
                <ul class="navbar-nav mr-auto">
                    <li class="nav-item active">
                        <Link className="nav-link" to="/">Home</Link>
                    </li>
                    <li class="nav-item">
                        <Link className="nav-link" to="/menu">Menu</Link>
                    </li>
                </ul>
                {
                    !isAuthenticated() ?
                    ( <button class="btn btn-outline-success my-2 my-sm-0" type="button" onClick={login.bind(this)}>Sign In</button>)
                    :(<button class="btn btn-outline-success my-2 my-sm-0" type="button" onClick={logout.bind(this)}>Sign Out</button>)
                }
            </div>
        </nav>
    );
}

export default withRouter(NavBar);

This is the final version of our NavBar component and it has a new method; logout() and I have put some logic to check for the authentication status of the user.

Here, I’m calling isAuthenticated() method of the Authobject passed in. Inside that method, the user’s authentication status is being checked.

1
2
3
4
5
6
isAuthenticated() {

let expiresAt = this.expiresAt;
return new Date().getTime() < expiresAt;

}

Above code in Auth.js checks the received access token’s expiry time against the current time.

Depending on the value returned by isAuthenticated(), NavBar shows Sign In or Sign Out buttons accordingly as follows.

1
2
3
4
5
6
7
8
9
{

!isAuthenticated() ?

( <button class="btn btn-outline-success my-2 my-sm-0" type="button" onClick={login.bind(this)}>Sign In</button>)

:(<button class="btn btn-outline-success my-2 my-sm-0" type="button" onClick={logout.bind(this)}>Sign Out</button>)

}

Implementing the logout functionality

In the case of the Sign Out button, I’ve defined logout() which in turn calls logout() method of the Authobject and redirects the user back to the home page of the app.

Now save the NavBar.js and refresh the app. After you log in, you’ll see the Sign Out button in the navbar, which will allow you to log out from the application.

Up to now, we have lifted the heaviest parts of the app, which is about getting your users authenticated. I’ll assure the rest will be a smooth ride and you reap the benefits from the things you have implemented in this phase:)

Invoking the PizzaShack API from your React App

At last, we have reached the final part of this post. I assume it’s been a long drive from up there. But have no fear, we’ll be done very soon :)

Our plan is to create a new component called Menuto render a list of menu items. We should write code inside this component to call the PizzaShack API in APIM with the access token, fetch the response and render the list in a nice format.

NavBar.js already has a link to access this component and it’ll only visible to logged in users. But we don’t have a Route for that yet. So let’s begin by creating a route for the Menu component.

Go to App.js and add below import:

1
import Menu from './components/Menu';

Then add this route as well.

1
<Route exact path=’/menu’ render={(props) => <Menu {…props} auth={auth} />}/>

Above route tells React to route all requests coming from /menu path to Menu component.

Install axios library

Inside the Menu component, we’ll have to make an HTTP GET call to APIM and fetch data. In order to do that, we need the axioslibrary, which is a promise-based HTTP client for the browser.

Stop the development server and issue the below command to install axioslibrary.

1
npm i axios

Fetching and rendering data

Create /src/components/Menu.js file and put below code into that:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import React, {Component} from 'react';
import axios from 'axios';

const API_URL = 'http://localhost:8280/pizzashack/1.0.0';

class Menu extends Component {

    constructor(props) {
        super(props);
        this.state = {
            menuItems : []
        };
    }

    componentDidMount () {
        const { getAccessToken } = this.props.auth;
        const headers = { 'Authorization': `Bearer ${getAccessToken()}`}
        axios.get(`${API_URL}/menu`, { headers })
            .then(response => this.setState({ menuItems: response.data }))
            .catch(error => this.setState({ message: error.message }));
    }

    render() {
        return (
            <div className="container">
                <div className="card-columns">
                        {this.state.menuItems === null && <p>Loading menu...</p>}
                        {
                            this.state.menuItems && this.state.menuItems.map(item => (
                                <div key={item.name} class="card">
                                    <div class="card-header">{item.name}</div>
                                    <div class="card-body">
                                        <p class="card-text">{item.description}</p>
                                        <a href="#" class="btn btn-primary">More...</a>
                                    </div>
                                </div>
                            ))
                        }
                </div>
            </div>
        );
    }
}

export default Menu;

Make sure to update the value of API_URL to match with your APIM hostname.

This Menu component is a stateful component and it holds a list of menu items as the state. Initially, the state will be an empty array. But once the component reaches the componentDidMount() lifecycle method, it’ll fetch data from APIM with the access token received with the help of axios client.

Upon the arrival of response data, it’ll be set as the internal state by calling this.setState(). When render() is called, it simply iterates through the internal state (menu items array) and renders a pretty Bootstrap card columns structure.

And finally (yey!!!) we have our Pizza menu rendered like below.

Closing remarks

I humbly believe you have learned something from this post. I admit that it’s quite long. But I tried my best to touch upon even the smallest steps hoping that they’ll add some value to you.

Also, please keep in mind that this is just a simple reference application I created only to demonstrate how React apps can collaborate with WSO2 API Manager. This might not be perfect for production deployment. But it’ll give you sufficient guidelines to start off.

You may use the source of this app as a boilerplate to start your own adventure. Also, you can send pull requests with improvements. I’d be more than happy to merge them :)

Also, if you need any help or have a better way to do things, please don’t hesitate to leave a comment.

Will meet you with another post soon. Cheers!


Written on April 25, 2019 by Dunith Dhanushka.

Originally published on Medium