Install Fastapi
https://github.com/tiangolo/fastapi
pip install fastapi
C:\fastAPI\loginregister>pip install fastapi
C:\fastAPI\loginregister>pip install "uvicorn[standard]"
Install sqlalchemy jinja2
https://pypi.org/project/Jinja2/
pip install python-multipart sqlalchemy jinja2
C:\fastAPI\loginregister>pip install python-multipart sqlalchemy jinja2
Create main.py
loginregister/main.py
#loginregister/main.py
from fastapi import FastAPI, Request, APIRouter,Depends,Form,HTTPException,Response
from starlette.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
from connection import Base,engine, sess_db
from sqlalchemy.orm import Session
from scurity import get_password_hash,verify_password, create_access_token, verify_token, COOKIE_NAME
from starlette.responses import RedirectResponse
# Repository
from repositoryuser import UserRepository, SendEmailVerify
# Model
from models import UserModel
templates = Jinja2Templates(directory="templates")
app = FastAPI()
app.mount("/static",StaticFiles(directory="static",html=True),name="static")
#db engin
Base.metadata.create_all(bind=engine)
@app.get("/")
def home(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
@app.get("/about")
def home(request: Request):
return templates.TemplateResponse("about.html", {"request": request})
@app.get("/user/signup")
def signup(req: Request):
return templates.TemplateResponse("signup.html", {"request": req})
@app.post("/signupuser")
def signup_user(db:Session=Depends(sess_db),username : str = Form(),email:str=Form(),password:str=Form()):
print(username)
print(email)
print(password)
userRepository=UserRepository(db)
db_user= userRepository.get_user_by_username(username)
if db_user:
return "username is not valid"
signup=UserModel(email=email,username=username,password=get_password_hash(password))
success=userRepository.create_user(signup)
token=create_access_token(signup)
SendEmailVerify.sendVerify(token)
if success:
return "create user successfully"
else:
raise HTTPException(
status_code=401, detail="Credentials not correct"
)
@app.get("/user/signin")
def login(req: Request):
return templates.TemplateResponse("/signin.html", {"request": req})
@app.post("/signinuser")
def signin_user(response:Response,db:Session=Depends(sess_db),username : str = Form(),password:str=Form()):
userRepository = UserRepository(db)
db_user = userRepository.get_user_by_username(username)
if not db_user:
return "username or password is not valid"
if verify_password(password,db_user.password):
token=create_access_token(db_user)
response.set_cookie(
key=COOKIE_NAME,
value=token,
httponly=True,
expires=1800
)
return {COOKIE_NAME:token,"token_type":"cairocoders"}
@app.get('/user/verify/{token}')
def verify_user(token,db:Session=Depends(sess_db)):
userRepository=UserRepository(db)
payload=verify_token(token)
username=payload.get("username")
db_user=userRepository.get_user_by_username(username)
if not username:
raise HTTPException(
status_code=401, detail="Credentials not correct"
)
if db_user.is_active==True:
return "your account has been allreay activeed"
db_user.is_active=True
db.commit()
response=RedirectResponse(url="/user/signin")
return response
#http://127.0.0.1:8000/user/verify/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImNseWRleTAxMzEiLCJlbWFpbCI6ImNseWRleUBnbWFpbC5jb20iLCJyb2xlIjoidXNlciIsImFjdGl2ZSI6ZmFsc2V9.BKektCLzr47qn-fRtnGVulSdYlcMdemJQO_p32jWDk0
Create database Connection sqlalchemy loginregister/connection.py
#loginregister/connection.py
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from typing import Optional
#dbcon = "postgresql://user:password@postgresserver/db"
dbcon = 'sqlite:///fastapidb.sqlite3'
engine = create_engine(dbcon)
SessionFactory = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
Base.metadata.create_all(bind=engine)
DATABASE_URL: Optional[str] = None
SECRET_KEY: Optional[str] = "cairocoders"
def sess_db():
db = SessionFactory()
try:
yield db
finally:
db.close()
Create database Model sqlalchemy loginregister/models.py
#loginregister/models.py
from sqlalchemy import Column,String,Integer,Boolean,Enum
from schema import Roles
from connection import Base
class UserModel(Base):
__tablename__ = "users"
id=Column(Integer,primary_key=True,index=True)
email=Column(String,unique=True,index=True)
username=Column(String,unique=True,index=True)
password=Column(String,unique=False,index=True)
is_active=Column(Boolean,default=False)
role=Column(Enum(Roles),default="user")
loginregister/repositoryuser.py
#loginregister/repositoryuser.py
from sqlalchemy.orm import Session
from models import UserModel
from typing import Dict,Any
from sqlalchemy.orm import Session
import smtplib
from email.message import EmailMessage
class UserRepository:
def __init__(self,sess:Session):
self.sess: Session=sess
def create_user(self,signup:UserModel) -> bool:
try:
self.sess.add(signup)
self.sess.commit()
except:
return False
return True
def get_user(self):
return self.sess.query(UserModel).all()
def get_user_by_username(self,username:str):
return self.sess.query(UserModel).filter(UserModel.username==username).first()
def update_user(self,id:int,details:Dict[str,Any]) -> bool:
try:
self.sess.query(UserModel).filter(UserModel.id==id).update(details)
self.sess.commit()
except:
return False
return True
def delete_user(self,id:int)-> bool:
try:
self.sess.query(UserModel).filter(UserModel.id==id).delete()
self.sess.commit()
except:
return False
return True
class SendEmailVerify:
def sendVerify(token):
email_address = "cairocoders0711@gmail.com" # type Email
email_password = "cairocoders-ednalan" # If you do not have a gmail apps password, create a new app with using generate password. Check your apps and passwords https://myaccount.google.com/apppasswords
# create email
msg = EmailMessage()
msg['Subject'] = "Email subject"
msg['From'] = email_address
msg['To'] = "clydeymojica0130@gmail.com" # type Email
msg.set_content(
f"""\
verify account
http://localhost:8080/user/verify/{token}
""",
)
# send email
with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp:
smtp.login(email_address, email_password)
smtp.send_message(msg)
loginregister/schema.py
#loginregister/schema.py
from datetime import date
from pydantic import BaseModel, EmailStr #pip install pydantic[email]
from enum import Enum
from fastapi import Form
class UserSchema(BaseModel):
email: EmailStr
username: str
password: str
class Config:
orm_mode = True
class Roles(Enum):
user = "user"
admin = "admin"
loginregister/scurity.py
#loginregister/scurity.py
from passlib.context import CryptContext #pip install passlib
from fastapi.security import OAuth2PasswordBearer
from jose import jwt #https://pypi.org/project/python-jose/ = pip install python-jose
from fastapi import Depends,Request
from fastapi import HTTPException
from models import UserModel
JWT_SECRET="cairocoders$§%§$Ednalan"
ALGORITHM="HS256"
ACCESS_TOKEN_EXPIRE_MINUTES=3000
pwd_context=CryptContext(schemes=["bcrypt"],deprecated="auto") #pip install bcrypt
# save token to oauth2_scheme
oauth2_scheme=OAuth2PasswordBearer(tokenUrl="user/signin")
COOKIE_NAME="Authorization"
# create Token
def create_access_token(user):
try:
payload={
"username":user.username,
"email":user.email,
"role":user.role.value,
"active":user.is_active,
}
return jwt.encode(payload,key=JWT_SECRET,algorithm=ALGORITHM)
except Exception as ex:
print(str(ex))
raise ex
# create verify Token
def verify_token(token):
try:
payload=jwt.decode(token,key=JWT_SECRET)
return payload
except Exception as ex:
print(str(ex))
raise ex
# password hash
def get_password_hash(password):
return pwd_context.hash(password)
# password verify
def verify_password(plain_password,hashed_password):
return pwd_context.verify(plain_password,hashed_password)
def get_current_user_from_token(token:str=Depends(oauth2_scheme)):
user= verify_token(token)
return user
def get_current_user_from_cookie(request:Request) -> UserModel:
token=request.cookies.get(COOKIE_NAME)
if token:
user = verify_token(token)
return user
Bootstrap 5 https://getbootstrap.com/docs/5.0/getting-started/introduction/
https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css
loginregister/templates/index.html
//loginregister/templates/index.html
{% extends "base.html" %}
{% block title %}Home Page{% endblock %}
{% block content %}
<div class="event">
<p><h1>Welcome to the home page.</h1></p>
</div>
{% endblock %}
loginregister/templates/base.html
//loginregister/templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', path='/style.css')}}"/>
<title>{% block title %} My Webpage {% endblock %}</title>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/about">About</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/user/signin">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/user/signup">Sign Up</a>
</li>
</ul>
</div>
</nav>
<div class="container">
<div class="row"><p><h3>FastAPI Login Register User Token Authentication with Password Hashing and Send Email SMTP</h3></p>
{% block content %}{% endblock %}
</div>
</div>
</body>
</html>
loginregister/templates/about.html
//loginregister/templates/about.html
{% extends "base.html" %}
{% block title %}About Page{% endblock %}
{% block content %}
<div class="event">
<p><h1> About page.</h1></p>
</div>
{% endblock %}
loginregister/templates/signin.html
//loginregister/templates/signin.html
{% extends "base.html" %}
{% block title %}Login Page{% endblock %}
{% block content %}
<section style="margin-top: 50px;">
<div class="container h-custom">
<div class="row d-flex justify-content-center align-items-center h-100">
<div class="col-lg-6">
<img src="{{ url_for('static', path='/login.png')}}" class="img-fluid" alt="Sample image">
</div>
<div class="col-lg-6 col-md-6">
<form action="/signinuser" method="post">
<div class="d-flex flex-row align-items-center justify-content-center justify-content-lg-start">
<p class="lead fw-normal mb-0 me-3">Sign in with</p>
<button type="button" class="btn btn-primary btn-floating mx-1">
<i class="fab fa-facebook-f"></i>
</button>
<button type="button" class="btn btn-primary btn-floating mx-1">
<i class="fab fa-twitter"></i>
</button>
<button type="button" class="btn btn-primary btn-floating mx-1">
<i class="fab fa-linkedin-in"></i>
</button>
</div>
<div class="divider d-flex align-items-center my-4">
<p class="text-center fw-bold mx-3 mb-0">Or</p>
</div>
<div class="form-outline mb-4">
<input type="text" id="username" class="form-control form-control-lg" name="username" placeholder="User Name" type="text" value="{{ username }}" />
<label class="form-label" for="username">User Name</label>
</div>
<div class="form-outline mb-3">
<input type="password" id="pass" name="password" value="{{ password }}" class="form-control form-control-lg" placeholder="Enter password" />
<label class="form-label" for="pass">Password</label>
</div>
<div class="d-flex justify-content-between align-items-center">
<div class="form-check mb-0">
<input class="form-check-input me-2" type="checkbox" value="" id="remme" />
<label class="form-check-label" for="remme">Remember me</label>
</div>
<a href="#!" class="text-body">Forgot password?</a>
</div>
<div class="text-center text-lg-start mt-4 pt-2">
<button type="submit" class="btn btn-primary btn-lg" style="padding-left: 2.5rem; padding-right: 2.5rem;">Login</button>
<p class="small fw-bold mt-2 pt-1 mb-0">Don't have an account? <a href="/user/signup" class="link-danger">Register</a></p>
</div>
</form>
</div>
</div>
</div>
</section>
{% endblock %}
loginregister/templates/signup.html
//loginregister/templates/signup.html
{% extends "base.html" %}
{% block title %}Sign Up Page{% endblock %}
{% block content %}
<section style="margin-top: 50px;">
<div class="container h-custom">
<div class="row d-flex justify-content-center align-items-center h-100">
<div class="col-lg-6">
<img src="{{ url_for('static', path='/reg.svg')}}" class="img-fluid" alt="Sample image">
</div>
<div class="col-lg-6 col-md-6">
<form action="/signupuser" method="post">
<div class="d-flex flex-row align-items-center justify-content-center justify-content-lg-start">
<p class="lead fw-normal mb-2 me-3">CREATE AN ACCOUNT</p>
</div>
<div class="form-outline mb-4">
<input type="text" id="username" class="form-control form-control-lg" name="username" placeholder="User Name" value="{{ username }}" />
<label class="form-label" for="username">User Name</label>
</div>
<div class="form-outline mb-4">
<input type="email" id="mail" class="form-control form-control-lg" name="email" value="{{ email }}" placeholder="Enter a valid email address" />
<label class="form-label" for="mail">Email address</label>
</div>
<div class="form-outline mb-3">
<input type="password" id="pass" name="password" value="{{ password }}" class="form-control form-control-lg" placeholder="Enter password" />
<label class="form-label" for="pass">Password</label>
</div>
<div class="text-center text-lg-start mt-4 pt-2">
<button type="submit" class="btn btn-success btn-lg" style="padding-left: 2.5rem; padding-right: 2.5rem;">Register</button>
<p class="small fw-bold mt-2 pt-1 mb-0">I am already a member? <a href="/user/signin" class="link-danger">Login</a></p>
</div>
</form>
</div>
</div>
</div>
</section>
{% endblock %}
loginregister/templates/verify.html
//loginregister/templates/verify.html
{% extends "base.html" %}
{% block title %}Contact Page{% endblock %}
{% block content %}
<div class="wrapper">
<div class="contacts">
<h3> verify your account</h3>
</div>
<div class="form">
<h3>Sign in to your email to verify your account</h3>
<form method="post">
<div>
<a class="signin__link" href="/user/signin">back to Login page</a>
</div>
</form>
</div>
</div>
{% endblock %}
run the FastAPI app C:\fastAPI\loginregister>uvicorn main:app --reload
with uvicorn using the file_name:app_instance open the link on the browser http://127.0.0.1:8000/
http://127.0.0.1:8000/docs
