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_body
C:\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/
