Building a secure B2B React/Express app with PropelAuth | Blog | PropelAuth

In this guide, we’ll build a simple B2B application with authentication. B2B applications allow your end-users to create organizations, manage roles within the organization, and invite users to the organization. This is also commonly called multi-tenant authentication.

The application will allow users to sign up/login, create/manage organizations, and logged-in users will see their organizations.

Hosted auth with PropelAuth

PropelAuth is a hosted authentication service focused on a great developer experience.

Follow the steps here to set up your project and make sure to select B2B as each user is not a member of an organization. For our hosted pages, we’ll use the primary color rgb(109, 73, 71) and this logo:

Setting up our React frontend

After setting up our project, our users can log in, but there’s nothing to do once they log in. So let’s continue by creating a UI. This UI will need a few components:

Creating our React app

To create a new React application, we will use create-react-app, following the official instructions here.

$ npx create-react-app frontend
$ cd frontend
$ yarn start  # or npm start

Configuring PropelAuth

Go to your PropelAuth project and click Frontend Integration in the sidebar. You will see:

Click Save, and PropelAuth will now accept requests from your frontend. If you log in, you will now be redirected to http://localhost:3000/.

How does authentication work?

How will our frontend and backend know if the current user is logged in? PropelAuth’s authentication is token-based. A token is a string that uniquely identifies a user, and we refer to these tokens as access tokens (since they give your users access to your APIs).

Your frontend will request a token from PropelAuth on behalf of a user.

PropelAuth returns access tokens only for valid logged-in users. Later on, when your frontend makes requests to your backend, it will include an access token, which your backend can validate and determine whose token it is.

This complexity is hidden in PropelAuth’s React library (including annoying things like storing the token, periodically refreshing the token, etc.).

Adding authentication

We’ll need to install the library first:

$ yarn add @propelauth/react
# or npm install --save @propelauth/react

AuthProvider manages our user’s authentication information, so it’s best to put it at the top level of our application, so it never unmounts.

import {AuthProvider} from '@propelauth/react';

ReactDOM.render(
    <AuthProvider authUrl="https://REPLACE_ME.propelauthtest.com">
        <App/>
    </AuthProvider>,
    document.getElementById('root')
);

The authUrl is the value we saw on the Frontend Integration page earlier.

App and any child components may now access authentication information. To demonstrate this, let’s set up our signup, login, and logout buttons based on whether the user is logged in.

import {withAuthInfo, useLogoutFunction, useRedirectFunctions} from '@propelauth/react';

function AuthenticationButtons(props) {
    const logoutFn = useLogoutFunction()
    const {redirectToSignupPage, redirectToLoginPage} = useRedirectFunctions()

if (props.isLoggedIn) {
        return <button onClick={logoutFn}>Logout</button>
    } else {
        return <div>
            <button onClick={redirectToSignupPage}>Signup</button>
            <button onClick={redirectToLoginPage}>Login</button>
        </div>
    }
}

export default withAuthInfo(AuthenticationButtons, {
    displayWhileLoading: <div>Loading...</div>
})

Managing organizations

Since we are making a B2B application, we want our end users to:

All of that is actually already done for you on your hosted pages. Sometimes, we also want to display organization information within our application. We’ll build a quick proof of concept to show what you can do.

This proof of concept includes:

import {useRedirectFunctions, withAuthInfo} from "@propelauth/react";

function OrganizationView(props) {
  const {redirectToCreateOrgPage} = useRedirectFunctions()

if (!props.isLoggedIn) {
    return <div>Login to view organizations</div>
  }

const orgs = props.orgHelper.getOrgs();
  return <div>Your Orgs:
    <ul>{orgs.map(org =>
            <li key={org.orgId}>
              <Org org={org} />
            </li>
    )}</ul>
    <button onClick={redirectToCreateOrgPage}>Create Org</button>
  </div>
}

function Org({org}) {
  const {redirectToOrgPage} = useRedirectFunctions()

return <a href="#" onClick={() => redirectToOrgPage(org.orgId)}>
    {JSON.stringify(org)}
  </a>
}

export default withAuthInfo(OrganizationView);

Making authenticated requests

There are a lot of ways to make HTTP requests in Javascript. You could use the Fetch API, XMLHttpRequest, or a library like axios.

Whichever you choose, to make an authenticated request on behalf of your user, you’ll need to provide an access token. Just like isLoggedIn, the access token is available from withAuthInfo. You provide it in the request in the Authorization header, like so:

Authorization: Bearer YOUR_ACCESS_TOKEN

With the Fetch API, this looks like:

function whoAmI(accessToken) {
    return fetch("/whoami", {
        method: "GET",
        headers: {
            "Authorization": `Bearer ${accessToken}`,
        }
    })
}

Setting up our Express backend

Our frontend is expecting to hit an endpoint /api/whoami and display the response to the user.

Creating an unprotected route

First we create our express project

$ mkdir backend # create a new project
$ cd backend
$ yarn init     # initialize the project
$ yarn add express

Then, we’ll create an unprotected endpoint.

const express = require('express')
const app = express()
const port = 3001

app.get('/api/whoami', (req, res) => {
    res.json({'test': 'test'});
})

app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`)
})

Run this with node index.js and test it by refreshing our React app.

Protecting our API

At this point, our frontend is passing in an access token, but our server isn’t doing anything with it yet. We’ll use PropelAuth’s Express library@propelauth/express to get the user.

$ yarn add @propelauth/express
const {requireUser} = require("./propelauth");

app.get('/api/whoami', requireUser, (req, res) => {
    res.json({'user': req.user});
})

Summary

Success! Our frontend made a request to our backend, and our backend was able to identify the user that made the request.

If you have any questions, please reach out at support@propelauth.com.