microdot.login — процесс входа пользователя

Высокоуровневая обёртка над microdot.session, реализующая привычный процесс входа по имени пользователя и паролю: функция обратного вызова user loader сопоставляет сохранённый в сессии идентификатор пользователя с объектом пользователя приложения, декораторы маршрутов перенаправляют неаутентифицированные запросы на настраиваемый URL входа, а необязательные файлы cookie «запомнить меня» позволяют вернувшимся посетителям оставаться авторизованными между сеансами браузера.

Требует, чтобы 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__(), но отклоняет сессии, восстановленные из файла cookie «запомнить меня» – пользователь должен был явно войти после последнего полного входа. Используется для ограничения доступа к чувствительным маршрутам (смена пароля, удаление учётной записи), чтобы украденный cookie «запомнить меня» не мог до них добраться.

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

Отмечает user как вошедшего в систему для request. Сохраняет идентификатор пользователя в сессии и возвращает ответ-перенаправление.

user

Объект пользователя, возвращённый функцией user loader. Должен иметь атрибут id.

remember

Если истинно, также устанавливает долгоживущий файл cookie _remember. Передайте True для значения по умолчанию в 30 дней или целое число для количества дней, в течение которых cookie должен сохраняться.

redirect_url

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

Возвращает ответ-перенаправление – верните его значение из обработчика маршрута входа.

async logout_user(request)

Удаляет идентификатор пользователя из сессии и любой файл cookie _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('/')

Файл cookie _remember сам по себе является подписанным JWT (с использованием секрета сессии), поэтому украденный cookie нельзя повторно использовать в другом приложении или после ротации секрета.