• Saltar al contenido principal
  • Skip to secondary menu
  • Saltar a la barra lateral principal
  • Saltar al pie de página
  • Inicio
  • Secciones
    • Ciencia de datos
    • Criptografía
    • Herramientas
    • Machine Learning
    • Noticias
    • Opinión
    • Productividad
    • Programación
      • JavaScript
      • Julia
      • Matlab
      • Python
      • R
  • Programación
    • JavaScript
    • Julia
    • Matlab
    • Python
    • R
  • Laboratorio
    • Encuestas: Tamaño de Muestra
    • Lotería: Probabilidad de Ganar
    • Reparto de Escaños (D’Hondt)
    • Tres en Raya con IA
  • Noticias
  • Boletín
  • Contacto
  • Tienda
    • Libros
    • Equipamiento de oficina
    • Equipamiento en movilidad
    • Tiendas afiliadas
      • AliExpress
      • Amazon
      • Banggood
      • GeekBuying
      • Lenovo

Analytics Lane

Ciencia e ingeniería de datos aplicada

  • Ciencia de datos
  • Machine Learning
  • IA Generativa
  • Python
  • Pandas
  • NumPy
  • Excel
  • Matlab

Registro de usuarios (8ª parte de creación de una API REST con Express y TypeScript)

noviembre 30, 2022 Por Daniel Rodríguez Deja un comentario
Tiempo de lectura: 6 minutos

En la entrega anterior se vio una forma para proteger las rutas mediante el uso de JWT (JSON Web Token). Aunque, dado que el API aún no cuenta con usuarios, cualquier que conozca la ruta puede obtener un token válido y acceder a la misma. En esta entrega vamos a ver cómo crear un proceso de registros de usuarios para nuestra API.

Esta entrada forma parte de la serie “Creación de una API REST con Express y TypeScript” de la cual forman los siguientes entregas:

  1. Creación de una API REST con Express y TypeScript
  2. Organizar el código del proyecto
  3. Configurar TypeORM para acceder a la base de datos
  4. Creación de rutas para consultar y agregar los registros
  5. Creación de rutas para modificar y borrar los registros
  6. Agregando logs al API con Winston
  7. Requerir autenticación mediante JWT
  8. Registro de usuarios
  9. Incluir un certificado en Express para servir el API mediante HTTPS
  10. Ejecutar la aplicación en producción con PM2

Función de hashing para contraseñas

Para guardar las contraseñas en la base de datos es necesario contar con una función de hashing. No es una buena práctica guardar las contraseñas encriptadas, cualquier que acceda a la aplicación tendrá la posibilidad de conseguir la clave de encriptado y recuperar de este modo las contraseñas. Por otro lado, almacenarlas sin proteger es una irresponsabilidad hacia los usuarios.

Curiosidad: La maldición de la dimensionalidad, o por qué añadir más datos puede empeorar tu modelo
En Analytics Lane
Curiosidad: La maldición de la dimensionalidad, o por qué añadir más datos puede empeorar tu modelo

El principal motivo por el que se debe almacenar el hash de la contraseña en lugar de está es porque las funciones de hashing son no reversibles. Esto es, a partir de un hash no se puede obtener el texto original. Debido a esta propiedad, aunque un atacante pueda obtener la tabla con los hashes de los usuarios, no podrá recuperar las contraseñas de una forma eficiente. Solamente mediante un ataque de fuerza bruta, generando hashes con todas las combinaciones de caracteres hasta que encuentre una que pueda generar un hash de la lista.

Uno de los algoritmos más utilizados para crear hashes de contraseñas es bcrypt. Debido a que la generación de un hash no es especialmente rápido, un ataque por fuerza bruta requerirá de mucha potencia de cálculo. En Node existe el paquete bcrypt en el que se implementa este algoritmo, para instalar solamente habrá que escribir en la terminal el comando

npm install bcrypt

Para trabajar en TypeScript también se deberán instalar las definiciones de tipos

npm install @types/bcrypt --save-dev

Publicidad


Creación de una tabla en la base de datos

En primer lugar, es necesario crear una tabla para almacenar los usuarios y las contraseñas. Para ello se irá a la carpeta entities y se creará un nuevo archivo llamado logins.ts en el que se escribirá el siguiente código.

