microdot.login — fluxo de autenticação de utilizadores

Um invólucro de nível superior em torno de microdot.session que implementa o fluxo convencional de autenticação por nome de utilizador/palavra-passe: uma callback user loader mapeia o identificador de utilizador armazenado na sessão de volta ao objeto de utilizador da aplicação, os decoradores de rotas redirecionam pedidos não autenticados para um URL de autenticação configurável, e cookies opcionais de «lembrar-me» permitem que visitantes recorrentes permaneçam autenticados entre sessões do navegador.

Requer que microdot.session esteja inicializado na aplicação antes de o objeto de autenticação ser construído — a sessão é onde o identificador de utilizador é armazenado.

class Login

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

URL para o qual o decorador redireciona pedidos não autenticados. A aplicação disponibiliza o formulário/handler de autenticação real nesta rota. Predefinição: '/login'.

user_loader(f)

Decorador que regista a callback de resolução de utilizador. f recebe o identificador de utilizador armazenado na sessão e devolve o objeto de utilizador (ou None se o identificador já não for válido — conta eliminada, sessão revogada, etc.).

login = Login()

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

Decorar uma rota com a instância Login protege-a com autenticação:

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

Os pedidos não autenticados são redirecionados para login_url com um parâmetro de consulta ?next=<original-url> para que o handler de autenticação possa enviar o utilizador de volta ao ponto de origem.

fresh(f)

Semelhante a __call__(), mas rejeita sessões restauradas a partir de um cookie de «lembrar-me» — o utilizador deve ter iniciado sessão explicitamente desde o último login completo. Utilizado para proteger rotas sensíveis (alteração de palavra-passe, eliminação de conta) para que um cookie de lembrar-me roubado não consiga aceder a elas.

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

Marca user como autenticado para request. Armazena o identificador de utilizador na sessão e devolve uma resposta de redirecionamento.

user

O objeto de utilizador devolvido pelo user loader. Deve ter um atributo id.

remember

Se verdadeiro, define também um cookie _remember de longa duração. Passe True para o valor predefinido de 30 dias, ou um inteiro para o número de dias durante os quais o cookie deve ser válido.

redirect_url

Para onde redirecionar após a autenticação. Substituído por ?next=<url> do pedido original se apontar para um caminho do mesmo site.

Devolve a resposta de redirecionamento — devolva o seu valor a partir do handler da rota de autenticação.

async logout_user(request)

Remove o identificador de utilizador da sessão e elimina qualquer cookie _remember. Utilize numa rota /logout

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

Devolve o objeto de utilizador autenticado atualmente (ou None). Memorizado em request.g.current_user para que chamadas repetidas dentro de um pedido acedam ao loader apenas uma vez.

Exemplo

Um fluxo de autenticação 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('/')

O cookie _remember é ele próprio um JWT assinado (utilizando o segredo da sessão), pelo que um cookie roubado não pode ser reutilizado noutra aplicação nem após a rotação do segredo.