React/Python Serverless B2B Starter App with Chalice
Tags: react, python, serverless, tutorial
Published: 2023-03-19 15:47:00

In this guide, we’ll build an example B2B application in React where users can sign up, login, manage their accounts, and view organization and member information, PropelAuth, React, Python, and Chalice.

![Demo of final app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9waj3mn20jr6ro7hxxzw.gif)

We're going to use the following technologies for this blog post:

* [**React.js**](https://reactjs.org/) - for our frontend
    
* [**Python**](https://www.python.org/) and [**Chalice**](https://aws.github.io/chalice/) - for our backend
    
* [**PropelAuth**](https://www.propelauth.com/) - for user login and management
    
The code is available on GitHub in these [**Github**](https://github.com/PropelAuth/react-frontend-starter) [**Repos**](https://github.com/PropelAuth/python-chalice-backend-starter)

### **Setting Up Authentication Pages**

Before creating our application, we'll first be setting up our project in PropelAuth. Projects in PropelAuth provide all of the necessary components of authentication for your applications, including hosted login pages, which we'll be using for this walkthrough. You can also set up additional features such as SAML, social logins/SSO, and more. For more information on how to add these options, be sure to check out our [**documentation**](https://docs.propelauth.com/overview).

The first step is to create a project in PropelAuth.

![Create project](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7s4fndq798zn7uq2dm8j.png)

Once a project has been created, you will be directed to the dashboard for your project, outlining the steps to complete adding authentication into your application.

![Dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gjbpt9a4ix8v9dpff8kj.png)

The first step is to customize the look of your hosted authentication pages. By clicking "View," you are redirected to this page:

![Editing hosted pages](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o5wrj4bnujy0k3w6nlwz.png)

Here you can adjust the look of all hosted authentication pages to best match your preferred branding and style guidelines. Feel free to make any changes, click "Save," and navigate back to the dashboard using the left sidebar. Now that we've changed the look of our pages, click "Mark As Done," and move on to the next step.

From here, you can [**configure other aspects of your end-users auth experience**](https://docs.propelauth.com/getting-started/), including:

* Adding "Login in with Google" or other SSO providers
    
* Collecting additional metadata on signup - like username or first name/last name
    
* Allowing your users to upload their own profile picture
    
* Letting your end-users create organizations and invite their coworkers (called B2B support)
    

For now, we are going to click "Mark As Done" on step 2, "Add Social Logins," and move on to step 3.

### **Sign Up As A Test User**

Under step 3, click "View," and a new tab should open with the authentication page you configured above. Sign up as if you are a user, exit the tab, and mark the step as done.

![Your users login page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bpcdhfvxorpmutja3ckl.png)

### **Create a React Project**

If you don't have a project already, you can create one with:

```bash
$ npx create-react-app frontend
```

Next, install the [**@propelauth/react**](https://www.npmjs.com/package/@propelauth/react) package. It provides an easy interface to access your users information. It will manage auth tokens for you and has nice features like refreshing auth information when the user reconnects to the internet or switches back to your tab.

```bash
$ npm install @propelauth/react
```

Finally, we’ll install react-router to use switching between pages in our application.

```bash
$ npm install react-router-dom@6
```

## **Frontend**

In this section, we will be setting up the frontend sections of our React application, walking through how to integrate PropelAuth into your components as we go.

### **Set Up Auth Provider**

First, navigate to the index.js file in your application. Here we are going to add our [**AuthProvider**](https://docs.propelauth.com/reference/frontend-apis/react#set-up-authprovider) and **BrowserRouter.**

```jsx
import { AuthProvider } from '@propelauth/react';
import {BrowserRouter} from "react-router-dom";

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <AuthProvider authUrl={process.env.REACT_APP_PROPELAUTH_AUTH_URL}>
	    <BrowserRouter>
	      <App/>
	    </BrowserRouter>
    </AuthProvider>
  </React.StrictMode>
);
```

The authUrl is available on the **Frontend Integration** section of your PropelAuth project, or step 4 in your dashboard.

**BrowserRouter** will manage our URL history while switching pages in our application. **AuthProvider** is the provider of a [**React context**](https://reactjs.org/docs/context.html) that manages the current user’s [**access token**](https://docs.propelauth.com/overview/access-tokens) and metadata, and all other components pull information from it. We are placing it at the top level of our application so that it never unmounts.

### **Get Logged In Status**

Next, in the **App.js**, we'll bring in the [**withAuthInfo**](https://docs.propelauth.com/getting-started/frontend-integration/nextjs#withauthinfo) function, which injects a user's information into a React component. In our project, we will be displaying different components depending on whether or not the user is logged in.

```jsx
import { withAuthInfo } from '@propelauth/react';

const App = withAuthInfo(({isLoggedIn}) => {
  if (isLoggedIn) {
      return <div>The User is logged in</div>
  } else {
      return <div>The User is logged out</div>
  }
})

export default App;
```

Using the code above, if the user is logged in they should see the following:

![User is logged in](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/850w6b9qmbi34y823bpp.png)

### **Create Sign In/SignOut Buttons**

[**@propelauth/react**](https://docs.propelauth.com/reference/frontend-apis/react.html) also provides React hooks for redirecting your users to the hosted login/signup/account pages you created in your PropelAuth project, or logging your users out. Let's add them to our App.js file.

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

const App = withAuthInfo(({isLoggedIn}) => {
  const logoutFn = useLogoutFunction()
  const {redirectToSignupPage, redirectToLoginPage} = useRedirectFunctions();
    
  if (isLoggedIn) {
      return <div>
          <p>The User is logged in</p>
          <button onClick={() => logoutFn()}>
              Click here to log out
          </button>
      </div>
  } else {
      return <div>
          To get started, please log in as test user.
          <br/>
          <button onClick={() => redirectToSignupPage()}>
              Sign up
          </button>
          <button onClick={() => redirectToLoginPage()}>
              Log in
          </button>
      </div>
  }
})
```

Now, if a user is logged out, they will see the following:

![Logged out](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/la2s3jevwqeqsoifxqdn.png)

And if they are logged in, they will see:

![Logged in](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s160a3a0n9foxadlou9a.png)

### **Display User Information**

The next step is to build out our routes, and create a route and component that will display the currently logged in user’s information. First, import **Route** and **Routes** from **react-router-dom** into **App.js.** Next, create **components/Home.jsx** and **components/UserInfo.jsx**, which will serve as our home page component and our user information route, respectively.

In **Home.jsx**, we’ll import [**withRequiredAuthInfo**](https://docs.propelauth.com/reference/frontend-apis/nextjs#withrequiredauthinfo), which is identical to **withAuthInfo** but the component will not be rendered if the user is not logged in, and instead will default to redirecting to the hosted sign up page unless otherwise specified. We’ll also import **Link** from **react-router-dom.** Finally, build out the rest of the component using **Link**:

```jsx
import {withRequiredAuthInfo} from "@propelauth/react";
import {Link} from "react-router-dom";

function Home(props) {
   return <div>
        <Link to="/user_info">
            Click Here to see user info
        </Link>
   </div>
}

export default withRequiredAuthInfo(Home);
```

In **UserInfo.jsx**, we'll create a  component to display user information that pulls the **user** object that is injected automatically from **withAuthInfo**.

```jsx
import {withAuthInfo} from '@propelauth/react';

function UserInfo({user}) {

    return <span>
        <h2>User Info</h2>
        {user && user.pictureUrl && <img src={user.pictureUrl} alt={"profile"} className="pictureUrl" />}
        <pre>user: {JSON.stringify(user, null, 2)}</pre>
    </span>
}

export default withRequiredAuthInfo(UserInfo);
```

Finally, in **App.js**, we’ll set up our routes for the Home and User Info components in the **isLoggedIn** check:

```jsx
if (isLoggedIn) {
      return <div>
          <p>The User is logged in</p>
          <button onClick={() => logoutFn()}>
              Click here to log out
          </button>
          <Routes>
            <Route exact path="/" element={<Home/>}/>
            <Route path="/user_info" element={<UserInfo/>}/>
          </Routes>
      </div>
  }
```

User’s should now be able to view their information if they are logged in and click the link on the Home component.

## **Sending Requests from Frontend to Backend**

Up to this point, we have stayed on the frontend components of our application, but next, we will be making requests to a protected backend.

### **Making Authenticated Requests**

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

```jsx
Authorization: Bearer ACCESS_TOKEN
```

With fetch, this looks like:

```jsx
function fetchWhoAmI(accessToken) {
    return fetch("/whoami", {
        headers: {
            "Content-Type": "application/json",
            "Authorization": `Bearer ${accessToken}`
        }
    }).then(response => {
        if (response.ok) {
            return response.json()
        } else {
            return {status: response.status}
        }
    })
}
```

We can add this to a new **components/AuthenticatedRequest.jsx** component using React's [**useEffect**](https://reactjs.org/docs/hooks-effect.html) hook.

```jsx
import {withRequiredAuthInfo} from "@propelauth/react";
import {useEffect, useState} from "react";

function fetchWhoAmI(accessToken) {
    return fetch("/whoami", {
        headers: {
            "Content-Type": "application/json",
            "Authorization": `Bearer ${accessToken}`
        }
    }).then(response => {
        if (response.ok) {
            return response.json()
        } else {
            return {status: response.status}
        }
    })
}

function AuthenticatedRequest({accessToken}) {
    const [response, setResponse] = useState(null);
    useEffect(() => {
        fetchWhoAmI(accessToken).then(setResponse)
    }, [accessToken])

    return <span>
        <h2>Server Response</h2>
        <pre>{response ? JSON.stringify(response, null, 2) : "Loading..."}</pre>
    </span>
}

export default withRequiredAuthInfo(AuthenticatedRequest);
```

### **A Quick Note On CORS**

Our React application runs on port 3000, so we’ll need to run our backend on a different port (in this tutorial, we use 3001). For security reasons, browsers will not allow you to make requests from one domain to another, and [**http://localhost:3000**](http://localhost:3000) and [**http://localhost:3001**](http://localhost:3001) are considered different domains.

A simple way to fix this issue is to add the following to your package.json:

```javascript
"proxy": "http://127.0.0.1:3001/"
```

This will automatically proxy certain requests (like JSON requests) to [**http://localhost:3001**](http://localhost:3001). For more information, see [**the official React docs**](https://create-react-app.dev/docs/proxying-api-requests-in-development/).

Great, we can now make authenticated requests to any backend we want. The only problem? We don't have a backend yet - let's fix that.

## **Authentication in Python and Chalice**

### **Creating a Virtual Environment**

First, we create a new virtual environment and install our dependencies. We'll use [**propelauth-py**](https://docs.propelauth.com/reference/backend-apis/python) to validate the access token's the frontend sends.

```bash
$ mkdir backend-chalice
$ cd backend
$ python3 -m venv venv
$ source venv/bin/activate
$ pip install propelauth-py python-dotenv chalice
```

Chalice manages resources within your AWS account, so you’ll need to also set up the [**AWS CLI.**](https://aws.amazon.com/cli/)

```bash
$ aws configure
```

Finally, we can create our project:

```bash
$ chalice new-project example
```

This creates an `example` directory with an [**app.py**](http://app.py) file that contains the following:

```python
from chalice import Chalice

app = Chalice(app_name='example')

@app.route('/')
def index():
    return {'hello': 'world'}
```

If we `cd` into the directory and run `chalice deploy`, we get:

```bash
$ chalice deploy
Creating deployment package.
Updating policy for IAM role: example-dev
Updating lambda function: example-dev
Updating rest API
Resources deployed:
  - Lambda ARN: {ARN}
  - Rest API URL: https://{uniquestring}.execute-api.us-west-2.amazonaws.com/api/

```

And our endpoint is live! You can test it with curl:

```bash
$ curl {Rest API URL from above}
{"hello":"world"}
```

Under the hood, you now have a lambda function in your AWS account that is run when someone hits that endpoint. You can actually see the cold start problem very clearly here because your first request to this endpoint will be a bit slower than every subsequent request.

### **Creating our protected route**

The code that we’ll add to [**app.py**](http://app.py) is simple enough that we can look at it first and explain it after:

```jsx
import os

from chalice import Chalice
from propelauth_py import init_base_auth, TokenVerificationMetadata, UnauthorizedException

app = Chalice(app_name='example')

auth = init_base_auth(
    os.getenv('PROPELAUTH_AUTH_URL'), 											    
    os.getenv('PROPELAUTH_API_KEY'),
    token_verification_metadata=TokenVerificationMetadata(
        verifier_key='''PROPELAUTH_VERIFIER_KEY''',
        issuer='PROPELAUTH_AUTH_URL'
    )
)

@app.route('/whoami')
def whoami():
    try:
        auth_header = app.current_request.headers.get("authorization")
        user = auth.validate_access_token_and_get_user(auth_header)
        return {'user_id': user.user_id}
    except UnauthorizedException:
        # UnauthorizedError is Chalice's exception which will return a 401
        raise UnauthorizedError
```

* **validate\_access\_token\_and\_get\_user** is a function takes in the **Authorization** HTTP header and verifies the request was made by a valid user. If the Authorization HTTP header is malformed or doesn’t contain a valid [**access token**](https://docs.propelauth.com/overview/access-tokens), an **UnauthorizedException** is thrown.
    
* **PROPELAUTH\_AUTH\_URL**, **PROPELAUTH\_API\_KEY,** and **PROPELAUTH\_VERIFIER\_KEY** can both be found by clicking on **Backend Integration** on the sidebar of your PropelAuth project. These are used *once* on startup to fetch the information needed to validate tokens. Access tokens (which are [**JWTs**](https://blog.propelauth.com/jwt-explained/)) are then validated quickly without needing to make any external requests.
    

Re-deploy your code:

```bash
$ chalice deploy
```

And now when we curl the endpoint, we’d expect a 401 since we aren’t providing an access token:

```bash
$ curl {URL}/whoami
{"Code":"InternalServerError","Message":"An internal server error occurred."}
```

But we actually get an internal server error? If you check your lambda logs in AWS, you’ll see that our lambda function doesn’t know what propelauth-py is. Chalice isn’t using our virtual environment to understand your project’s dependencies, it’s using the `requirements.txt` file. If we run

```javascript
pip freeze
```

to determine the exact version we're on, and redeploy, we get our 401:

```bash
$ curl {URL}/whoami
{"Code":"UnauthorizedError","Message":""}

```

We should also verify that a valid access token returns a 200 with our user id. Take the `AUTH_URL` from your PropelAuth dashboard and visit `{AUTH_URL}/api/v1/refresh_token`, which will return both an access token and metadata about the user. We’ll pass this along in the Authorization header:

```bash
$ curl -H "Authorization: Bearer eyJ..." {URL}/whoami
{"user_id":"c7fb5888-8ff2-4ad6-aa10-7775911ecd41"}
```

Now that we have a backend running, if we now include our **components/AuthenticatedRequest.jsx** in a new Route on **App.js:**

```jsx
<Route path="/auth" element={<AuthenticatedRequest/>}/>
```

And if we add a new link to **components/Home.jsx:**

```jsx
<Link to="/auth">
    Click Here to see an authenticated request to the backend
</Link>
```

If the user is logged in, they should see the server response from our authenticated request:

![Authenticated response](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/88liv9dhiqmdjm3g6a4n.png)

## **Organization Information**

A common use case for B2B applications is the ability to separate users into **organizations or teams**. For the purposes of this application build, we will add functionality to view the list of organizations that the current user is a part of, and to view other members of that organization.

### **Display Organizations**

First, we’ll create **components/ListOfOrgs.jsx** that will list all of the organizations a **user** is a part of, or display a button to redirect to the organization create/invitation page if they are not a part of any organizations.

```jsx
import {useRedirectFunctions, withRequiredAuthInfo} from "@propelauth/react";
import {Link} from "react-router-dom";

function NoOrganizations() {
    const {redirectToCreateOrgPage} = useRedirectFunctions()

    return <div>
        You aren't a member of any organizations.<br/>
        You can either create one below, or ask for an invitation.<br/>
        <button onClick={redirectToCreateOrgPage}>
            Create an organization
        </button>
    </div>
}

function ListOrganizations({orgs}) {
    return <>
        <h3>Your organizations</h3>
        <ul>
            {orgs.map(org => {
                return <li key={org.orgId}>
                    <Link to={`/org/${org.urlSafeOrgName}`}>
                        {org.orgName}
                    </Link>
                </li>
            })}
        </ul>
    </>
}

function ListOfOrgs(props) {
    const orgs = props.orgHelper.getOrgs()
    if (orgs.length === 0) {
        return <NoOrganizations />
    } else {
        return <ListOrganizations orgs={orgs}/>
    }
}

// By default, if the user is not logged in they are redirected to the login page
export default withRequiredAuthInfo(ListOfOrgs);
```

**orgHelper** allows us to easily get all the **organizations** that the user is a member of, and by passing this array into our helper component **ListOrganizations**, we can map over each organization and create a new **Link** and dynamic route to display relevant information.

Next, we’ll create **components/OrgInfo.jsx** and, following the same pattern we did with **AuthenticatedRequest.jsx**, we’ll use React's [**useEffect**](https://reactjs.org/docs/hooks-effect.html) hook to send a fetch to a new route, **org/orgId.**

```jsx
import {withAuthInfo} from '@propelauth/react';
import {useParams} from "react-router-dom";
import { useEffect, useState } from "react";

function fetchOrgInfo(orgId, accessToken) {
    return fetch(`/org/${orgId}`, {
        headers: {
            "Content-Type": "application/json",
            "Authorization": `Bearer ${accessToken}`
        }
    }).then(response => {
        // console.log('response: ', response)
        if (response.ok) {
            return response.json()
        } else {
            return {status: response.status}
        }
    })
}

function OrgInfo({ orgHelper, accessToken }) {
    const {orgName} = useParams();
    const orgId = orgHelper.getOrgByName(orgName).orgId

    const [response, setResponse] = useState(null)
    
    useEffect(() => {
        fetchOrgInfo(orgId, accessToken).then(setResponse)
    }, [orgId, accessToken])

    return <div>
        <p>{response ? JSON.stringify(response.org) : "Loading..."}</p>
    </div>
}

export default withAuthInfo(OrgInfo);
```

We then need to create the backend route to return an authenticated response, so in our **backend-chalice/example/app.py** file we’ll add a new route:

```python
@app.route('/org/{org_id}')
def org_info(org_id):
    try:
        auth_header = app.current_request.headers.get("authorization")
        user_and_org_member_info = auth.validate_access_token_and_get_user_with_org(
            auth_header, org_id
        )
        return {'user_id': user_and_org_member_info.user.user_id,
                'org_name': user_and_org_member_info.org_member_info.org_name}
    except UnauthorizedException:
        raise UnauthorizedError
    except ForbiddenException:
        # We return a 403 for valid users that do not have access to the specified organization
        raise ForbiddenError
```

[**validate\_access\_token\_and\_get\_user\_with\_orgis**](https://docs.propelauth.com/reference/backend-apis/python#validate-access-token-and-get-user-with-org) a function will verify that a request was made by a valid user AND that user is in an organization.

Finally we need to display the new routes in our application. In **App.js** we need to import and create new **Route** paths for **ListOfOrgs.jsx** and **OrgInfo.jsx**

```jsx
<Route path="/orgs" element={<ListOfOrgs/>}/>
<Route path="/org/:orgName" element={<OrgInfo/>}/>
```

And on **Home.jsx** we need to add a new **Link** to the path **/orgs** to display **ListOfOrgs.jsx**

```javascript
<Link to="/orgs">
    Click Here to see org info
</Link>
```

![Org page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dm0jl542f21fj0kyutx1.png)

We’ll leave it up to you to make it look good :)

## **Wrapping Up**

This guide provides a comprehensive look at an authentication application framework you can use to get started. By using PropelAuth for authentication, we were able to skip building any auth UIs, transactional emails, invitation flows, and more.

Our frontend made an authenticated request to our backend, and our backend was able to identify the user that made the request. You can use this for things like saving information in a database per user\_id.

If you have any questions, please reach out to [**support@propelauth.com**](mailto:support@propelauth.com).