import { compareSync, genSaltSync, hashSync } from 'bcrypt';
import { BeforeInsert, Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export default class Logins {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  username: string;

  @Column()
  password: string;

  @BeforeInsert()
  hashPassword() {
    this.password = hashSync(this.password, genSaltSync(10));
  }

  validatePassword(password: string): boolean {
    return compareSync(password, this.password);
  }
}

Como se puede ver se ha creado una envida de TypeORM en el que se tiene un id, una cadena de texto para almacenar el nombre de usuarios y otra cadena de texto para almacenar la contraseña.

En esta ocasión se han creado dos métodos en la clase hashPassword() y validatePassword(). El primero método se ejecuta inmediatamente antes de guardar el objeto en la base de datos, lo que se configura con el embellecedor BeforeInsert, y lo que hace es reemplazar la contraseña por el hash. Para lo que recurre a la función hashSync del paquete bcrypt. A la que se le debe pasar el password y una salt generada con la función genSaltSync(). El segundo método, validatePassword(), comprueba que una contraseña coincide con el hash almacenado en la base de datos. Para esto recurre a la función compareSync() que recibe como entrada una contraseña y un hash, en el caso de coincidir devuelve verdadero y falsos en cualquier otro.

La tabla que se ha creado es básica, en el caso de necesidad se podría agregar otros campos para almacenar grupos o roles de los usuarios.

Registro de usuarios

Ahora es necesario contar con la capacidad de registrar usuarios, para lo que en el archivo routes/auth.ts se agrega una nueva ruta register en la que se agrega el siguiente código.

router.post('/register', (req: Request, res: Response) => {
  const username = String(req.body.username);
  const password = String(req.body.password);

  datasource
    .getRepository(Logins)
    .findOneByOrFail({ username })
    .then(() => {
      responseAndLogger(res, 'User already exists', 406);
    })
    .catch(() => {
      const user = new Logins();
      user.username = username;
      user.password = password;

      datasource
        .getRepository(Logins)
        .save(user)
        .then((user) => res.send(user))
        .catch((error) => responseAndLogger(res, error.message, 500));
    });
});

El código obtiene el usuario y la contraseña del cuerpo. En primer lugar, busca si el usuario existe, en caso de que sea así devele un error indicado que el usuario existe y no se puede crear una nueva. Por otro lado, si el nombre de usuario está libre, creará un nuevo objeto, lo almacena en la base de datos y devuelve este. En ese caso no es un problema devolver el usuario ya que la contraseña se encuentra protegida y no se puede obtener a partir del hash. Aunque para mayor seguridad se puede devolver solo el id o una confirmación de la creación.

Ahora accediendo a la ruta http://localhost:4000/auth/register se puede registrar un usuario mediante una llamada POST. Para ello se debe incluir un JSON con el usuario y el nombre en el cuerpo.

Creación de un nuevo usuario en la ruta
Creación de un nuevo usuario en la ruta

Nótese que actualmente cualquier persona que conozca la ruta podrá registrar usuarios. En una API en será necesario proteger esta ruta mediante un middleware para evitar que se puedan crear usuarios.

Publicidad


Obtención de un token

Una vez se tiene la capacidad de crear usuarios, en el mismo archivo routes/auth.ts, se debe modificar la ruta login con el siguiente código.

router.post('/login', (req: Request, res: Response) => {
  const username = String(req.body.username);
  const password = String(req.body.password);

  datasource
    .getRepository(Logins)
    .findOneByOrFail({ username })
    .then((user) => {
      if (user.validatePassword(password)) {
        sign({ id: user.id, username: user.username }, secret, { expiresIn }, (err, token) => {
          if (err) {
            responseAndLogger(res, 'It was not possible to generate the token', 400);
          }

          return res.send({ token });
        });
      } else {
        responseAndLogger(res, 'Invalid password', 400);
      }
    })
    .catch(() => responseAndLogger(res, 'Invalid user', 400));
});

En este caso también se recupera el usuario y la contraseña del cuerpo. A diferencia del caso anterior busca el usuario en la base de datos y, en el caso de localizar uno con el mismo nombre, comprobará si la contraseña es válida. En el caso afirmativo devolverá un token con el id de usuario y su nombre y un fallo en cualquier otra situación. Un ejemplo de respuesta válida se puede ver en la siguiente captura de pantalla.

Obtención de un token al indicar un usuario y contraseña válidos
Obtención de un token al indicar un usuario y contraseña válidos

Por otro lado, cuando el usuario no es válido la respuesta seria como la siguiente.

Error al indicar una contraseña inválida para un usuario válido.
Error al indicar una contraseña inválida para un usuario válido.

Nótese que en este caso se devele un error diferente si el usuario no existe o la contraseña no es válida. A nivel de seguridad lo mejor sería devolver siempre el mismo mensaje para no dar pistas a un posible atacante. Aunque en este caso es útil para saber la ruta que ha seguido el código.

Conclusiones

En esta entrega se ha visto como una opción para el registro de usuarios en el API. Existen algunas librerías que facilitan esta tarea, como puede ser el caso de Passport, pero esta es una solución sencilla que puede ser utilizada en la mayoría de los casos. La próxima entrega se verá cómo incluir un certificado para servir el API mediante HTTPS.

El código creado en toda la serie de publicaciones se puede encontrar en la cuenta de GitHub de Analytics Lane.

Imagen de Tayeb MEZAHDIA en Pixabay

¿Te ha parecido de utilidad el contenido?

¡Puntúalo entre una y cinco estrellas!

Puntuación promedio 0 / 5. Votos emitidos: 0

Ya que has encontrado útil este contenido...

¡Síguenos en redes sociales!

¡Siento que este contenido no te haya sido útil!

¡Déjame mejorar este contenido!

Dime, ¿cómo puedo mejorar este contenido?

Publicidad


Publicaciones relacionadas

  • Curiosidad: La maldición de la dimensionalidad, o por qué añadir más datos puede empeorar tu modelo
  • ¿Está concentrado el MSCI World? Un análisis con Gini, Lorenz y leyes de potencia
  • Curiosidad: ¿Por qué usamos p < 0.05? Un umbral que cambió la historia de la ciencia
  • Programador de tareas de Windows: Guía definitiva para automatizar tu trabajo (BAT, PowerShell y Python)
  • La Paradoja del Cumpleaños, o por qué no es tan raro compartir fecha de nacimiento
  • Cómo abrir una ventana de Chrome con tamaño y posición específicos desde la línea de comandos en Windows
  • Curiosidad: El sesgo de supervivencia, o por qué prestar atención sólo a los que “llegaron” puede engañarte
  • Documentar tu API de Express con TypeScript usando OpenAPI (Swagger)
  • Data Lake y Data Warehouse: diferencias, usos y cómo se complementan en la era del dato

Publicado en: JavaScript Etiquetado como: Express, Node, TypeORM, TypeScript

Interacciones con los lectores

Deja una respuesta Cancelar la respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

I accept the Terms and Conditions and the Privacy Policy

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Barra lateral principal

Suscríbete a nuestro boletín

Suscríbete al boletín semanal para estar al día de todas las publicaciones.

Política de Privacidad

Analytics Lane en redes sociales

  • Amazon
  • Bluesky
  • Facebook
  • GitHub
  • Instagram
  • Mastodon
  • Pinterest
  • RSS
  • Telegram
  • Tumblr
  • Twitter
  • YouTube

Publicidad

Entradas recientes

Data Lake y Data Warehouse: diferencias, usos y cómo se complementan en la era del dato

octubre 23, 2025 Por Daniel Rodríguez

Documentar tu API de Express con TypeScript usando OpenAPI (Swagger)

octubre 21, 2025 Por Daniel Rodríguez

Curiosidad: El sesgo de supervivencia, o por qué prestar atención sólo a los que “llegaron” puede engañarte

octubre 16, 2025 Por Daniel Rodríguez

Publicidad

Es tendencia

  • Entendiendo la validación cruzada: Selección de la profundidad óptima en un árbol de decisión publicado el septiembre 13, 2024 | en Ciencia de datos
  • Buscar en Excel con dos o más criterios publicado el septiembre 7, 2022 | en Herramientas
  • Aprovecha un 40% de descuento en Coursera Plus para impulsar tus habilidades en Ciencia de Datos e Inteligencia Artificial publicado el noviembre 9, 2024 | en Noticias, Reseñas
  • Diferencias entre CPU, GPU, TPU y NPU publicado el abril 19, 2023 | en Herramientas
  • La Lotería: una perspectiva estadística publicado el diciembre 22, 2023 | en Opinión

Publicidad

Lo mejor valorado

4.9 (24)

Seleccionar filas y columnas en Pandas con iloc y loc

4.6 (16)

Archivos JSON con Python: lectura y escritura

4.4 (14)

Ordenación de diccionarios en Python mediante clave o valor

4.7 (13)

Operaciones de filtrado de DataFrame con Pandas en base a los valores de las columnas

4.5 (10)

Diferencias entre var y let en JavaScript

Publicidad

Comentarios recientes

  • Daniel Rodríguez en Probabilidad básica: cómo entender el azar en nuestra vida diaria
  • Pepe en Probabilidad básica: cómo entender el azar en nuestra vida diaria
  • CARLOS ARETURO BELLO CACERES en Justicio: La herramienta gratuita de IA para consultas legales
  • Piera en Ecuaciones multilínea en Markdown
  • Daniel Rodríguez en Tutorial de Mypy para Principiantes

Publicidad


Footer

Analytics Lane

  • Acerca de Analytics Lane
  • Boletín de noticias
  • Contacto
  • Libros
  • Lo más popular
  • Noticias
  • Tienda
  • Tiendas afiliadas

Secciones

  • Ciencia de datos
  • Criptografía
  • Herramientas
  • Machine Learning
  • Opinión
  • Productividad
  • Programación
  • Reseñas

Sobre de Analytics Lane

En Analytics Lane tratamos de explicar los principales conceptos de la ciencia e ingeniería de datos con un enfoque práctico. Los principales temas tratados son ciencia de datos, ingeniería de datos, inteligencia artificial, machine learning, deep learning y criptografía. Además, también se habla de los principales lenguajes de programación y herramientas utilizadas por los científicos e ingenieros de datos.

Copyright © 2018-2025 Analytics Lane ·Términos y condiciones ·Política de Cookies ·Política de Privacidad ·Herramientas de privacidad ·Contacto