Python Flask
https://flask.palletsprojects.com/en/2.2.x/installation/
Create an environment
C:\flask_dev>py -3 -m venv venv
Activate the environment
C:\flask_dev>venv\Scripts\activate
Install Flask
venv C:\flask_dev>pip install Flask
C:\flask_dev\flaskreact\app.py
Install requirements
Flask-JWT-Extended
Flask-JWT-Extended not only adds support for using JSON Web Tokens (JWT) to Flask for protecting routes, but also many helpful (and optional) features built in to make working with JSON Web Tokens easier.
https://pypi.org/project/Flask-JWT-Extended/
(venv) PS C:\flask_dev\flaskreact>pip install Flask-JWT-Extended
Flask-Bcrypt
Flask-Bcrypt is a Flask extension that provides bcrypt hashing utilities for your application.
https://pypi.org/project/Flask-Bcrypt/
(venv) PS C:\flask_dev\flaskreact>pip install Flask-Bcrypt
Flask-SQLAlchemy
Flask-SQLAlchemy is an extension for Flask that adds support for SQLAlchemy to your application.
https://flask-sqlalchemy.palletsprojects.com/en/3.0.x/
(venv) PS C:\flask_dev\flaskreact>pip install -U Flask-SQLAlchemy
C:\flask_dev\flaskreact\app.py
Flask-Cors
A Flask extension for handling Cross Origin Resource Sharing (CORS), making cross-origin AJAX possible.
https://pypi.org/project/Flask-Cors/
(venv) PS C:\flask_dev\flaskreact>pip install -U flask-cors
C:\flask_dev\flaskreact\app.py
#C:\flask_dev\flaskreact\app.py import json from flask import Flask, request, jsonify from datetime import datetime, timedelta, timezone from flask_jwt_extended import create_access_token,get_jwt,get_jwt_identity, unset_jwt_cookies, jwt_required, JWTManager #pip install Flask-JWT-Extended = https://pypi.org/project/Flask-JWT-Extended/ from flask_bcrypt import Bcrypt #pip install Flask-Bcrypt = https://pypi.org/project/Flask-Bcrypt/ from flask_cors import CORS #ModuleNotFoundError: No module named 'flask_cors' = pip install Flask-Cors from models import db, User api = Flask(__name__) CORS(api, supports_credentials=True) api.config['SECRET_KEY'] = 'cairocoders-ednalan' api.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///flaskdb.db' # Databse configuration Mysql Username:password@hostname/databasename #app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:''@localhost/flaskreact' api.config["JWT_ACCESS_TOKEN_EXPIRES"] = timedelta(hours=1) jwt = JWTManager(api) SQLALCHEMY_TRACK_MODIFICATIONS = False SQLALCHEMY_ECHO = True bcrypt = Bcrypt(api) db.init_app(api) with api.app_context(): db.create_all() @api.route("/") def hello_world(): return "<p>Hello, World!</p>" @api.route('/logintoken', methods=["POST"]) def create_token(): email = request.json.get("email", None) password = request.json.get("password", None) user = User.query.filter_by(email=email).first() #if email != "test" or password != "test": # return {"msg": "Wrong email or password"}, 401 if user is None: return jsonify({"error": "Wrong email or passwords"}), 401 if not bcrypt.check_password_hash(user.password, password): return jsonify({"error": "Unauthorized"}), 401 access_token = create_access_token(identity=email) #response = {"access_token":access_token} return jsonify({ "email": email, "access_token": access_token }) #return response @api.route("/signup", methods=["POST"]) def signup(): email = request.json["email"] password = request.json["password"] user_exists = User.query.filter_by(email=email).first() is not None if user_exists: return jsonify({"error": "Email already exists"}), 409 hashed_password = bcrypt.generate_password_hash(password) new_user = User(name="cairocoders Ednalan", email=email, password=hashed_password, about="sample about me") db.session.add(new_user) db.session.commit() return jsonify({ "id": new_user.id, "email": new_user.email }) @api.after_request def refresh_expiring_jwts(response): try: exp_timestamp = get_jwt()["exp"] now = datetime.now(timezone.utc) target_timestamp = datetime.timestamp(now + timedelta(minutes=30)) if target_timestamp > exp_timestamp: access_token = create_access_token(identity=get_jwt_identity()) data = response.get_json() if type(data) is dict: data["access_token"] = access_token response.data = json.dumps(data) return response except (RuntimeError, KeyError): # Case where there is not a valid JWT. Just return the original respone return response @api.route("/logout", methods=["POST"]) def logout(): response = jsonify({"msg": "logout successful"}) unset_jwt_cookies(response) return response @api.route('/profile/<getemail>') @jwt_required() def my_profile(getemail): print(getemail) if not getemail: return jsonify({"error": "Unauthorized Access"}), 401 user = User.query.filter_by(email=getemail).first() response_body = { "id": user.id, "name": user.name, "email": user.email, "about" : user.about } return response_bodyC:\flask_dev\flaskreact\models.py
#C:\flask_dev\flaskreact\models.py from flask_sqlalchemy import SQLAlchemy from uuid import uuid4 db = SQLAlchemy() def get_uuid(): return uuid4().hex class User(db.Model): __tablename__ = "users" id = db.Column(db.String(11), primary_key=True, unique=True, default=get_uuid) name = db.Column(db.String(150), unique=True) email = db.Column(db.String(150), unique=True) password = db.Column(db.Text, nullable=False) about = db.Column(db.Text, nullable=False)Postman
Postman is an API platform for building and using APIs. Postman simplifies each step of the API lifecycle and streamlines collaboration so you can create better APIs—faster.
https://www.postman.com/
Login
Post : http://127.0.0.1:5000/logintoken
Body - row - json
{
"email":"cairocoders@gmail.com",
"password":"123456"
}
Sign Up
Post : http://127.0.0.1:5000/signup
Body - row - json
{
"email":"cairocoders@gmail.com",
"password":"123456"
}
Profile
Get : http://127.0.0.1:5000/profile/cairocoders@gmail.com
Headers :
Key : Authorization
value : Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTY
Log Out
Get : http://127.0.0.1:5000/logout
run (venv) C:\flask_dev\flaskreact>flask run
React JS
https://create-react-app.dev/
Create Project
C:\react-js>npx create-react-app myreactdev
Run
C:\react-js\myreactdev> npm start
C:\react-js\myreactdev\src\App.js
//C:\react-js\myreactdev\src\App.js import React, { } from 'react'; import './App.css'; import {BrowserRouter, Routes, Route} from 'react-router-dom'; import Login from './components/Login' import Header from './components/Header' import Profile from './components/Profile' import useToken from './components/useToken' function App() { const { token, removeToken, setToken } = useToken(); return ( <div className="vh-100 gradient-custom"> <div className="container"> <h1 className="page-header text-center">React-JS and Python Flask Login Token Authentication flask_jwt_extended with Profile | SQLAlchemy.</h1> <BrowserRouter> <Header token={removeToken}/> {!token && token!=="" &&token!== undefined? <Login setToken={setToken} /> :( <> <Routes> <Route exact path="/profile" element={<Profile token={token} setToken={setToken}/>}></Route> </Routes> </> )} </BrowserRouter> </div> </div> ); } export default App;Install React Router Dom
https://www.npmjs.com/package/react-router-dom
C:\react-js\myreactdev>npm i react-router-dom --save
Install Axios
https://www.npmjs.com/package/axios
C:\react-js\myreactdev>npm install axios --save
C:\react-js\myreactdev\src\components\Login.js
//C:\react-js\myreactdev\src\components\Login.js import React, { useState } from "react"; import axios from "axios"; import { useNavigate } from "react-router-dom"; function Login(props) { const [loginForm, setloginForm] = useState({ email: "", password: "" }) const navigate = useNavigate(); function btnlogin(event) { axios({ method: "POST", url:"http://127.0.0.1:5000/logintoken", data:{ email: loginForm.email, password: loginForm.password } }) .then((response) => { console.log(response) props.setToken(response.data.access_token) alert("Successfully Login"); localStorage.setItem('email', loginForm.email) navigate('/profile'); }).catch((error) => { if (error.response) { console.log(error.response) console.log(error.response.status) console.log(error.response.headers) if (error.response.status === 401) { alert("Invalid credentials"); } } }) setloginForm(({ email: "", password: ""})) event.preventDefault() } function handleChange(event) { const {value, name} = event.target setloginForm(prevNote => ({ ...prevNote, [name]: value}) )} let imgs = [ 'https://as1.ftcdn.net/v2/jpg/03/39/70/90/1000_F_339709048_ZITR4wrVsOXCKdjHncdtabSNWpIhiaR7.jpg', ]; return ( <div> <div className="container h-50"> <div className="container-fluid h-custom"> <div className="row d-flex justify-content-center align-items-center h-50"> <div className="col-md-9 col-lg-6 col-xl-5"> <img src={imgs[0]} className="img-fluid"/> </div> <div className="col-md-8 col-lg-6 col-xl-4 offset-xl-1"> <form> <div className="d-flex flex-row align-items-center justify-content-center justify-content-lg-start"> <p className="lead fw-normal mb-0 me-3">Log Into Your Account</p> </div> <div className="form-outline mb-4"> <input type="email" value={loginForm.email} onChange={handleChange} text={loginForm.email} name="email" id="form3Example3" className="form-control form-control-lg" placeholder="Enter a valid email address" /> <label className="form-label" for="form3Example3">Email address</label> </div> <div className="form-outline mb-3"> <input type="password" value={loginForm.password} onChange={handleChange} text={loginForm.password} name="password" id="form3Example4" className="form-control form-control-lg" placeholder="Enter password" /> <label className="form-label" for="form3Example4">Password</label> </div> <div className="d-flex justify-content-between align-items-center"> <div className="form-check mb-0"> <input className="form-check-input me-2" type="checkbox" value="" id="form2Example3" /> <label className="form-check-label" for="form2Example3"> Remember me </label> </div> <a href="#!" className="text-body">Forgot password?</a> </div> <div className="text-center text-lg-start mt-4 pt-2"> <button type="button" className="btn btn-primary btn-lg" onClick={btnlogin} >Login</button> <p className="small fw-bold mt-2 pt-1 mb-0">Don't have an account? <a href="/register" className="link-danger">Register</a></p> </div> </form> </div> </div> </div> </div> </div> ); } export default Login;C:\react-js\myreactdev\src\components\Header.js
//C:\react-js\myreactdev\src\components\Header.js import React, { } from "react"; import axios from "axios"; import {useNavigate} from "react-router-dom"; function Header(props) { const navigate = useNavigate(); function logMeOut() { axios({ method: "POST", url:"http://127.0.0.1:5000/logout", }) .then((response) => { props.token() localStorage.removeItem('email') navigate("/"); }).catch((error) => { if (error.response) { console.log(error.response) console.log(error.response.status) console.log(error.response.headers) } }) } const logged = localStorage.getItem('email'); return( <nav className="navbar navbar-expand-lg bg-light"> <div className="container-fluid"> <a className="navbar-brand" href="#">Cairocoders</a> <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span className="navbar-toggler-icon"></span> </button> <div className="collapse navbar-collapse" id="navbarSupportedContent"> <ul className="navbar-nav me-auto mb-2 mb-lg-0"> <li className="nav-item"> <a className="nav-link active" aria-current="page" href="#">Home</a> </li> <li className="nav-item"> <a className="nav-link" href="#">About</a> </li> </ul> {!logged? <button className="btn btn-outline-success" type="submit">Login</button> :<button className="btn btn-outline-danger" type="submit" onClick={logMeOut}>Logout</button>} </div> </div> </nav> ) } export default Header;C:\react-js\myreactdev\src\components\useToken.js
//C:\react-js\myreactdev\src\components\useToken.js import { useState } from 'react'; function useToken() { function getToken() { const userToken = localStorage.getItem('token'); //https://javascript.info/localstorage return userToken && userToken } const [token, setToken] = useState(getToken()); function saveToken(userToken) { localStorage.setItem('token', userToken); setToken(userToken); }; function removeToken() { localStorage.removeItem("token"); setToken(null); } return { setToken: saveToken, token, removeToken } } export default useToken;C:\react-js\myreactdev\src\components\Profile.js
//C:\react-js\myreactdev\src\components\Profile.js import React, { useState, useEffect } from "react"; import axios from "axios"; function Profile(props) { const [profileData, setProfileData] = useState(null) useEffect(() => { getUsers(); }, []); const email = localStorage.getItem('email'); function getUsers() { axios({ method: "GET", url:`http://127.0.0.1:5000/profile/${email}`, headers: { Authorization: 'Bearer ' + props.token } }) .then((response) => { console.log(response) const res =response.data res.access_token && props.setToken(res.access_token) setProfileData(({ profile_name: res.name, profile_email: res.email, about_me: res.about})) }).catch((error) => { if (error.response) { console.log(error.response) console.log(error.response.status) console.log(error.response.headers) } }) } let imgs = [ 'https://mdbcdn.b-cdn.net/img/Photos/new-templates/bootstrap-chat/ava1-bg.webp', ]; return ( <div className="container"> <div className="row d-flex justify-content-center align-items-center h-50"> <div className="col col-lg-12"> <div className="card mb-3"> {profileData && <div className="row g-0"> <div className="col-md-4 bg-c-lite-green text-center text-white"> <img src={imgs[0]} className="img-fluid my-5" width="150"/> <h5>{profileData.profile_name}</h5> <p>Coder</p> <i className="far fa-edit mb-5"></i> </div> <div className="col-md-8"> <div className="card-body p-4"> <h6>Your profile details:</h6> <div className="row pt-1"> <div className="col-6 mb-3"> <h6>Email</h6> <p className="text-muted">{profileData.profile_email}</p> </div> <div className="col-6 mb-3"> <h6>Phone</h6> <p className="text-muted">123 456 789</p> </div> </div> <h6>About</h6> <div className="d-flex justify-content-start"> {profileData.about_me} </div> </div> </div> </div> } </div> </div> </div> </div> ); } export default Profile;react-js\myreactdev\src\App.css
//react-js\myreactdev\src\App.css body { margin: 0; background-color: #ffffff; } .h-50 { margin-top:50px; } .bg-c-lite-green { background: -webkit-gradient(linear, left top, right top, from(#f29263), to(#ee5a6f)); background: linear-gradient(to right, #ee5a6f, #f29263); }react-js\myreactdev\public\index.html
//react-js\myreactdev\public\index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="theme-color" content="#000000" /> <meta name="description" content="Web site created using create-react-app" /> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <title>React App</title> <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"/> </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"></div> </body> </html>Run C:\react-j\myreactdev>npm start
http://localhost:3000/