microdot.login — процес входу користувача

Обгортка вищого рівня навколо microdot.session, що реалізує стандартний процес входу за іменем користувача та паролем: зворотний виклик user loader відображає збережений у сесії ідентифікатор користувача назад до об’єкта користувача застосунку, декоратори маршрутів перенаправляють неавтентифіковані запити на налаштовану URL-адресу входу, а необов’язкові куки «запам’ятати мене» дозволяють повторним відвідувачам залишатися авторизованими між сесіями браузера.

Вимагає попередньої ініціалізації microdot.session на застосунку до створення об’єкта входу — саме у сесії зберігається ідентифікатор користувача.

class Login

class microdot.login.Login(login_url: str = '/login')
login_url

URL, на яку декоратор перенаправляє неавтентифіковані запити. Застосунок надає фактичну форму/обробник входу за цим маршрутом. За замовчуванням '/login'.

user_loader(f)

Декоратор, що реєструє зворотний виклик вирішення користувача. f приймає ідентифікатор користувача, збережений у сесії, і повертає об’єкт користувача (або None, якщо ідентифікатор більше не дійсний — видалений акаунт, відкликана сесія тощо).

login = Login()

@login.user_loader
async def load_user(user_id):
    return users.get(user_id)
__call__(f)

Декорування маршруту екземпляром Login захищає його за автентифікацією:

@app.get('/dashboard')
@login
async def dashboard(request):
    user = request.g.current_user
    # ...

Неавтентифіковані запити перенаправляються до login_url з параметром запиту ?next=<original-url>, щоб обробник входу міг повернути користувача туди, куди він хотів потрапити.

fresh(f)

Подібно до __call__(), але відхиляє сесії, відновлені з куки «запам’ятати мене» — користувач повинен був увійти явно після останнього повного входу. Використовується для захисту чутливих маршрутів (зміна пароля, видалення акаунта), щоб викрадена кука запам’ятовування не могла до них дістатися.

async login_user(request, user, remember: bool | int = False, redirect_url: str = '/')

Позначає user як авторизованого для request. Зберігає ідентифікатор користувача у сесії та повертає відповідь-перенаправлення.

user

Об’єкт користувача, повернутий завантажувачем користувача. Повинен мати атрибут id.

remember

Якщо правдиве, також встановлює довготривалу куку _remember. Передайте True для типових 30 днів або ціле число для кількості днів дії куки.

redirect_url

Куди перенаправляти після входу. Перевизначається ?next=<url> з вихідного запиту, якщо він вказує на шлях того самого сайту.

Повертає відповідь-перенаправлення — поверніть її значення з обробника маршруту входу.

async logout_user(request)

Очищає ідентифікатор користувача із сесії та видаляє будь-яку куку _remember. Використовується у маршруті /logout

@app.post('/logout')
async def logout(request):
    await login.logout_user(request)
    return redirect('/')
async get_current_user(request)

Повертає поточний авторизований об’єкт користувача (або None). Кешується у request.g.current_user, тому повторні виклики в межах одного запиту звертаються до завантажувача лише один раз.

Приклад

Мінімальний процес входу:

from microdot import Microdot, redirect
from microdot.session import Session
from microdot.login import Login

app = Microdot()
Session(app, secret_key=load_secret())
login = Login()

@login.user_loader
async def load_user(user_id):
    return users.get(user_id)

@app.get('/login')
async def login_page(request):
    return Response.send_file('static/login.html')

@app.post('/login')
async def do_login(request):
    user = authenticate(request.form['user'], request.form['pass'])
    if not user:
        return redirect('/login?error=1')
    return await login.login_user(request, user, remember=True)

@app.get('/dashboard')
@login
async def dashboard(request):
    return 'hi ' + request.g.current_user.name

@app.post('/logout')
async def logout(request):
    await login.logout_user(request)
    return redirect('/')

Кука _remember є підписаним JWT (використовуючи секрет сесії), тому викрадена кука не може бути повторно використана в іншому застосунку або після ротації секрета.