Iniciar sesión método de login

Avanzado
Nest
Nest
Actualizado: 10/06/2025

¡Desbloquea el curso completo!

IA
Ejercicios
Certificado
Entrar

Introducción

Después de haber implementado el registro de usuarios, el siguiente paso crucial es crear un método de login para que los usuarios puedan autenticarse. Este método verificará las credenciales del usuario y, si son correctas, generará un Token Web JSON (JWT), que el frontend podrá usar para acceder a rutas protegidas.

¿Te está gustando esta lección?

Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.

Progreso guardado
Asistente IA
Ejercicios
Iniciar sesión gratis

Más de 25.000 desarrolladores ya confían en CertiDevs

Instalar librerías

Instalar las librerías de autenticación y generación de tokens JWT, además de bcrypt para verificar contraseñas cifradas:

npm install @nestjs/jwt @nestjs/passport passport passport-jwt bcrypt
# o
yarn add @nestjs/jwt @nestjs/passport passport passport-jwt bcrypt

Configurar JwtModule

¡Muy Importante! La clave secreta (secret) para firmar tus tokens JWT debe ser larga, compleja y nunca expuesta en tu código fuente. Es una buena práctica generarla y almacenarla como una variable de entorno.

Puedes usar este script de Node.js para generar una clave segura:

secrets.js

const crypto = require('crypto');
const secret = crypto.randomBytes(32).toString('hex'); // Genera 32 bytes (256 bits) de aleatoriedad
console.log(secret);

Ejecuta este script una sola vez (node secrets.js), copia la clave generada y guárdala en tu archivo .env en la raíz de tu proyecto:

.env

# ... otras variables de entorno ...
JWT_SECRET=tu_clave_secreta_segura_generada_aqui

Configuración del Módulo JWT en AppModule

Vamos a configurar JwtModule en tu AppModule usando forRootAsync para que pueda leer la clave secreta desde las variables de entorno de forma segura a través de ConfigService. También importamos PassportModule, que es la base para la estrategia JWT.

// src/app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule, ConfigService } from '@nestjs/config'; // Importa ConfigService
import { AuthModule } from './auth/auth.module';
import { User } from './user/user.entity';
import { JwtModule } from '@nestjs/jwt'; // Importa JwtModule
import { PassportModule } from '@nestjs/passport'; // Importa PassportModule

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: '.env',
    }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: (configService: ConfigService) => ({
        type: configService.get<'mysql'>('DB_TYPE'),
        host: configService.get<string>('DB_HOST'),
        port: configService.get<number>('DB_PORT'),
        username: configService.get<string>('DB_USERNAME'),
        password: configService.get<string>('DB_PASSWORD'),
        database: configService.get<string>('DB_DATABASE'),
        entities: [__dirname + '/**/*.entity{.ts,.js}'],
        synchronize: true, // ¡Recuerda que esto nunca en producción! Usar migraciones.
      }),
      inject: [ConfigService],
    }),
    // Configuración del módulo JWT para la firma de tokens
    JwtModule.forRootAsync({
      imports: [ConfigModule], // Importa ConfigModule para acceder a variables de entorno
      useFactory: (configService: ConfigService) => ({
        secret: configService.get<string>('JWT_SECRET'), // Lee la clave secreta de las variables de entorno
        signOptions: { expiresIn: '7d' }, // El token expira en 7 días
      }),
      inject: [ConfigService], // Inyecta ConfigService en la factory
    }),
    PassportModule, // Necesario para la autenticación basada en estrategias (como JWT)
    AuthModule, // Nuestro módulo de autenticación
  ],
})
export class AppModule {}

Creación del DTO para el Login

Definiremos un DTO simple para recibir las credenciales del usuario al intentar iniciar sesión.

src/auth/dto/login.dto.ts

import { IsEmail, IsString, IsNotEmpty } from 'class-validator';

export class LoginDto {
  @IsEmail({}, { message: 'El email debe ser una dirección de correo válida.' })
  @IsNotEmpty({ message: 'El email es un campo requerido.' })
  email: string;

  @IsString({ message: 'La contraseña debe ser una cadena de texto.' })
  @IsNotEmpty({ message: 'La contraseña es un campo requerido.' })
  password: string;
}

Desarrollo del Servicio AuthService (Lógica de Login)

Ahora, añadiremos la lógica de login a nuestro AuthService. Aquí es donde verificaremos las credenciales y generaremos el JWT.

  // src/auth/auth.service.ts
