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:
- Login/signup buttons if the user is NOT logged in
- Organization management and logout buttons if the user is logged in
- Display the user’s organizations and their role
- Display a message that’s the result of hitting an API which we’ll implement later on
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:
- Port - The port your app runs on locally. The default for React is 3000. After entering
3000, your test environment will only accept requests fromhttp://localhost:3000. - Login Redirect Path - After a user logs in, they will be redirected here. For example,
/will redirect them tohttp://localhost:3000/. - Logout Redirect Path - After a user logs out, they will be redirected here.
- Auth URL - This is where your authentication pages are hosted, and you will need this for the next step.
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:
- Create organizations
- Invite new users to their organizations
- Manage the roles of users within their organizations
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:
- List all the current user’s organizations, including their role
- Clicking an organization lets you invite new users and manage existing users
- A button which allows you to create new organizations
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.