Create a Authentication System Using React ,FastApi and MongoDB(FARM stack)

Manjeet Kapil
9 min readJul 2, 2021

--

In this project i have used FastApi for backend APis and MongoDb as our databse and React as our Frontend Framework.In this system we will have feature of registering a user and user can login with his given username and password.So lets write some code …First we will cover our Backend.

FastApi

FASTAPI-BACKEND

Go to any directory in your sytem and make new dir and go inside it

mkdir backend
cd backend/

then make a virtual env using below command and activate it below two commands for Linux Users vary from system to sytem.

python -m venv env
source env/bin/activate

After this install FastApi using command pip install fastapi and pip install uvicornmake a new file named main.py and inside it put this code

from fastapi import FastAPI
app = FastAPI()
@app.get('/')
def index():
return {'data':'Hello World'}

Now run command uvicorn main:app --reload now you can visit http://127.0.0.1:8000/ and our most simplest api is up and running and most cool feature here is the swagger docs which you can access by running http://127.0.0.1:8000/docs .Now our basic app is up and running lets write our register and login endpoints.

Add this code to main.py above our (‘/’) route and also install pip install pydantic

from pydantic import BaseModel
from typing import Optional
class User(BaseModel):
username: str
company: str
password: str
class Login(BaseModel):
username: str
password: str
class Token(BaseModel):
access_token: str
token_type: str
class TokenData(BaseModel):
username: Optional[str] = None

So User model is for request for register route so in fast api you can pre define the request objects so it can handle basic error, you can learn more about them in documentation,Login model is for request for login route,Token model and TokenData is for Tokens which we will be using little later.Connection to Mongodb for this we will use pymongo pip install pymongo and pip install dnspython so attach this code below all imports in main.py

from pymongo import MongoClient
mongodb_uri = 'mongodb+srv://<user>:<password>@cluster0.nbszr.mongodb.net/myFirstDatabase?retryWrites=true&w=majority'
port = 8000
client = MongoClient(mongodb_uri, port)
db = client["User"]

So here we are connecting our mongodb atlas db with FastAPi and db is our instance for User(db) ,you change it which suits you,after this we will be using this db for every database thing.

Create New file name hashing.py for password hashing and add below code and also install pip install passlib and pip install cryptography and pip install openpyxl

from passlib.context import CryptContextpwd_cxt = CryptContext(schemes =["bcrypt"],deprecated="auto")class Hash():
def bcrypt(password:str):
return pwd_cxt.hash(password)
def verify(hashed,normal):
return pwd_cxt.verify(normal,hashed)

So this Hash class bcrypt function chages simple password to some hashed value which we will store in database. and verify password checks that giver user password matches with database hashed password or not.

Create New File name jwttoken.py and install pip install python-jose and add below code to it.

from datetime import datetime, timedelta
from jose import JWTError, jwt
# from main import TokenData
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
def create_access_token(data: dict):
to_encode = data.copy()
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
def verify_token(token:str,credentials_exception):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
token_data = main.TokenData(username=username)
except JWTError:
raise credentials_exception

This file is for jwttoken related stuff so it creates a token valid for 30 minutes using the function create_access_token for a user using it’s username and verify token is used for finding the current user which generated the token.(Refrence FastAPi Official Docs)

Make a new file named oauth.py for finding the current user which generated the token.Attach below code to it.

from fastapi import Depends,HTTPException
from jwttoken import verify_token
from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")oauth2_scheme
def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
return verify_token(token,credentials_exception)

Now lets attach our routes so for that add below code to main.py

