React/FastAPI Starter Guide
This guide walks through the process of setting up authentication using PropelAuth on top of a product built in FastAPI & React.
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:
There’s a lot more to discover here. We can set up social logins, like Login with Google, passwordless logins via Magic Links. We can collect more information on signup like first/last name or accepting terms of service. We can also create and manage 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
Prereqs:
- An application using a React-based framework. We use Next.js (pages router) and Typescript for this guide.
Installation
We’re going to use @propelauth/react which allows us to manage our user’s information.
npm install @propelauth/react
Run it and update the Dashboard
We are now ready to run the application:
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
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:
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
. We’ll replace our index.tsx
file with this:
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>
}
}
And…
Oh, we’re not logged in yet. We could navigate directly to our Auth URL and login, but let’s instead add a few links to our application.
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
which won’t render the underlying component until loading is finishing, making the code a bit cleaner:
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
which can make it even cleaner:
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.
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:
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.
Now that our frontend has all the basics implemented, let’s move over to the backend.
Authentication in FastAPI
Prereqs:
- A FastAPI application. If you don’t have one, follow the guide here.
- A PropelAuth API Key. You can generate one on the Backend Integration section of your dashboard.
Installation
Install the propelauth-fastapi
library in your project:
pip install propelauth-fastapi
Creating an unprotected route
Let’s say we started with this application (taken from FastAPI’s getting started guide):
from fastapi import FastAPI
app = FastAPI()
@app.get("/api/test")
def read_root():
return {"Hello": "World"}
If we run the application:
uvicorn main:app --reload --port=3001
and then hit the endpoint with curl:
$ curl localhost:3001/api/test
{"Hello":"World"}
We can see what you probably already knew from the code, read_root
is accessible to anyone.
Protecting our unprotected route
To lock it down so that only users can access it, all we have to do is initialize the propelauth_fastapi library, and dependency inject a user.
from fastapi import FastAPI, Depends
from propelauth_fastapi import init_auth
from propelauth_py.user import User
app = FastAPI()
auth = init_auth("YOUR_AUTH_URL", "YOUR_API_KEY")
@app.get("/api/test")
def read_root(user: User = Depends(auth.require_user)):
return {"Hello": user.user_id}
And now when we hit the endpoint with curl:
$ curl -v localhost:3001/api/test
...
< HTTP/1.1 401 Unauthorized
...
{"detail":"Unauthorized"}
You can see that we get a 401 Unauthorized error, as we didn’t pass in an information to indicate who we are. If you want to test the backend in isolation, you can read about that here. But let’s instead go back to the frontend, and see how to make an authenticated HTTP request.
Making an authenticated HTTP request
Prereqs:
- To make a request from Next.js to a backend running on a different localhost port, you’ll need to handle CORS issues.
For Next.js, we handle CORS locally by adding a rewrite for all /api/*
routes to our backend on port 3001. This is our next.config.js
:
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'http://127.0.0.1:3001/api/:path*'
}
]
}
}
module.exports = nextConfig
Start with an unauthenticated HTTP request
Let’s head back to our Next.js application and make a new file pages/requestdemo.tsx
that uses useEffect
to make an HTTP request to our backend:
import {useEffect, useState} from "react";
const fetchFromApi = async () => {
const response = await fetch("/api/test", {
headers: {
"Content-Type": "application/json",
}
});
if (response.ok) {
return response.json();
} else {
return {status: response.status};
}
}
export default 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>
}
}
And, since we didn’t specify any user information, we get the same 401 Unauthorized error.
Now, make it authenticated
Similar to user
or isLoggedIn
, one of the other values that we receive from useAuthInfo
, withAuthInfo
, and withRequiredAuthInfo
is an accessToken
.
This accessToken is unique to our user, and is what our backend is checking when it says we are authorized or unauthorized. We need to pass this in to our backend via an Authorization header, like so:
Authorization: Bearer ${accessToken}
Here’s that same component from the last section, but this time it will pass the access token to the backend:
import {useEffect, useState} from "react";
import {withAuthInfo} from "@propelauth/react";
const fetchFromApi = async (accessToken: string | null) => {
const response = await fetch("/api/test", {
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>
}
})
And now, as long as we are logged in, we see:
Organizations, Custom User Schema, Impersonation, and more
Now that you’ve built a basic auth system using PropelAuth, you can easily add more features on top of it. Whether you want to add organizations, collect and store more information about your users, or even see what your users are seeing - it’s all possible now!
If you have any questions, please reach out at support@propelauth.com.