microdot.login — flujo de inicio de sesión de usuario

Un envoltorio de nivel superior alrededor de microdot.session que implementa el flujo convencional de inicio de sesión con nombre de usuario / contraseña: una función de retorno (callback) user loader asigna el id de usuario almacenado en la sesión de vuelta al objeto de usuario de la aplicación, los decoradores de ruta redirigen las solicitudes no autenticadas a una URL de inicio de sesión configurable, y las cookies opcionales de «recordarme» permiten que los visitantes recurrentes permanezcan conectados entre sesiones del navegador.

Requiere que microdot.session esté inicializado en la aplicación antes de construir el objeto de inicio de sesión: la sesión es donde se almacena el id de usuario.

class Login

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

URL a la que el decorador redirige las solicitudes no autenticadas. La aplicación proporciona el formulario/controlador de inicio de sesión real en esta ruta. Por defecto '/login'.

user_loader(f)

Decorador que registra la función de retorno que resuelve el usuario. f recibe el id de usuario almacenado en la sesión y devuelve el objeto de usuario (o None si el id ya no es válido: una cuenta eliminada, una sesión revocada, etc.).

login = Login()

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

Decorar una ruta con la instancia de Login la protege tras la autenticación:

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

Las solicitudes no autenticadas se redirigen a login_url con un parámetro de consulta ?next=<original-url> para que el controlador de inicio de sesión pueda devolver al usuario al lugar de donde vino.

fresh(f)

Como __call__(), pero rechaza las sesiones restauradas a partir de una cookie de «recordarme»: el usuario debe haber iniciado sesión explícitamente desde el último inicio de sesión completo. Se usa para proteger rutas sensibles (cambio de contraseña, eliminación de cuenta) de modo que una cookie de recordarme robada no pueda alcanzarlas.

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

Marca a user como conectado para request. Almacena el id de usuario en la sesión y devuelve una respuesta de redirección.

user

El objeto de usuario devuelto por el cargador de usuarios. Debe tener un atributo id.

remember

Si es verdadero, también establece una cookie _remember de larga duración. Pase True para los 30 días predeterminados, o un entero para el número de días que debe durar la cookie.

redirect_url

A dónde redirigir después del inicio de sesión. Se anula con ?next=<url> de la solicitud original si apunta a una ruta del mismo sitio.

Devuelve la respuesta de redirección: devuelva su valor desde el controlador de la ruta de inicio de sesión.

async logout_user(request)

Borra el id de usuario de la sesión y elimina cualquier cookie _remember. Úselo desde una ruta /logout:

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

Devuelve el objeto de usuario actualmente conectado (o None). Se memoiza en request.g.current_user para que las llamadas repetidas dentro de una misma solicitud accedan al cargador solo una vez.

Ejemplo

Un flujo de inicio de sesión mínimo:

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('/')

La cookie _remember es en sí misma un JWT firmado (usando el secreto de la sesión), por lo que una cookie robada no puede reutilizarse en una aplicación diferente ni después de que el secreto haya sido rotado.