¿Cómo gestionar la autenticación y el almacenamiento de contraseñas de manera efectiva?
La implementación adecuada de la autenticación de usuarios es una cuestión crítica en el desarrollo de aplicaciones web. El uso de JSON Web Tokens (JWT) y un buen manejo de las contraseñas forman el núcleo para proteger a nuestros usuarios. Antes de hablar sobre los tokens, es importante crear y almacenar usuarios de manera segura. Esto implica seguir ciertas mejores prácticas, como no almacenar directamente las contraseñas junto a la información del usuario.
¿Por qué es mejor separar la entidad de autenticación?
Separar la entidad de autenticación del almacenamiento de la información del usuario nos ayuda a:
Minimizar errores: No debemos preocuparnos por devolver contraseñas junto con el resto de información del usuario.
Simplificar la gestión de datos sensibles: Evita acceder accidentalmente a campos sensibles como contraseñas.
Aumentar la seguridad: Dificulta el acceso no autorizado a contraseñas al mantenerlas en una entidad separada y gestionada internamente.
Creando una nueva entidad de autenticación
Para crear una nueva entidad que gestione la autenticación y el almacenamiento seguro de contraseñas, seguimos estos pasos:
Crear un nuevo archivo de autorización: Dentro de una carpeta llamada aux_de_autorización, creamos un archivo index.js.
Definir la lógica de la función: Aquí exportamos una función que verifica si el almacenamiento es el adecuado y si no, utiliza el almacenaje predeterminado.
Realizar el almacenamiento adecuado:
Crear constantes para el almacenamiento y definir su estructura.
Crear funciones para agregar los datos (nombre de usuario, contraseña, etc.) de manera separada y solo cuando sean necesarias.
Implementación del controlador de usuario
Cuando creamos o editamos un usuario, debemos asegurarnos de:
Existencia de la contraseña: Siempre debe haber una contraseña al crear un usuario.
Actualización del nombre de usuario y contraseña: Si estamos editando, debemos actualizar estos campos si se modifican.
Ejemplo de implementación en código:
constaddUsuario=async(data)=>{const{ usuario, password }= data;// Siempre asignar identificadores únicos.const augData ={...data,userId: data.id,password: data.password,};// Lógica para almacenar en la tabla 'auth'const result =await authStore.add(augData);return result;};
Probando la implementación
Al finalizar el desarrollo, es vital probar la funcionalidad completa para confirmar que la autenticación y el almacenamiento de contraseñas funcionan correctamente:
Iniciando el servidor: Asegúrate de que al reiniciar no haya errores en el sistema.
Simulación de creación de usuarios: Añade varios usuarios y verifica que se almacenan sus credenciales adecuadamente en la base de datos separada de autenticación.
Inicia tu servidor y realiza pruebas de usuario para validar que todo esté operando de manera óptima.
¿Qué sigue después de la autenticación?
Con la gestión de usuarios lista, el siguiente paso es utilizar el sistema JWT para autenticar operaciones y peticiones en la aplicación. Esto asegurará que solo usuarios legítimos puedan realizar acciones específicas en el sistema.
Recuerda, asegurar los datos de tus usuarios es crucial para mantener la confianza y la integridad de tu aplicación. Continúa explorando y perfeccionando tus habilidades en autenticación y seguridad de datos, un área vital en la programación moderna.
Para los que tiene el siguiente error:nanoid es not a function
const{ nanoid }=require('nanoid')
Muchas gracias jm_devlife
, me sacaste de apuros... Gran aporte
:thumbsup:
Vengo del mundo de Java y estoy acostumbrado a la creación de clases que servirán como payloads de request y response en cada petición. Además de crear clases que simulan el modelo de la base de datos. ¿En NodeJS no se realiza esto? Veo que se crean objetos dentro de las funciones pero no creamos prototipos ni clases en ningún componente. Quizás la creación de estas clases ayudarían a tener un código más mantenible y robusto. ¿Qué opinan?
Las clases en JavaScript son azúcar sintáctico. Esto es, no cambian absolutamente nada por debajo, sino que le dan una forma distinta al código.
.
Si tu ves más claro el código con clases, úsalas. Si, cómo es mi caso, lo ves mejor con funciones que devuelven objetos, entonces no las uses.
.
Por detrás, va a suceder lo mismo, y el rendimiento va a ser el mismo ;)
¡Hola! Vengo del futuro. Si instalaste Nanoid 4.0.0 o arriba (puedes verificar fácilmente en package.json), las soluciones de nanoid is not a function no te van a ayudar porque para llamar Nanoid hay que usar import(), pero Nanoid funciona diferente ahí y volverás a ver el mismo error en el servidor.
Mi solución a esto fue bajar la versión de Nanoid usando
npm i -S nanoid@^3.0.0
y llamarlo con const { nanoid } = require('nanoid') como viene en otros comentarios.
Hola, no era más facil enseñar a la gente a usar ES6?
import { nanoid } from 'nanoid';
function addUser(id, username, name) {
return new Promise((resolve, reject) => {
const newUser = {
id: id || nanoid(),
name: name,
username: username,
};
Les recomiendo la extensión Thunderbolt de VS Code que prácticamente reemplaza a Postman y/o Insomnia para hacer esos métodos HTTP. Aquí dejo un vídeo de Youtube sobre ella
body_name, body_password me llegaban undefined a las funciones del almacenamiento, lo arreglé cambiano "req body" por "req query". ¿Estoy haciendo algo mal?
Request body: es lo que le envías como objeto JSON al servidor
Request query: Son los datos que envías a través de la URL, algo como lo siguiente:
https://platzi.com/index.html?variable=valor
Probablemente es la forma en la que estabas mandando los datos. Cuando obtienes valores por req.query es porque estos van en la URL del endpoint.
Cuando usas req.body es porque los estás manando en un json en el cuerpo de la petición :D
normalmente lo que uno hace en el frontend es guardar el token en localstorage. Pero que pasa si alguien abre la consola del navegador y extrae el token guardado? asi de facil puede robarte tu identidad, mas alla del tiempo de vida del token nunca supe como resolver ese pequeño detalle de seguridad…algun iluminado?
Si, tengo entendido que un JWT debe colocarse en una cookie
Guardar el JWT en localstorage es altamente peligroso
El toquen de autenticacion JWT debe guardarse en la memoria de la aplicacion del cliente (conocido tambien como estado de la aplicacion). Con respecto al token de refresco (refreshToken) es buena practica guardarlo en una cookie httpOnly para minimizar ataques de CSRF. Todo lo que puede escribirse con JS, puede ser accedido con JS. por lo que tambien es bueno implementar una estrategia de rotacion de tokens para minimizar el daño que un token comprometido puede causar.
Otra solución para
nanoid es not a function
Definición de nano:
const nanoid = require('nanoid');
Uso correcto:
nanoid.funcion();
Ejemplo:
nanoid.nanoid();
{
"error": false,
"status": 500,
"body": "auth.upsert is not a function"
}
Hola Ernesto, verifica que en el controller de auth tengas esta función y la estes retornando
app.use(express.urlencoded({extended:true}))//* Así ya no tenemos que instalar body-parserapp.use(express.json())
Ya que yo no instalé body parse porque estaba desactualizado. Entonces yo por error no puse la segunda línea de código para convertir la información a JSON. Creo que después de eso ya todo funcionó correctamente.
¿por qué mezclar las funciones de crear usuario y actualizar usuario en un mismo controlador? No sería más mantenible o mejor (no sé) separar estas funciones?
me sale error 500: "body": "collection is not defined" no se donde es
Es en dummy.js , para ello hay que validar que exista la tabla o collection en este caso. Se corrige con:
if(!db[collection]){ db[collection]=[]}
No se recomienda guardar las contraseñas con los usuarios. Deben estar en diferentes entidades.
no me funciono nanoid. por lo tanto tuve que hacer algo rápido para seguir con el curso.
asyncfunctionupsert(body){const user ={name: body.name,username: body.username,}if(body.id){ user.id= body.id}else{ user.id=nanoid()}// checking if password existif(body.password|| body.username){await auth.upsert({id: user.id,username: user.username,password: body.password,})}return store.upsert(TABLA, user);}```
En lugar de un or a la hora de guardar el auth con la contraseña no vendria mejor un and?
para asegurarse de que a la hora de crear un auth le estamos enviando una contraseña
Qué buena pregunta, osea lo hace de esa forma con un or porque ve un escenario de actualización de usuario donde bien o solo cambie el username o el password pero no los 2 al momento de actualizar. A mi tambien me llamo la atención esa parte y pienso que se puede mejorar, pero como no se me ocurrió nada mejor lo deje como en la clase.
Por que si no se esta llamando a upsert en ningun lado, se esta ejecutando?
A mi me parece que no es una función.
¿Hay alguna forma de reutilizar el código del index.js del user y el del auth, me salta mucho el que el código esté repetido en dos archivos.
claro. la llamada al store es simpre la mimsa, lo unico que cambia es el path relativo al controller
const ctrl =require("./controller");
Lo unico que tendrias que hacer es crear un modulo en api y que llame al controlador, pero usando un path dinamico que se genere en base a alguna variable recibida, o injectandolo del mismo modo que al controller se le injecta el store.
El tema es que esta api es muy sencilla, pero en un proyecto mas grande el index de cada componente podria tener cosas diferentes....
Tengo un error 404 Not Found
CannotPOST/api/user
Que puede ser?
Hola Javier: podrías revisar si tienes levantado el servicio, luego verificar si hay algún typo en el path.
Muestra tu codigo para poder apoyarte.
¿En este caso el index.js de auth funciona parecido al init.py en Python cuando empaquetamos un módulo?
Si, prácticamente funciona igual.
Tengo problema al momento de querer guardar el usuario, me sale el siguiente mensaje.