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 = () =&gt; {
        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;
This is the result
After logging in with test@test.se and password 123

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

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.