Adiciona primeiras partes da autenticacao

This commit is contained in:
LeoMortari
2025-09-11 17:51:28 -03:00
parent 85aac808e9
commit ee1e3bd7f8
11 changed files with 228 additions and 6 deletions

View File

@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { AuthController } from './auth.controller';
describe('AuthController', () => {
let controller: AuthController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AuthController],
}).compile();
controller = module.get<AuthController>(AuthController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

@@ -0,0 +1,29 @@
import { Controller, Post, Body, HttpCode, HttpStatus } from '@nestjs/common';
import LoginResponseDto from './dto/loginResponse.dto';
import { AuthService } from './auth.service';
import { LoginDto } from './dto/login.dto';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Post('login')
async login(@Body() loginDto: LoginDto): Promise<LoginResponseDto> {
return this.authService.login(loginDto);
}
@Post('logout')
@HttpCode(HttpStatus.OK)
async logout(@Body() body: { refreshToken: string }): Promise<void> {
return this.authService.logout(body.refreshToken);
}
@Post('refresh-token')
async refreshToken(
@Body() body: { refreshToken: string },
): Promise<LoginResponseDto> {
return this.authService.refreshToken(body.refreshToken);
}
}

10
src/auth/auth.module.ts Normal file
View File

@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
@Module({
controllers: [AuthController],
providers: [AuthService],
exports: [AuthService],
})
export class AuthModule {}

View File

@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { AuthService } from './auth.service';
describe('AuthService', () => {
let service: AuthService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [AuthService],
}).compile();
service = module.get<AuthService>(AuthService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

103
src/auth/auth.service.ts Normal file
View File

@@ -0,0 +1,103 @@
import axios, { isAxiosError } from 'axios';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { LoginDto } from './dto/login.dto';
import LoginResponseDto from './dto/loginResponse.dto';
@Injectable()
export class AuthService {
private readonly keycloakUrl =
'https://auth.clipperia.com.br/realms/clipperia/protocol/openid-connect';
private readonly clientId = 'usuarios';
private readonly clientSecret = process.env.KEYCLOAK_CLIENT_SECRET;
private readonly keycloakApi = axios.create({
baseURL: this.keycloakUrl,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
async login(loginDto: LoginDto) {
try {
const params = new URLSearchParams({
client_id: this.clientId,
grant_type: 'password',
username: loginDto.username,
password: loginDto.password,
});
if (this.clientSecret) {
params.append('client_secret', this.clientSecret);
}
const { data } = await this.keycloakApi.post<LoginResponseDto>(
'/token',
params,
);
return data;
} catch (error) {
if (isAxiosError(error)) {
console.error(error.response?.data);
throw new UnauthorizedException(error.response?.data);
}
throw new UnauthorizedException('Usuário ou senha inválidos');
}
}
async logout(refreshToken: string): Promise<void> {
try {
const data = new URLSearchParams({
client_id: this.clientId,
refresh_token: refreshToken,
});
if (this.clientSecret) {
data.append('client_secret', this.clientSecret);
}
await this.keycloakApi.post('/logout', data);
} catch (error) {
if (isAxiosError(error)) {
console.error(error.response?.data);
throw new UnauthorizedException(error.response?.data);
}
throw new UnauthorizedException('Erro ao deslogar usuário');
}
}
async refreshToken(refreshToken: string) {
console.log(refreshToken);
try {
const params = new URLSearchParams({
client_id: this.clientId,
grant_type: 'refresh_token',
refresh_token: refreshToken,
});
if (this.clientSecret) {
params.append('client_secret', this.clientSecret);
}
const { data } = await this.keycloakApi.post<LoginResponseDto>(
'/token',
params,
);
return data;
} catch (error) {
if (isAxiosError(error)) {
console.error(error.response?.data);
throw new UnauthorizedException(error.response?.data);
}
throw new UnauthorizedException('Erro ao renovar token');
}
}
}

16
src/auth/dto/login.dto.ts Normal file
View File

@@ -0,0 +1,16 @@
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
export class LoginDto {
@IsString()
@IsNotEmpty()
username: string;
@IsString()
@IsEmail()
@IsNotEmpty()
email: string;
@IsString()
@IsNotEmpty()
password: string;
}

View File

@@ -0,0 +1,8 @@
export default class LoginResponseDto {
access_token: string;
refresh_token: string;
token_type: string;
expires_in: number;
refresh_expires_in: number;
scope: string;
}