Introduction
In this tutorial, we'll be building a login authentication using React and FastApi. This will help show how we can use both packages for a login authentication process but before that, let's take at React and also what FastApi is.
What is FastApi
FastAPI is a modern, fast (high-performance) web framework for building APIs with Python. It supports both synchronous and asynchronous actions, data validation, authentication, and interactive API documentation, all of which are powered by OpenAPI. It comes with exciting features like:
What is React
React is a user interface development library. It can construct full-stack apps by talking with a server/API and operates as a SPA (single page app) on the client. Because it is competent and directly equivalent to frameworks like Angular or Vue, React is frequently referred to as a frontend "framework".
Requirements
- Python installed.
- Basic knowledge of Javascript.
- Basic Knowledge of React.
- Knowledge on python is a plus.
Installing FastAPI
open up our terminal and cd into our project folder
and write the following:
mkdir backend
cd
into the just created folder and run the following:
pip install fastapi
pip install "uvicorn[standard]"
pip install pyjwt
let's leave that for later and proceed with building our frontend.
Building The Frontend
let's create and app and install the following packages:
npx create-react-app frontend
Next we install the following packages:
npm install axios react-router-dom
After we've done that, navigate to src/index.js
and import BrowserRouter
:
import { BrowserRouter } from "react-router-dom";
We then have to replace the React.StrictMode
tags with this:
<BrowserRouter>
<App />
</BrowserRouter>,
Now head over to app.js
and import this:
import { Routes, Route } from "react-router-dom";
import Login from "./login";
import Profile from "./Profile";
Inside our return()
lets delete our div and replace it with this:
<div className ="App">
<Routes><!---what are routes in react-!>
<Route path="/" element = {<Login/>}/>
<Route path="/profile" element = {<Profile/>}/>
</Routes>
</div>
Here we are using the routes to the pages we'll be creating shortly. Next let's create a file called Login.js
in our src
folder and paste this:
export default function Login() {
return (
<>
<h1>login page</h1>
</>
);
}
We'll also create another file called Profile.js
and paste this:
export default function Profile() {
return (
<>
<h1>profile page</h1>
</>
);
}
Now let's start our application:
npm start
As you can see, our page is working fine (also check the profile page by adding /profile
to the url). Now that we're done with basics, let's proceed to setting up our authentication.
Let's create a new file in our src
folder called Auth.js
and paste this:
import { useLocation,Navigate } from "react-router-dom"
export const setToken = (token)=>{
localStorage.setItem('temitope', token)// make up your own token
}
export const fetchToken = (token)=>{
return localStorage.getItem('temitope')
}
export function RequireToken({children}){
let auth = fetchToken()
let location = useLocation()
if(!auth){
return <Navigate to='/' state ={{from : location}}/>;
}
return children;
}
}
Here we created variables setting our token, fetching and also requiring our token, so let's go back to our app.js
and import our token:
import { RequireToken } from "./Auth";
We'll be adding some things in our app.js
. In our Route path="/profile"
let's make changes to the element by adding our RequireToken
so our Route path="/profile"
should look like this:
<Route
path="/profile"
element={
<RequireToken>
<Profile />
</RequireToken>
}
/>
When we save this and go to our app, we see that our profile page is now protected and can only be accessed with a valid token. Now let's finish our login page with our login form. Head over to the login page, clear all, and paste this:
import { useNavigate } from "react-router";
import { fetchToken } from "./Auth";
export default function Login() {
const navigate = useNavigate();
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
//check to see if the fields are not empty
const login = () => {
if ((username == "") & (password == "")) {
return;
} else {
// make api call to our backend. we'll leave this for later
}
};
return (
<>
<div style={{ minHeight: 800, marginTop: 30 }}>
<h1>login page</h1>
<div style={{ marginTop: 30 }}>
{fetchToken() ? (
<p>you are logged in</p>
) : (
<div>
<form>
<label style={{ marginRight: 10 }}>Input Username</label>
<input
type="text"
onChange={(e) => setUsername(e.target.value)}
/>
<label style={{ marginRight: 10 }}>Input Password</label>
<input
type="text"
onChange={(e) => setPassword(e.target.value)}
/>
<button onClick={login}>Login</button>
</form>
</div>
)}
</div>
</div>
</>
);
}
We'll be pausing there for now. It's time to work on our backend.
Creating the backend
Now lets open up our backend folder, create a main.py
file and input the following:
from fastapi import FastAPI
from pydantic import BaseModel
import jwt
from pydantic import BaseModel
from fastapi.encoders import jsonable_encoder
from fastapi.middleware.cors import CORSMiddleware
SECERT_KEY = "YOUR_FAST_API_SECRET_KEY"
ALGORITHM ="HS256"
ACCESS_TOKEN_EXPIRES_MINUTES = 800
test_user = {
"username": "temitope",
"password": "temipassword",
}
app = FastAPI()
origins = {
"http://localhost",
"http://localhost:3000",
}
app.add_middleware(
CORSMiddleware,
allow_origins = origins,
allow_credentials =True,
allow_methods = ["*"],
allow_headers= ["*"],
)
class LoginItem(BaseModel):
username: str
password: str
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.post("/login")
async def user_login(loginitem:LoginItem):
data = jsonable_encoder(loginitem)
if data['username']== test_user['username'] and data['password']== test_user['password']:
encoded_jwt = jwt.encode(data, SECERT_KEY, algorithm=ALGORITHM)
return {"token": encoded_jwt}
else:
return {"message":"login failed"}
Here we are trying to:
- Generate a token.
- Defining a
test user object
to check against the user login credentials - Configuring our
CORS
to allow our React app to send POST requests - Running a check with the coming data with
test_user
.
Almost done now that we're done, let's go back to the frontend to finish things up. Head over to login.js and replace it with this:
import { useNavigate } from "react-router";
import { fetchToken, setToken } from "./Auth";
import { useState } from "react";
import axios from "axios";
export default function Login() {
const navigate = useNavigate();
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
//check to see if the fields are not empty
const login = () => {
if ((username == "") & (password == "")) {
return;
} else {
// make api call to our backend. we'll leave thisfor later
axios
.post("http://localhost:8000/login", {
username: username,
password: password,
})
.then(function (response) {
console.log(response.data.token, "response.data.token");
if (response.data.token) {
setToken(response.data.token);
navigate("/profile");
}
})
.catch(function (error) {
console.log(error, "error");
});
}
};
return (
<div style={{ minHeight: 800, marginTop: 30 }}>
<h1>login page</h1>
<div style={{ marginTop: 30 }}>
{fetchToken() ? (
<p>you are logged in</p>
) : (
<div>
<form>
<label style={{ marginRight: 10 }}>Input Username</label>
<input
type="text"
onChange={(e) => setUsername(e.target.value)}
/>
<label style={{ marginRight: 10 }}>Input Password</label>
<input
type="text"
onChange={(e) => setPassword(e.target.value)}
/>
<button type="button" onClick={login}>
Login
</button>
</form>
</div>
)}
</div>
</div>
);
}
We'll also make changes to our profile.js
so let's open it up and paste this:
import { useNavigate } from "react-router";
export default function Profile() {
const navigate = useNavigate();
const signOut = () => {
localStorage.removeItem("temitope");
navigate("/");
};
return (
<>
<div style={{ marginTop: 20, minHeight: 700 }}>
<h1>Profile page</h1>
<p>Hello there, welcome to your profile page</p>
<button onClick={signOut}>sign out</button>
</div>
</>
);
}
We're done, let' test our app . Run the code:
uvicorn main:app --reload
Conclusion
In the tutorial, we looked at what FastApi is and also what React is. We also learned how to install FastApi and React using these ideas to build our login authentication. Here's a link to the repo on github. Happy coding!
Top comments (5)
How do you verify the token you saved in local storage? I can set any data as a token in local storage and access the profile page without authentication!
IT DOESNT WORK NONE OF IT WORKS
Nothing works. Spend a few hours debugging, still didn't solve all the bugs
I have also phased issues with this code, nothing works. Is there an updated code that works?
How about users signing up?