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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | #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 |
loginregister/connection.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #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" 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() |
loginregister/models.py
1 2 3 4 5 6 7 8 9 10 11 12 13 | #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" ) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | #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 """ , ) # send email with smtplib.SMTP_SSL( 'smtp.gmail.com' , 465 ) as smtp: smtp.login(email_address, email_password) smtp.send_message(msg) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #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" |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | #loginregister/scurity.py from passlib.context import CryptContext #pip install passlib from fastapi.security import OAuth2PasswordBearer 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 |
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
1 2 3 4 5 6 7 8 9 10 11 12 | //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 %} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | //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> |
1 2 3 4 5 6 7 8 9 10 11 12 | //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 %} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | //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 %} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | //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 %} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | //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 %} |
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