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_p32jWDk0Create 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 userBootstrap 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