# Example App React/Chalice

In this guide, we’ll build an example B2B application in a React-based framework where users can sign up, login, manage their accounts, and view organization and member information using PropelAuth, Next.js, and Chalice.

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

- [**Next.js Pages Router**](https://nextjs.org/docs/pages) - for our frontend
- [**Python**](https://www.python.org/?ref=propelauth.com) and [**Chalice**](https://aws.github.io/chalice/?ref=propelauth.com) - for our backend
- [**PropelAuth**](/content/site-root.html) - for user login and management

## [Setting up our Authentication UIs](https://docs.propelauth.com/getting-started/example-apps/react-python-chalice#setting-up-our-authentication-uis)

PropelAuth provides UIs for signup, login, and account management, so we don’t need to build them ourselves.

To get started, let’s create a project:

We now actually have all the UIs we need to fully test the experience of our users. The preview button lets you view different pages, like the signup page:

For now, though, let’s customize these UIs, which we can do on the **Look & Feel** section of the dashboard followed by **Open Editor**:

There’s a lot more to discover in the PropelAuth dashboard. We can set up [social logins](https://docs.propelauth.com/overview/authentication/login-methods#social-login-sso), like Login with Google, and enable [passwordless logins via Magic Links](https://docs.propelauth.com/overview/authentication/login-methods#passwordless-magic-links). We can collect more information on signup like [first/last name or accepting terms of service](https://docs.propelauth.com/overview/user-management/user-properties). We can also create and manage [organizations](https://docs.propelauth.com/overview/basics/organizations), if our users need to work together in groups. But, for now, let’s jump right in to building our frontend.

## [Authentication in React](https://docs.propelauth.com/getting-started/example-apps/react-python-chalice#authentication-in-react)

### Prereqs

- An application using a [**React-based framework**](https://react.dev/learn/start-a-new-react-project). We use Next.js Pages Router and Typescript for this guide. If you'd prefer to use Vanilla React or Next.js with App Router, check out our [quickstart guide](https://docs.propelauth.com/getting-started/quickstart-fe).

### Next.js Installation

Start by creating a new Next.js project:

```bash
npx create-next-app@latest
```

For this guide we'll be using TypeScript as well as Pages Router, so make sure to set the following when prompted:

```bash
✔ What is your project named? `my-app`
✔ Would you like to use TypeScript? `Yes`
✔ Would you like to use App Router? (recommended) `No`
```

### PropelAuth Installation

We’re going to use PropelAuth's [**@propelauth/react**](https://www.npmjs.com/package/@propelauth/react?ref=propelauth.com) library which allows us to manage our user’s information. Change directory to your Next.js app and run this command:

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

### Run it and update the Dashboard

We are now ready to run the application:

```bash
npm run dev
```

Note down which port the application is running on, and then update the PropelAuth dashboard. We need to configure PropelAuth so that it knows both where the application is running and where to redirect the user back to.

Enter the URL the frontend is running on in the **Primary Frontend Location** section under **Frontend Integration.** For our example, this is `http://localhost:3000`

You’ll also want to copy the Auth URL, as we’ll use it in the next step.

### Set up the AuthProvider

The [AuthProvider](https://docs.propelauth.com/reference/frontend-apis/react#auth-provider) is responsible for checking if the current user is logged in and fetching information about them. You should add it to the top level of your application so it never unmounts. In Next.js, this is the `_app.tsx` file:

### _app.tsx

```tsx
import type { AppProps } from 'next/app'
import { AuthProvider } from '@propelauth/react'

export default function App({ Component, pageProps }: AppProps) {
    return (
        <AuthProvider authUrl="ENTER_YOUR_AUTH_URL_HERE">
            <Component {...pageProps} />
        </AuthProvider>
    )
}
```

### Displaying user information

And now, the moment you’ve been waiting for! Let’s display the user’s email address via the React hook [useAuthInfo](https://docs.propelauth.com/reference/frontend-apis/react#use-auth-info) . We’ll replace our `index.tsx` file with this:

### index.tsx

```tsx
import { useAuthInfo } from '@propelauth/react'

export default function Home() {
    const { loading, isLoggedIn, user } = useAuthInfo()
    if (loading) {
        return <div>Loading...</div>
    } else if (isLoggedIn) {
        return <div>Logged in as {user.email}</div>
    } else {
        return <div>Not logged in</div>
    }
}
```

### Small note: withAuthInfo and withRequiredAuthInfo

`useAuthInfo` is great, but it does mean you have to check the loading state often. PropelAuth also provides a higher order component [withAuthInfo](https://docs.propelauth.com/reference/frontend-apis/react#with-auth-info) which won’t render the underlying component until loading is finishing, making the code a bit cleaner:

```tsx
import { withAuthInfo } from '@propelauth/react'

export default withAuthInfo(function Home({ isLoggedIn, user }) {
    if (isLoggedIn) {
        return <div>Logged in as {user.email}</div>
    } else {
        return <div>Not logged in</div>
    }
})
```

Similarly, if you know the user is logged in, you can use [withRequiredAuthInfo](https://docs.propelauth.com/reference/frontend-apis/react#with-required-auth-info) which can make it even cleaner:

```tsx
import { withRequiredAuthInfo } from '@propelauth/react'

// By default, will redirect the user to the login page if they aren't logged in
export default withRequiredAuthInfo(function Home({ user }) {
    return <div>Logged in as {user.email}</div>
})
```

You can read more about this [here](/content/post/why-we-have-both-react-hooks-and-hocs/index.html).

### Adding Login/Account/Logout Buttons to our application

The `@propelauth/react` library also provides React hooks `useRedirectFunctions` and `useLogoutFunction` which provide easy ways for us to send the user to the hosted pages (like login, signup, account, etc) or log the user out entirely. Let’s update our `index.tsx` file:

### index.tsx

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

export default withAuthInfo(function Home({ isLoggedIn, user }) {
    const { redirectToLoginPage, redirectToAccountPage } = useRedirectFunctions()
    const logoutFn = useLogoutFunction()

if (isLoggedIn) {
        return (
            <>
                <div>Logged in as {user.email}</div>
                <div>
                    <button onClick={() => redirectToAccountPage()}>Account</button>
                    <button onClick={() => logoutFn(false)}>Logout</button>
                </div>
            </>
        )
    } else {
        return (
            <>
                <div>Not logged in</div>
                <button onClick={() => redirectToLoginPage()}>Login</button>
            </>
        )
    }
})
```

And now our users have an easy way to login, view their account information, and logout.

## [Sending Requests from Frontend to Backend](https://docs.propelauth.com/getting-started/example-apps/react-python-chalice#sending-requests-from-frontend-to-backend)

### Start with an unauthenticated HTTP request

In our Next.js application, let's make a new file `requestdemo.tsx` that uses `useEffect` to make an HTTP request to our backend:

### requestdemo.tsx

```jsx
import { useEffect, useState } from 'react'
import { withAuthInfo } from '@propelauth/react'

const fetchFromApi = async () => {
    const response = await fetch('/api/whoami', {
        headers: {
            'Content-Type': 'application/json',
        },
    })
    if (response.ok) {
        return response.json()
    } else {
        return { status: response.status }
    }
}

export default withAuthInfo(function RequestDemo() {
    const [response, setResponse] = useState(null)

useEffect(() => {
        fetchFromApi().then((data) => setResponse(data))
    }, [])

if (response) {
        return <pre>{JSON.stringify(response, null, 2)}</pre>
    } else {
        return <div>Loading...</div>
    }
})
```

### Authentication in Python and Chalice

### Installation

Install the `propelauth-py` and `chalice` libraries in your project:

```bash
pip install propelauth-py 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)` that we can adjust to contain the following:

### app.py

```python
from chalice import Chalice

app = Chalice(app_name='example')

@app.route('/whoami')
def index():
    return {'Hello': 'World'}
```

It also creates a new `requirements.txt` file. Let's add the `propelauth-py` to it:

```
propelauth-py==3.1.16
```

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/
```

### A Quick Note On CORS

To make a request from Next.js to our new backend, you’ll need to handle [CORS issues](/content/post/avoiding-cors-issues-in-react-next-js/index.html).

For Next.js, we handle CORS locally by adding a rewrite for all `/api/*` routes to our backend. This is our `next.config.mjs`:

### next.config.mjs

```jsx
/** @type {import('next').NextConfig} */
const nextConfig = {
    reactStrictMode: true,
    async rewrites() {
        return [
            {
                source: '/api/:path*',
                destination: 'https://{uniquestring}.execute-api.us-west-2.amazonaws.com/api/:path*',
            },
        ]
    },
}

export default nextConfig
```

Now that we have CORS addressed as well as our backend running, we can then navigate back to the `/requestdemo` page:

### Creating our protected route

To lock it down so that only users can access it, all we have to do is initialize the [propelauth_py library](https://docs.propelauth.com/reference/backend-apis/python), and use PropelAuth's [validate_access_token_and_get_user](https://docs.propelauth.com/reference/backend-apis/python#protect-api-routes) to validate the user's access token. This will return a [user object](https://docs.propelauth.com/reference/backend-apis/python#user) which we can use to return back the user's information.

To get your `YOUR_AUTH_URL` and `YOUR_API_KEY`, navigate to the **Backend Integration** page in your PropelAuth Dashboard.

### app.py

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

app = Chalice(app_name='example')

auth = init_base_auth("YOUR_AUTH_URL", "YOUR_API_KEY")

@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
```

Re-deploy your code:

```bash
chalice deploy
```

And now when we visit the `/requestdemo` page again, we instead get a 401 Unauthorized error!

### Making an Authenticated HTTP request

Here's that same page from the last section, but this time it will pass the access token to the backend:

### requestdemo.tsx

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

const fetchFromApi = async (accessToken: string | null) => {
    const response = await fetch("/api/whoami", {
        headers: {
            "Content-Type": "application/json",
            "Authorization": `Bearer ${accessToken}`
        }
    });
    if (response.ok) {
        return response.json();
    } else {
        return {status: response.status};
    }
}

export default withAuthInfo(function RequestDemo({accessToken}) {
    const [response, setResponse] = useState(null);

useEffect(() => {
        fetchFromApi(accessToken).then((data) => setResponse(data));
    }, [])

if (response) {
        return <pre>{JSON.stringify(response, null, 2)}</pre>
    } else {
        return <div>Loading...</div>
    }
})
```

## [Organization Information](https://docs.propelauth.com/getting-started/example-apps/react-python-chalice#organization-information)

### Display Organizations

First, we’ll create a **orgs.tsx** page 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.

### orgs.tsx

```jsx
import {useRedirectFunctions, WithLoggedInAuthInfoProps, withRequiredAuthInfo, OrgMemberInfoClass} from "@propelauth/react";
import Link from 'next/link';

interface OrgProps {
    orgs: OrgMemberInfoClass[]
}

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}: OrgProps) {
    return <>
        <h3>Your organizations</h3>
        <ul>
            {orgs.map(org => {
                return <li key={org.orgId}>
                    <Link href={`/org/${org.orgId}`}> 
                        {org.orgName} 
                    </Link>
                </li>
            })}
        </ul>
    </>
}

function ListOfOrgs(props: WithLoggedInAuthInfoProps) {
    const orgs = props.userClass.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);
```

Let's add a button to our `index.tsx` page so we can easily navigate to our new page.

### index.tsx

```tsx
<Link href="/orgs">
    <button>Org List</button>
</Link>
```

When we navigate to the new page, we'll either see a button to redirect the user to create an org or a list of orgs that the user belongs to.

### /org/[orgId].tsx

```jsx
import { withRequiredAuthInfo, WithLoggedInAuthInfoProps } from '@propelauth/react';
import { useRouter } from 'next/router';
import { useEffect, useState } from "react";

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

function OrgInfo({ accessToken }: WithLoggedInAuthInfoProps) {
  const router = useRouter();
  const orgId = router.query.orgId;
  const [response, setResponse] = useState(null)

useEffect(() => {
    fetchOrgInfo(orgId? orgId.toString() : "", accessToken).then(setResponse)
  }, [orgId, accessToken])

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

export default withRequiredAuthInfo(OrgInfo);
```

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

### app.py

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

When we click on one of our org links, we'll now see a JSON blob of the users who belong to that org.

### 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](https://docs.propelauth.com/overview/basics/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 at **[support@propelauth.com](mailto:support@propelauth.com)**.
