Starlette-Login
Repository: Starlette-Login
Starlette-Login provides user session management for Starlette.
Much inspired by Flask-Login, it handles the common tasks of logging in, logging out, and remembering your users' sessions over extended periods of time.
Key features:
- Store the active user’s ID in the session, and let you log them in and out easily.
- Restrict routes to logged-in user only
- Handle remember-me functionality
Installation
Stable
Development
Basic Example
To run example below, please install uvicorn
.
Models
User model and how to get the user by username
and id
models.py
import typing
from dataclasses import dataclass
from starlette.requests import Request
from starlette_login.mixins import UserMixin
@dataclass
class User(UserMixin):
identifier: int
username: str
password: str = 'password'
def check_password(self, password: str):
return self.password == password
@property
def is_authenticated(self) -> bool:
return True
@property
def display_name(self) -> str:
return self.username
@property
def identity(self) -> int:
return self.identifier
class UserList:
def __init__(self):
self.user_list = []
def dict_username(self) -> dict:
d = {}
for user in self.user_list:
d[user.username] = user
return d
def dict_id(self) -> dict:
d = {}
for user in self.user_list:
d[user.identity] = user
return d
def add(self, user: User) -> bool:
if user.identity in self.dict_id():
return False
self.user_list.append(user)
return True
def get_by_username(self, username: str) -> typing.Optional[User]:
return self.dict_username().get(username)
def get_by_id(self, identifier: int) -> typing.Optional[User]:
return self.dict_id().get(identifier)
def user_loader(self, request: Request, user_id: int):
return self.get_by_id(user_id)
user_list = UserList()
user_list.add(User(identifier=1, username='user1', password='password'))
user_list.add(User(identifier=2, username='user2', password='password'))
Routes
Implement login
and logout
utils and login_required
decorator
routes.py
from urllib.parse import parse_qsl
from starlette.requests import Request
from starlette.responses import (
RedirectResponse, HTMLResponse, PlainTextResponse
)
from starlette_login.decorator import login_required
from starlette_login.utils import login_user, logout_user
from models import user_list
HOME_PAGE = "You are logged in as <strong>{username}</strong>"
LOGIN_PAGE = """
<h4>{error}<h4>
<form method="POST">
<label>username <input name="username"></label>
<label>Password <input name="password" type="password"></label>
<button type="submit">Login</button>
</form>
"""
async def login_page(request: Request):
error = ''
if request.user.is_authenticated:
return RedirectResponse('/', 302)
if request.method == 'POST':
body = (await request.body()).decode()
data = dict(parse_qsl(body))
user = user_list.get_by_username(data['username'])
if not user:
error = 'Invalid username'
elif user.check_password(data['password']) is False:
error = 'Invalid password'
else:
await login_user(request, user)
return RedirectResponse('/', 302)
return HTMLResponse(LOGIN_PAGE.format(error=error))
async def logout_page(request: Request):
if request.user.is_authenticated:
content = 'Logged out'
await logout_user(request)
else:
content = 'You not logged in'
return PlainTextResponse(content)
async def home_page(request: Request):
if request.user.is_authenticated:
content = HOME_PAGE.format(username=request.user.username)
else:
content = 'You are not logged in'
return HTMLResponse(content=content)
@login_required
async def protected_page(request: Request):
return PlainTextResponse(f'You are logged in as {request.user.username}')
Starlette Application
Using AuthenticationMiddleware
,
then using the LoginManager
and set LoginManager
's user_loader
callback function by set_user_loader
method.
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.sessions import SessionMiddleware
from starlette.routing import Route
from starlette_login.backends import SessionAuthBackend
from starlette_login.login_manager import LoginManager
from starlette_login.middleware import AuthenticationMiddleware
from models import user_list
from routes import home_page, login_page, logout_page, protected_page
login_manager = LoginManager(redirect_to='login', secret_key='secret')
login_manager.set_user_loader(user_list.user_loader)
app = Starlette(
middleware=[
Middleware(SessionMiddleware, secret_key='secret'),
Middleware(
AuthenticationMiddleware,
backend=SessionAuthBackend(login_manager),
login_manager=login_manager,
allow_websocket=False,
)
],
routes=[
Route('/', home_page, name='home'),
Route('/login', login_page, methods=['GET', 'POST'], name='login'),
Route('/logout', logout_page, name='logout'),
Route('/protected', protected_page, name='protected'),
],
)
app.state.login_manager = login_manager
Run
Endpoints
- Home:
/
- Login:
/login
- Logout:
/logout
More Examples
- Basic Auth
- Token Auth: TODO
- Multiple Auth: TODO