7-Seven-Up / megastore-backend

MIT License
1 stars 0 forks source link

feat: authenticate users #49

Closed EzeSosa closed 1 week ago

EzeSosa commented 1 week ago

Introducción

Se agregó la implementación del inicio de sesión (signin) para autenticar a los usuarios de nuestra aplicación y, a su vez, proteger los endpoints actualizando la configuración de seguridad.

Protección de endpoints

En cuanto a los endpoints, se implementó la siguiente configuración:

Inicio de sesión

El flujo de trabajo implementado para el inicio de sesión es el siguiente:

  1. Se realiza un POST a /auth/signin con un cuerpo conteniendo el usuario y la contraseña
  2. Llegada la solicitud al servidor, se llama al Authentication Manager para autenticar al usuario. (*)
  3. Si la autenticación es correcta, se genera y se devuelve un JWT único para el usuario con una duración de 30 minutos que deberá ser incluido en cada una de sus requests como token de autenticación

El Authentication Manager es un componente de Spring cuya responsabilidad es definir una estrategia de autenticación (en este caso usuario y contraseña) para autenticar a un usuario registrado. La estrategia particular que utilizamos implica un proveedor de autenticación, el Dao Authentication Provider. Este proveedor requiere de un usuario y una contraseña para poder realizar la autenticación**.

Para ello, debe obtener el usuario de la base de datos y debe contar con un Password Encoder para comparar el hash de la contraseña ingresada en el signin y la contraseña almacenada en la base de datos --esta implementación supone que las contraseñas fueron hasheadas.

image

Requests posteriores

En cada una de las requests posteriores, el usuario deberá ingresar su JWT dentro del encabezado de autorización con el formato 'Bearer [token]'. Cada request que se realice a los endpoints de los ítems 2) y 3) de la sección de protección de endpoints será interceptada por un filtro de autenticación. Este filtro será el encargado de:

  1. Extraer el token de la request,
  2. Extraer el usuario del token,
  3. Autenticar al usuario creando un objeto de autenticación, y
  4. Almacenar dicho objeto de autenticación en el Security Context Holder para permitir que se lleve a cabo la request.

Este último punto es sumamente importante. El Security Context Holder es donde Spring guarda los detalles de quién está autenticado, permitiendo reconocer al usuario que está realizando una request. Por defecto, el Security Context Holder almacena las autenticaciones en su propio hilo local, i.e, se limpia luego de cada ejecución de una petición. Es debido a ello que cada una de las requests tiene que pasar por el filtro de autenticación de modo que el Security Context Holder sea rellenado.

image

Adicional

EDIT 1: Se me ocurrió que los endpoints para obtener los productos, categorías y tamaños deberían no estar protegidos para imitar el funcionamiento de los e-commerces comunes (como MELI), que permiten a los usuarios no logeados visualizar el catálogo de productos y logearse únicamente para hacer los pedidos

delgadomatias commented 1 week ago

Lo veo re bien y la explicación esta espectacular.

Fedesan14 commented 1 week ago

Amé la descripción del MR, muy buena y te felicito por eso. Lo unico, a mi parecer quedaría mejor las imágenes con fondo y no que sea un PNG sin fondo porque se pierde el detalle -o es muy dificil de ver- las líneas que relacionan los componentes :)

martinjcrosetto commented 1 week ago

Muy buena explicación del MR, quedaría formular las issue para la expiración de la recuperación de contraseña y activación de la cuenta, concuerdo con fede sobre los dibujos en formato PNG, almenos el fondo de github lo vuelve muy complejo de leer, por lo demás excelente!👏

EzeSosa commented 1 week ago

Basic

Siguiendo la sugerencia de Fede, en los últimos commits agregué la Basic Authentication para aprovechar el filtro de autenticación particularmente en el método del sign in. Como estaba implementado anteriormente, en el método del signin (esto es, una vez pasados todos los filtros) se estaba llamando al Authentication Manager para que realice la autenticación, cuando este mismo comportamiento es implementado en Spring a través de la Basic Authentication.

Por ello, habilité la Basic Authentication para recibir las credenciales del usuario en el encabezado de la request al endpoint de sign in, dejando la responsabilidad de la autenticación al propio filtro de Basic.

Sin embargo, ante esto hubo un problema: los usuarios podían utilizar la Basic Authentication para todos los endpoints protegidos. Si bien esto de por sí no es un problema si como equipo decidiéramos utilizar sólo Basic, pero entonces la autenticación por JWT no cobraría sentido. Además, la idea era utilizar Basic únicamete para el sign in, mientras se utiliza la autenticación por JWT para los demás endpoints protegidos.

La solución que se me ocurrió para esto fue implementar el JWT filter después del Basic, limpiando el SecurityContextHolder si el Bearer token era inválido. De esta forma, las requests al signin podían realizarse mediante Basic y las demás debían ser autenticadas por JWT. Me gustaría conocer tu opinión sobre esto @Fedesan14

Fedesan14 commented 1 week ago

@EzeSosa No lo hice nunca, pero habria que ver la posibilidad de configurar que el BasicAuthFilter solo lo haga para los endpoints /signin. A nivel seguridad, no habria diferencia alguna