A simple JWT example with NodeJS and Python (part 2)
A continuation of https://blogg.fsh.se/2022/06/06/a-simple-jwt-example-with-nodejs-and-python-part-1/
This article describes the creation of a simple React client logging into the backend using JWT.
The React client
Keep track of the JWT token in a state variable. This would be better saved in the browsers local storage,
import './App.css';
import { useState } from 'react';
import "./styles.css";
function App() {
const [token, setToken] = useState(0)
const [text, setText] = useState({ text: "", color: "black" })
Handler code for the submit button. Standard fetch() with a handler that receives the JWT token.
const handleSubmit = async (event) => {
event.preventDefault();
console.log(event);
// Prevent page reload
const email_value = event.target.name.value
const password_value = event.target.password.value
const info = {
email: email_value,
password: password_value
}
try {
await fetch("http://192.168.1.67:5000/login",
{
method: "POST",
body: JSON.stringify(info),
headers: {
"Content-Type": "application/json;charset=UTF-8",
"Accept": "application/json"
}
})
.then((response) => {
const statusCode = response.status;
const data = response.json();
return Promise.all([statusCode, data]);
})
.then((data) => {
// This is how the backend returns the data
data = data[1];
// You could also check the status code here but the token is important
if (data.access_token) {
setToken(data.access_token);
// localStorage.setItem("access_token", data.access_token); // Better!
setText({
text: "Sucessfully logged in!",
color: "green"
});
} else {
setText({
text: "Incorrect email or password",
color: "red"
});
}
});
} catch (err) {
console.error(err);
}
};
Code for handling a click on the Profile button. Just makes a call to a protected entry point so a valid JWT token is needed!
const handleProfileClick = (event) => {
event.preventDefault();
try {
fetch("http://192.168.1.67:5000/profile",
{
method: "GET",
headers: {
"Content-Type": "application/json;charset=UTF-8",
"Accept": "application/json",
"Authorization": "Bearer " + token
}
})
.then((response) => {
const statusCode = response.status;
console.log(statusCode)
const data = response.json();
console.log(data)
return Promise.all([statusCode, data]);
})
.then((data) => {
console.log(data);
data = data[1]
if (data.about) {
setText({ text: data.about, color: "black" })
}
});
} catch (err) {
console.error(err);
}
};
Handler for the Logout buttton. The backend will kill the token, making it ununsable.
const handleLogout = (event) => {
event.preventDefault();
try {
fetch("http://192.168.1.67:5000/logout",
{
method: "POST",
headers: {
"Authorization": "Bearer " + token
}
})
.then((response) => {
const statusCode = response.status;
const data = response.json();
return Promise.all([statusCode, data]);
})
.then((data) => {
console.log(data)
localStorage.removeItem("access_token");
setToken(0);
setText({
text: "Succesfully logged out!",
color: "green"
})
})
} catch (err) {
console.log(err);
}
};
Renderers of the logged in state (just a test message) and the not-logged-in state (a login form)
The styles CSS that makes the login form nice to look at!
styles.css
.app {
font-family: sans-serif;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
gap: 20px;
height: 100vh;
font-family: Cambria, Cochin, Georgia, Times, "Times New Roman", serif;
background-color: #f8f9fd;
}
input[type="text"],
input[type="password"] {
height: 25px;
border: 1px solid rgba(0, 0, 0, 0.2);
}
input[type="submit"] {
margin-top: 10px;
cursor: pointer;
font-size: 15px;
background: #01d28e;
border: 1px solid #01d28e;
color: #fff;
padding: 10px 20px;
}
input[type="submit"]:hover {
background: #6cf0c2;
}
.button-container {
display: flex;
justify-content: center;
}
.login-form {
background-color: white;
padding: 2rem;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
.list-container {
display: flex;
}
.error {
color: red;
font-size: 12px;
}
.title {
font-size: 25px;
margin-bottom: 20px;
}
.input-container {
display: flex;
flex-direction: column;
gap: 8px;
margin: 10px;
}
const RenderLoggedInState = () => {
return <div>
<h1>You are logged in!</h1>
<button onclick="{handleProfileClick}">Profile</button>
<button onclick="{handleLogout}">Logout</button>
</div>
}
const renderForm = () => {
return <div classname="login-form">
<div classname="form">
<div classname="title">Sign In</div>
<form onsubmit="{handleSubmit}">
<div classname="input-container">
<label>Username </label>
<input type="text" name="name">
</div>
<div classname="input-container">
<label>Password</label>
<input type="password" name="password">
</div>
<div classname="button-container">
<input type="submit" value="Login">
</div>
</form>
</div>
<div>
<h5>Do you have an invitation code? If so, signup here!</h5>
</div>
</div>
}
return (<div classname="app">
{token === 0 ? renderForm() : RenderLoggedInState()}
{text.text}
</div>);
}
export default App;


Complete code found here: https://github.com/fshsweden/login-frontend-test