nest-authify
Version:
Complete authentication and authorization package for NestJS - Monolith and Microservices ready with OAuth, JWT, Redis sessions
441 lines (416 loc) • 11.1 kB
text/typescript
import {
BadRequestException,
Body,
Controller,
Get,
HttpCode,
HttpStatus,
Inject,
Post,
UnauthorizedException
} from '@nestjs/common';
import {
ApiBearerAuth,
ApiBody,
ApiOperation,
ApiResponse,
ApiTags,
} from '@nestjs/swagger';
import { AUTH_SERVICE } from '../constants';
import { CurrentUser } from '../decorators/current-user.decorator';
import { Public } from '../decorators/public.decorator';
import { SessionId } from '../decorators/session-id.decorator';
import {
ChangePasswordDto,
LoginRequestDto,
LoginResponseDto,
RefreshTokenDto,
RegisterRequestDto,
UserProfileDto,
} from '../dto/auth.dto';
import { LoginResponse } from '../interfaces/auth-options.interface';
import { BaseAuthService } from '../services/base-auth.service';
/**
* Controlador de autenticación con endpoints listos para usar
* Documentado con Swagger
*/
export class AuthController {
constructor(
private readonly authService: BaseAuthService,
) { }
/**
* Registro de nuevos usuarios
*/
async register(
registerDto: RegisterRequestDto,
): Promise<LoginResponse> {
const user = await this.authService.register(registerDto);
const session = await this.authService.createSession(user);
return {
...session,
user: this.sanitizeUser(user),
};
}
/**
* Login con credenciales locales
*/
async login(
loginDto: LoginRequestDto,
): Promise<LoginResponse> {
// Validar que se proporcione username o email
if (!loginDto.username && !loginDto.email) {
throw new BadRequestException('Username or email is required');
}
// Usar email o username para la validación
const identifier = loginDto.email || loginDto.username;
// Validar credenciales
const user = await this.authService.validateUser(
identifier,
loginDto.password,
);
if (!user) {
throw new UnauthorizedException('Invalid credentials');
}
// Verificar si el usuario está activo
if (!user.isActive) {
throw new UnauthorizedException('User account is inactive');
}
// Crear sesión
const session = await this.authService.createSession(user);
return {
...session,
user: this.sanitizeUser(user),
};
}
/**
* Obtener perfil del usuario autenticado
*/
async getProfile( user: any): Promise<UserProfileDto> {
return this.sanitizeUser(user);
}
/**
* Refrescar access token
*/
async refresh(
refreshDto: RefreshTokenDto,
): Promise<{ accessToken: string; expiresIn: number }> {
return this.authService.refreshAccessToken(refreshDto.refreshToken);
}
/**
* Cerrar sesión actual
*/
async logout( sessionId: string): Promise<{ message: string }> {
if (!sessionId) {
throw new BadRequestException('Session ID not found');
}
await this.authService.revokeSession(sessionId);
return { message: 'Logged out successfully' };
}
/**
* Cerrar todas las sesiones del usuario
*/
async logoutAll(
userId: string,
): Promise<{ message: string }> {
if (!userId) {
throw new BadRequestException('User ID not found');
}
await this.authService.revokeAllUserSessions(userId);
return { message: 'All sessions revoked successfully' };
}
/**
* Verificar si el token es válido
*/
async verifyToken(
user: any,
): Promise<{ valid: boolean; user: UserProfileDto }> {
return {
valid: true,
user: this.sanitizeUser(user),
};
}
/**
* Cambiar contraseña
*/
async changePassword(
userId: string,
changePasswordDto: ChangePasswordDto,
): Promise<{ message: string }> {
if (!userId) {
throw new BadRequestException('User ID not found');
}
await this.authService.changePassword(
userId,
changePasswordDto.oldPassword,
changePasswordDto.newPassword,
);
return { message: 'Password changed successfully' };
}
/**
* Actualizar perfil del usuario
*/
async updateProfile(
userId: string,
updateData: Partial<any>,
): Promise<UserProfileDto> {
if (!userId) {
throw new BadRequestException('User ID not found');
}
// No permitir actualizar campos sensibles
const { password, roles, permissions, isActive, ...safeData } = updateData;
const updatedUser = await this.authService.updateUserProfile(
userId,
safeData,
);
return this.sanitizeUser(updatedUser);
}
/**
* Obtener información de sesión actual
*/
async getSession(
user: any,
sessionId: string,
): Promise<{
sessionId: string;
userId: string;
username?: string;
email?: string;
roles: string[];
}> {
return {
sessionId: sessionId || 'unknown',
userId: user.id,
username: user.username,
email: user.email,
roles: user.roles || [],
};
}
/**
* Sanitiza el objeto de usuario eliminando campos sensibles
*/
private sanitizeUser(user: any): UserProfileDto {
if (!user) return user;
const { password, ...sanitized } = user;
return sanitized;
}
}