Server-Side Scripting/Cookies and Sessions/Python (FastAPI)
Appearance
main.py
[edit | edit source]# Demonstrates a server-side website using session variables.
#
# References:
# https://fastapi.tiangolo.com/tutorial/bigger-applications/
# https://fastapi.tiangolo.com/tutorial/static-files/
# https://github.com/encode/starlette/blob/master/tests/middleware/test_session.py
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
import uvicorn
from starlette.middleware.sessions import SessionMiddleware
import os
from routers import lesson12
app = FastAPI()
# 256-bit key secures session variables
app.add_middleware(SessionMiddleware, secret_key=os.urandom(32))
app.include_router(lesson12.router)
app.mount("/", StaticFiles(directory="static", html = True), name="static")
if __name__ == "__main__":
uvicorn.run("main:app", host="0.0.0.0", port=5000)
routers/lesson12.py
[edit | edit source]# Demonstrates session and cookie processing. The username is stored
# as a cookie and an internal userid is saved in a session variable.
# Also demonstrates secure password authentication using bcrypt salt
# and hash.
#
# References:
# https://en.wikibooks.org/wiki/Python_Programming
# https://fastapi.tiangolo.com/tutorial/cookie-params/
# https://fastapi.tiangolo.com/advanced/response-cookies/
# http://zetcode.com/python/bcrypt/
from fastapi import APIRouter, Request, Response
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.templating import Jinja2Templates
import bcrypt
users = [
# Password is the same as the username, just salted and hashed.
# Don't do this in a production application! Use custom passwords.
{ "userid": 1, "username": "admin",
"password": b'$2b$12$6xEcJ9bCRo3JgNWyn32fwuSoRh1pg8f81jjHpYq6NQ9Y8uDkhWOE6'},
{ "userid": 2, "username": "test",
"password": b'$2b$12$UZLEFMg9ez.n88Sjpb/ZN.VVlmyPPxHOeL/DE452Si4H3PSQSB0Pa'}
]
router = APIRouter(prefix="/lesson12")
templates = Jinja2Templates(directory="templates")
@router.get("/", response_class=HTMLResponse)
async def get_lesson12(request: Request):
username = request.cookies.get("username")
userid = request.session.get("userid")
return build_response(request, username, userid)
@router.post("/", response_class=HTMLResponse)
async def post_lesson12(request: Request, response: Response):
username = request.cookies.get("username")
userid = request.session.get("userid")
form = dict(await request.form())
if form.get("reload"):
return RedirectResponse(request.url)
if form.get("log-out"):
request.session.clear()
userid = None
return build_response(request, username, userid)
if form.get("forget-me"):
request.session.clear()
response = build_response(request, None, None)
response.set_cookie("username", "", expires=0)
return response
if form.get("log-in"):
username = form.get("username")
password = form.get("password")
userid = authenticate_user(username, password)
if not userid:
return RedirectResponse(request.url, status_code=303)
request.session["userid"] = userid
response = build_response(request, username, userid)
response.set_cookie("username", username)
return response
return build_response(request, username, userid)
def build_response(request: Request, username, userid):
cookie = str(bool(username))
session = str(bool(userid))
welcome = build_welcome(username, userid)
if not username:
username = ""
return templates.TemplateResponse(
"lesson12.html",
{
"request": request,
"cookie": cookie,
"session": session,
"welcome": welcome,
"username": username
}
)
def build_welcome(username, userid):
if username and userid:
welcome = f"Welcome back {username}! You are logged in."
elif username:
welcome = f"Welcome back {username}! Please log in."
else:
welcome = "Welcome! Please log in."
return welcome
def authenticate_user(username, password):
for user in users:
if user["username"] == username:
result = bcrypt.checkpw(password.encode(), user["password"])
if result:
# should track successful logins
return user["userid"]
else:
# Should track failed attempts, lock account, etc.
return None
return None
def generate_hashed_password(password):
# Use this function to generate hashed passwords to save in
# the users list or a database.
salt = bcrypt.gensalt()
hashed = bcrypt.hashpw(password, salt)
return hashed
templates/lesson12.html
[edit | edit source]<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Lesson 12</title>
<link rel="stylesheet" href="{{url_for('static', path='/styles.css')}}">
</head>
<body>
<h1>Cookies and Sessions</h1>
<p>Cookie: {{cookie}}<br>Session: {{session}}</p>
<p>{{welcome}}</p>
<hr>
<form method="POST">
<p><label for="username">Username:</label>
<input type="text" id="username" name="username" value="{{username}}">
</p>
<p><label for="password">Password:</label>
<input type="password" id="password" name="password">
</p>
<input type="submit" id="log-in" name="log-in" value="Log In">
<input type="submit" id="log-out" name="log-out" value="Log Out">
<input type="submit" id="forget-me" name="forget-me" value="Forget Me">
<input type="submit" id="reload" name="Reload" value="Reload">
</form>
</body>
</html>
Try It
[edit | edit source]See Server-Side Scripting/Routes and Templates/Python (FastAPI) to create a test environment.