import hashing import Hash
from fastapi import FastAPI, HTTPException, Depends, Request,status
from oauth import get_current_user
from jwttoken import create_access_token
from fastapi.security import OAuth2PasswordRequestForm
from fastapi.middleware.cors import CORSMiddleware
origins = [
"http://localhost:3000",
"http://localhost:8080",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.post('/register')
def create_user(request:User):
hashed_pass = Hash.bcrypt(request.password)
user_object = dict(request)
user_object["password"] = hashed_pass
user_id = db["users"].insert(user_object)
return {"res":"created"}
@app.post('/login')
def login(request:OAuth2PasswordRequestForm = Depends()):
user = db["users"].find_one({"username":request.username})
if not user:
raiseHTTPException(status_code=status.HTTP_404_NOT_FOUND)
if not Hash.verify(user["password"],request.password):
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
access_token = create_access_token(data={"sub": user["username"] })
return {"access_token": access_token, "token_type": "bearer"}

In the register route we are using Hash.bcrypt which is a class for converting normal password to some hashed password then we change our user object with hashed password and save it to the pymongo.In the login route first we are finding are the user with that username than we are verifying the password using same Hash class then we create a access token using Python-jose library in which we only provides our username than it return the token.Middleware is for accepting the requesting from frontend server so attach it ike that and if your frontend is at other route then you can add that in origin list.

Now we have 4 files named hashing.py,jwttoken.py,main.py,oauth.py if some library i missed while installing you can easily install it by seeing the error.

These are all files for our backend system now you can see (“/”) route in which you can’t directly access that endpoint we can secure similarly all end points by adding “current_user: User = Depends(get_current_user)” inside paramets of fucntion ,for accesing these endpoints you have to provide token than only you can access that page.So for trying it out in postman inside header section in key -value pairs write key: Authorization ,value: Bearer <token> token you will get when you login through /login route.Now our Backend system is Ready.

REACT-FRONTEND

In this part we will have two options first of login and second of register ,i have used very little css for this project as our main focus is connecting the backend and frontend now.Those looks like this…

Register route
Login Route

so lets start with code now, first make a new dicrectory and create new react app.

mkdir frontend
cd frontend/
npx create-react-app <app_name>
cd <app_name>
npm start

Our basic app is ready in App.js remove everything form function app and put below code

import {
BrowserRouter as Router,
Switch,
Redirect,
Route,
Link,
} from "react-router-dom";
import React, { useState } from "react";
const AuthApi = React.createContext();function App() {
const [auth, setAuth] = useState(false);
return (
<>
<AuthApi.Provider value={{ auth, setAuth }}>
<Router>
<div>
<nav>
<ul>
{!auth ? (
<li>
<Link to="/register">Regsiter</Link>
</li>
) : (
<></>
)}
{!auth ? (
<li>
<Link to="/login">Login</Link>
</li>
) : (
<></>
)}
<li>
<Link to="/">Home</Link>
</li>
</ul>
</nav>
<Routes />
</div>
</Router>
</AuthApi.Provider>
</>
);
}

And add these functions to same file that is below

const Routes = () => {
const Auth = React.useContext(AuthApi);
return (
<Switch>
<Route path="/register">
<Register />
</Route>
<ProtectedLogin
path="/login"
auth={Auth.auth}
component={Login}
></ProtectedLogin>
<ProtectedRoute
path="/"
auth={Auth.auth}
component={Home}
></ProtectedRoute>
</Switch>
);
};
const Home = () => {
return(
<h1>home</h1>
)
}
const Login = () => {
return(
<h1>Login</h1>
)
}
fucntion Register() {
return(
<h1>Register</h1>
)
}
const ProtectedRoute = ({ auth, component: Component, ...rest }) => {
return (
<Route
{...rest}
render={() => (auth ? <Component /> : <Redirect to="/login" />)}
/>
);
};
const ProtectedLogin = ({ auth, component: Component, ...rest }) => {
return (
<Route
{...rest}
render={() => (!auth ? <Component /> : <Redirect to="/" />)}
/>
);
};

In this whole code we have done routing and used auth so that when user is logged in he can’t see register and login.If he is not logged in then he will redirected to Login so that he can login easily. In the routes function we have three routes login,register and home as told already. Protected Login is used for switching user when he is logged in than he will only see home if not than he will only see the login he can’t access the home.We are storing auth in Authapi using context so that we can use it anywhere in the module.

Now we will do registeration first so for that we have to make form and send that formdata to /register route so change the register function by below code and also import and axios,by command npm i axios .

function Register() {
const [name, setName] = useState("");
const [company, setCompany] = useState("");
const [password, setPassword] = useState("");
const handleSubmit = (evt) => {
evt.preventDefault();
const data = {
username: name,
company: company,
password: password,
};
axios
.post("http://127.0.0.1:8000/register", data)
.then((response) => {
console.log(response);
alert(response);
})
.catch((error) => {
alert(error);
});
};
return (
<>
<form
style={{
marginTop: "100px",
marginLeft: "50px",
border: "solid 1px",
width: "max-content",
borderColor: "green",
}}
onSubmit={handleSubmit}
>
<div style={{ textAlign: "center" }}>Register Yourself</div>
<br />
<label>Username:</label>
<input
type="text"
className="username"
value={name}
onChange={(e) => setName(e.target.value)}
></input>
<br />
<br />
<label>Company: </label>
<input
type="text"
className="company"
value={company}
onChange={(e) => setCompany(e.target.value)}
></input>
<br />
<br />
<label>Password: </label>
<input
type="password"
className="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
></input>
<br />
<br />
<div style={{ textAlign: "center" }}>
<input type="submit" value="Submit" />
</div>
</form>
</>
);
}

Normal query call for post request using axios we have used three useState hooks for our form data and changing it with Onchange and on Onsubmit we handle the handleSubmit in which we do the axios post call to our backend server.

Now change the Login function as shown below for logining in and we we will also js-cookie library for storing token as cookie locally npm install js-cookie

const Login = () => {
const Auth = React.useContext(AuthApi);
const [name, setName] = useState("");
const [password, setPassword] = useState("");
const handleSubmit = async (evt) => {
if (evt) {
evt.preventDefault();
}
const data = {
username: name,
password: password,
};
const news = async () => {
let res = await axios
.post("http://127.0.0.1:8000/login", data)
.then((response) => {
console.log(response);
Cookies.set("token", response.data.access_token);
return response;
})
.catch((error) => {
console.log(error.message);
});
return res;
};
let x = await news();
if (x) {
window.location.reload();
}
};
return (
<>
<form
style={{
marginTop: "100px",
marginLeft: "50px",
border: "solid 1px",
width: "max-content",
borderColor: "green",
}}
onSubmit={handleSubmit}
>
<div style={{ textAlign: "center" }}>Login</div>
<br />
<label>Username:</label>
<input
type="text"
className="username"
value={name}
onChange={(e) => setName(e.target.value)}
></input>
<br />
<br />
<label>Password: </label>
<input
type="password"
className="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
></input>
<br />
<br />
<div style={{ textAlign: "center" }}>
<input type="submit" value="Submit" />
</div>
</form>
</>
);
};

We are storing Token in Cookie using key token so we can fetch it when needed other things are similar to register, so let’s move on so for fetching and storing token globally we will use context again and add this line at top near imports `const TokenApi = React.createContext();` after doing all changes for our token fucntion app will look like this

function App() {
const [auth, setAuth] = useState(false);
const [token, setToken] = useState("");
const readCookie = () => {
let token = Cookies.get("token");
if (token) {
setAuth(true);
setToken(token);
}
};
React.useEffect(() => {
readCookie();
}, []);
return (
<>
<AuthApi.Provider value={{ auth, setAuth }}>
<TokenApi.Provider value={{ token, setToken }}>
<Router>
<div>
<nav>
<ul>
{!auth ? (
<li>
<Link to="/register">Regsiter</Link>
</li>
) : (
<></>
)}
{!auth ? (
<li>
<Link to="/login">Login</Link>
</li>
) : (
<></>
)}
<li>
<Link to="/">Home</Link>
</li>
</ul>
</nav>
<Routes />
</div>
</Router>
</TokenApi.Provider>
</AuthApi.Provider>
</>
);
}

So we are storing cookie in TokenAPi using token,setToken so that we can use it whenver needed for authorization.

Now let us add Home function where we will do the authorization part using stored token, which will look like this.

const Home = () => {
const [data, setData] = useState("");
const Auth = React.useContext(AuthApi);
const Token = React.useContext(TokenApi);
const handleonclick = () => {
Auth.setAuth(false);
Cookies.remove("token");
};
let toke = Token.token;
const headers = {
Authorization: `Bearer ${toke}`,
};
const getdata = async () => {
let res = await axios
.get("http://127.0.0.1:8000/", { headers })
.then((response) => {
return response.data.data;
});
return res;
};
React.useEffect(async () => {
let x = await getdata();
setData(x);
console.log(x);
}, []);
return (
<>
<h2>Home</h2>
<button onClick={handleonclick}>Logout</button>
<h1>{data}</h1>
</>
);
};

Here we have logout button which simply destroys stored cookie by OnClick event callinghandleonclick() function than we have our secured route which we secured using tokens first we are finding token then defining header object and suppliying it to axios get request and storing the data inside const data then rendering it on the page.Below is the source code for this file Thanks for reading my bad english and sorry for all mistakes.

Happy Coding 😊

--

--