File size: 1,534 Bytes
70e641d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
"""Dependencias de autorizaci贸n y CSRF para FastAPI.

Cierra el agujero cr铆tico de la versi贸n PHP: /api/interpret y /api/papers estaban
abiertos. Aqu铆 requieren sesi贸n v谩lida. Las peticiones mutantes exigen doble-token CSRF.
"""

from __future__ import annotations

import hmac

from fastapi import Cookie, Header, HTTPException, Request, status

from .session import CABECERA_CSRF, COOKIE_CSRF, COOKIE_SESION, leer_sesion


def usuario_actual(request: Request) -> dict:
    """Devuelve la sesi贸n o 401. Usar como dependencia en rutas protegidas."""
    token = request.cookies.get(COOKIE_SESION)
    sesion = leer_sesion(token)
    if not sesion or not sesion.get("email"):
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="No autenticado.")
    return sesion


def verificar_csrf(
    request: Request,
    x_csrf_token: str | None = Header(default=None, alias=CABECERA_CSRF),
    morphos_csrf: str | None = Cookie(default=None, alias=COOKIE_CSRF),
) -> None:
    """Double-submit cookie: la cabecera debe coincidir con la cookie CSRF."""
    if request.method in ("GET", "HEAD", "OPTIONS"):
        return
    # Comparaci贸n en tiempo constante: el token es un secreto de sesi贸n, as铆 que se compara con
    # compare_digest por costumbre defensiva (no con `!=`, que corta en el primer byte distinto).
    if not x_csrf_token or not morphos_csrf or not hmac.compare_digest(x_csrf_token, morphos_csrf):
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="CSRF inv谩lido.")