import {
  Injectable,
  ConflictException,
  InternalServerErrorException,
  UnauthorizedException, // Importa UnauthorizedException
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from '../user/user.entity';
import * as bcrypt from 'bcrypt';
import { RegisterDto } from './dto/register.dto';
import { LoginDto } from './dto/login.dto'; // Importa el LoginDto
import { JwtService } from '@nestjs/jwt'; // Importa JwtService
import { Role } from '../user/role.enum';


@Injectable()
export class AuthService {
  constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>,
    private jwtService: JwtService, // Inyecta JwtService
  ) {}

  async register(registerDto: RegisterDto): Promise<User> {
    const { email, password, ...userData } = registerDto;

    const existingUser = await this.userRepository.findOne({ where: { email } });
    if (existingUser) {
      throw new ConflictException('El email ya está registrado.');
    }

    try {
      const hashedPassword = await bcrypt.hash(password, 10);
      const newUser = this.userRepository.create({
        email,
        password: hashedPassword,
        role: Role.USER,
        ...userData
      });

      await this.userRepository.save(newUser);
      delete newUser.password; // No devolver la contraseña cifrada
      return newUser;

    } catch (error) {
      console.error('Error al registrar usuario:', error);
      throw new InternalServerErrorException('Error al registrar el usuario. Por favor, inténtelo de nuevo.');
    }
  }

  // Nuevo método de login
  async login(loginDto: LoginDto): Promise<{ accessToken: string }> { // Retorna un objeto con el token
    const { email, password } = loginDto;

    // 1. Buscar el usuario por email
    const user = await this.userRepository.findOne({ where: { email } });

    // 2. Verificar si el usuario existe y si la contraseña es correcta
    // Es mejor lanzar UnauthorizedException para ambos casos por seguridad (prevenir enumeración de usuarios)
    if (!user || !(await bcrypt.compare(password, user.password))) {
      throw new UnauthorizedException('Credenciales incorrectas (email o contraseña).'); // HTTP 401 Unauthorized
    }

    // 3. Crear el payload del token JWT
    const payload = {
      sub: user.id, // 'sub' (subject) es una convención para el ID del usuario
      email: user.email,
      role: user.role,
      // Puedes añadir más datos relevantes aquí, pero evita información sensible
    };

    // 4. Generar y devolver el token de acceso
    const accessToken = await this.jwtService.signAsync(payload);
    return { accessToken }; // Retorna el token envuelto en un objeto
  }
}

Actualización del Controlador AuthController

Ahora, añadimos el endpoint login al AuthController, que delegará la autenticación al AuthService.

// src/auth/auth.controller.ts
import {
  Controller,
  Post,
  Body,
  HttpCode,
  HttpStatus,
  ConflictException,
  UsePipes,
  ValidationPipe,
} from '@nestjs/common';
import { AuthService } from './auth.service';
import { RegisterDto } from './dto/register.dto';
import { LoginDto } from './dto/login.dto'; // Importa LoginDto
import { User } from '../user/user.entity';

@Controller('auth')
export class AuthController {
  constructor(private readonly authService: AuthService) {}

  @Post('register')
  @HttpCode(HttpStatus.CREATED)
  async register(@Body() registerDto: RegisterDto): Promise<User> {
    return this.authService.register(registerDto);
  }

  // Nuevo método de login
  @Post('login')
  @HttpCode(HttpStatus.OK) // Retorna un 200 OK si el login es exitoso
  async login(@Body() loginDto: LoginDto): Promise<{ accessToken: string }> {
    return this.authService.login(loginDto);
  }
}

Configuración de main.ts para Validación Global

Asegúrate de que tu main.ts tenga configurado el ValidationPipe globalmente para que los DTOs funcionen correctamente.

// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.useGlobalPipes(new ValidationPipe({
    whitelist: true,
    forbidNonWhitelisted: true,
    transform: true,
  }));

  await app.listen(3000);
}
bootstrap();

Recepción del token

Una aplicación frontend debería tener un formulario de login que llame al método login del controlador de NestJS.

Por ejemplo en Angular podría ser login.component.ts.

Ejemplo de petición de login:

Una vez completado el login, el frontend recibe un token JWT, ejemplo de respuesta de login:

Ejemplo de token JWT obtenido:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjEsImVtYWlsIjoidXNlcjFAZ21haWwuY29tIiwicm9sZSI6ImFk.....

Tambien podemos usar Postman o Insomnia como en lecciones pasadas para comprobar el endpoint de login.

  • Ruta: POST http://localhost:3000/auth/login
  • Cuerpo de la Solicitud (JSON):
{
  "email": "user1@gmail.com",
  "password": "admin1"
}
  • Si las credenciales son correctas, recibirás un 200 OK con un objeto JSON que contendrá tu accessToken compuesto tambien por un token JWT.
  • Si las credenciales son incorrectas (email no registrado o contraseña errónea), recibirás un 401 Unauthorized.
  • Si los datos enviados no cumplen con el formato del DTO, recibirás un 400 Bad Request.

Verificar token JWT

En jwt.io se puede comprobar el token y su estructura interna:

Aprendizajes de esta lección

  • Configurar la seguridad JWT en NestJS
  • Generar una clave secreta para firmar token JWT
  • Generar token JWT en login
  • Realizar login de usuarios (autenticar usuarios)

Completa Nest y certifícate

Únete a nuestra plataforma y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.

Asistente IA

Resuelve dudas al instante

Ejercicios

Practica con proyectos reales

Certificados

Valida tus conocimientos

Más de 25.000 desarrolladores ya se han certificado con CertiDevs

⭐⭐⭐⭐⭐
4.9/5 valoración