Adiciona primeiros Guards de autenticacao
This commit is contained in:
@@ -23,6 +23,7 @@
|
||||
"@nestjs/common": "11.0.1",
|
||||
"@nestjs/config": "4.0.2",
|
||||
"@nestjs/core": "11.0.1",
|
||||
"@nestjs/passport": "11.0.5",
|
||||
"@nestjs/platform-express": "11.0.1",
|
||||
"@prisma/client": "6.14.0",
|
||||
"axios": "1.12.0",
|
||||
@@ -30,6 +31,9 @@
|
||||
"class-transformer": "0.5.1",
|
||||
"class-validator": "0.14.2",
|
||||
"dayjs": "1.11.13",
|
||||
"jwks-rsa": "3.2.0",
|
||||
"passport": "0.7.0",
|
||||
"passport-jwt": "4.0.1",
|
||||
"reflect-metadata": "0.2.2",
|
||||
"rxjs": "7.8.1"
|
||||
},
|
||||
|
||||
@@ -12,7 +12,7 @@ import { LoggerMiddleware } from './middleware/logger.middleware';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({ isGlobal: true }),
|
||||
ConfigModule.forRoot(),
|
||||
PrismaModule,
|
||||
VideosModule,
|
||||
AuthModule,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AuthService } from './auth.service';
|
||||
import { AuthController } from './auth.controller';
|
||||
import { KeycloakJwtStrategy } from './keycloak.strategy';
|
||||
|
||||
@Module({
|
||||
controllers: [AuthController],
|
||||
providers: [AuthService],
|
||||
providers: [AuthService, KeycloakJwtStrategy],
|
||||
exports: [AuthService],
|
||||
})
|
||||
export class AuthModule {}
|
||||
|
||||
@@ -10,8 +10,7 @@ import LoginResponseDto from './dto/loginResponse.dto';
|
||||
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 clientId = 'account';
|
||||
|
||||
private readonly keycloakApi = axios.create({
|
||||
baseURL: this.keycloakUrl,
|
||||
@@ -29,10 +28,6 @@ export class AuthService {
|
||||
password: loginDto.password,
|
||||
});
|
||||
|
||||
if (this.clientSecret) {
|
||||
params.append('client_secret', this.clientSecret);
|
||||
}
|
||||
|
||||
const { data } = await this.keycloakApi.post<LoginResponseDto>(
|
||||
'/token',
|
||||
params,
|
||||
@@ -56,10 +51,6 @@ export class AuthService {
|
||||
refresh_token: refreshToken,
|
||||
});
|
||||
|
||||
if (this.clientSecret) {
|
||||
data.append('client_secret', this.clientSecret);
|
||||
}
|
||||
|
||||
await this.keycloakApi.post('/logout', data);
|
||||
} catch (error) {
|
||||
if (isAxiosError(error)) {
|
||||
@@ -72,8 +63,6 @@ export class AuthService {
|
||||
}
|
||||
|
||||
async refreshToken(refreshToken: string) {
|
||||
console.log(refreshToken);
|
||||
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
client_id: this.clientId,
|
||||
@@ -81,10 +70,6 @@ export class AuthService {
|
||||
refresh_token: refreshToken,
|
||||
});
|
||||
|
||||
if (this.clientSecret) {
|
||||
params.append('client_secret', this.clientSecret);
|
||||
}
|
||||
|
||||
const { data } = await this.keycloakApi.post<LoginResponseDto>(
|
||||
'/token',
|
||||
params,
|
||||
|
||||
18
src/auth/keycloak-auth.guard.ts
Normal file
18
src/auth/keycloak-auth.guard.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import type { JwtPayload } from './keycloak.strategy';
|
||||
|
||||
@Injectable()
|
||||
export class KeycloakAuthGuard extends AuthGuard('jwt') {
|
||||
handleRequest(err: unknown, user: JwtPayload): JwtPayload {
|
||||
if (err || !user) {
|
||||
if (err instanceof UnauthorizedException) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
throw new UnauthorizedException('Usuário não autenticado');
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
||||
54
src/auth/keycloak.strategy.ts
Normal file
54
src/auth/keycloak.strategy.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||
import * as jwksRsa from 'jwks-rsa';
|
||||
|
||||
export type JwtAudience = string | string[] | undefined;
|
||||
export interface JwtRealmAccess {
|
||||
roles: string[];
|
||||
}
|
||||
export interface JwtResourceAccessEntry {
|
||||
roles: string[];
|
||||
}
|
||||
export type JwtResourceAccess =
|
||||
| Record<string, JwtResourceAccessEntry>
|
||||
| undefined;
|
||||
export interface JwtPayload {
|
||||
sub: string;
|
||||
email?: string;
|
||||
preferred_username?: string;
|
||||
given_name?: string;
|
||||
family_name?: string;
|
||||
scope?: string;
|
||||
realm_access?: JwtRealmAccess;
|
||||
resource_access?: JwtResourceAccess;
|
||||
iat: number;
|
||||
exp: number;
|
||||
iss: string;
|
||||
aud?: JwtAudience;
|
||||
[claim: string]: unknown;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class KeycloakJwtStrategy extends PassportStrategy(Strategy, 'jwt') {
|
||||
constructor() {
|
||||
super({
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
secretOrKeyProvider: jwksRsa.passportJwtSecret({
|
||||
cache: true,
|
||||
rateLimit: true,
|
||||
jwksRequestsPerMinute: 5,
|
||||
jwksUri:
|
||||
'https://auth.clipperia.com.br/realms/clipperia/protocol/openid-connect/certs',
|
||||
}),
|
||||
algorithms: ['RS256'],
|
||||
audience: 'account',
|
||||
issuer: 'https://auth.clipperia.com.br/realms/clipperia',
|
||||
ignoreExpiration: false,
|
||||
});
|
||||
}
|
||||
|
||||
validate(payload: JwtPayload): JwtPayload {
|
||||
return payload;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
Delete,
|
||||
Body,
|
||||
Query,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { videos, Prisma, video_situation } from 'generated/prisma';
|
||||
|
||||
@@ -13,12 +14,14 @@ import { VideosService } from './videos.service';
|
||||
import { VideoResponseDto } from './dto/video-response.dto';
|
||||
import { PaginatedQueryDto, PaginatedResponse } from '../shared/dto/paginated';
|
||||
import { EBooleanPipe } from '../shared/pipe';
|
||||
import { KeycloakAuthGuard } from '../auth/keycloak-auth.guard';
|
||||
|
||||
@Controller('videos')
|
||||
export class VideosController {
|
||||
constructor(private readonly videosService: VideosService) {}
|
||||
|
||||
@Get()
|
||||
@UseGuards(KeycloakAuthGuard)
|
||||
async list(
|
||||
@Query() query: PaginatedQueryDto,
|
||||
@Query('situation') situation?: video_situation,
|
||||
|
||||
Reference in New Issue
Block a user