#Local strategy using passport
1 messages · Page 1 of 1 (latest)
ok let me show what I ahve
@Module({
imports: [
JwtModule.registerAsync({
useFactory: async (config: ConfigService) => ({
secret: config.get('auth.secret'),
signOptions: {
expiresIn: '2h',
algorithm: 'HS256',
},
}),
inject: [ConfigService],
}),
PassportModule,
UsersModule,
],
providers: [Bcrypt, AuthService, LocalStrategy, JwtAuthStrategy],
controllers: [AuthController],
})
export class AuthModule {}
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { plainToInstance } from 'class-transformer';
import { Strategy } from 'passport-local';
import { STRATEGY_LOCAL } from '~/modules/auth/constants/strategy.constant';
import { Bcrypt } from '~common/utils/security/bcrypt';
import { PrismaService } from '~infra/database/prisma.service';
import { UserCredentialsValidation } from '~modules/users/dtos/user-credentials-validation.dto';
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy, STRATEGY_LOCAL) {
constructor(
private readonly prismaService: PrismaService,
private readonly bcrypt: Bcrypt,
) {
super({
usernameField: 'username',
passwordField: 'password',
passReqToCallback: true,
});
}
async validate(_request: Request, username: string, password: string) {
const user = await this.prismaService.user.findUnique({
where: { username },
});
if (!user) {
return null;
}
const match = await this.bcrypt.comparePassword(password, user.password);
if (!match) {
return null;
}
// use class transformer to remove fields like
return plainToInstance(UserCredentialsValidation, user, { excludeExtraneousValues: true });
}
}
my strategy
How are you saying this is the strategy to use?
import { Body, Controller, HttpCode, HttpStatus, Post, Req, Res, UseGuards } from '@nestjs/common';
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { Request, Response } from 'express';
import { LocalAuthGuard } from '~modules/auth/guards/local-auth.guards';
import { AuthService } from './auth.service';
import { Public } from './decorators/public.decorator';
import { LoginUserDto } from './dto/login-user.dto';
@Public()
@Controller('auth')
@ApiTags('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Post('login')
@UseGuards(LocalAuthGuard)
@HttpCode(204)
@ApiOperation({ summary: 'Login User' })
@ApiResponse({ status: 204, description: 'User logged with success!' })
@ApiResponse({
status: 401,
description: 'Invalid credentials (username or password).',
})
async signIn(@Body() userDto: LoginUserDto, @Res({ passthrough: true }) response: Response) {
await this.authService.signIn(userDto, response);
}
@Post('logout')
@HttpCode(HttpStatus.NO_CONTENT)
async signOut(@Req() request: Request, @Res({ passthrough: true }) response: Response) {
this.authService.signOut(request, response);
}
}
So I put the JWT Auth Guard globally and use the Public() to ignore it as I have seen on the documentation
And the LocalAuthGuard?
Only extends the AuthGuard
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { STRATEGY_LOCAL } from '../constants/strategy.constant';
@Injectable()
export class LocalAuthGuard extends AuthGuard(STRATEGY_LOCAL) {}
Okay, so the only other things I could think that might be happening here is that either PrismaService or Bcrypt are set to be request scoped
ok already try remove bcrypt
import * as bcrypt from 'bcrypt';
export class Bcrypt {
/**
* Encodes the given raw password using the bcrypt library
* @param rawPassword - The password to be encoded
* @returns A promise that resolves to the encoded password
*/
async encodePassword(rawPassword: string): Promise<string> {
const salt = await bcrypt.genSalt();
return bcrypt.hash(rawPassword, salt);
}
/**
* Compares the given raw password with the encoded password (hash)
* @param rawPassword - The password to be compared.
* @param hash - The encoded password to compare with
* @returns A promise that resolves to true if the passwords match, false otherwise
*/
async comparePassword(rawPassword: string, hash: string): Promise<boolean> {
return bcrypt.compare(rawPassword, hash);
}
}
Bcrypt is oly this
the prisma Service is globally
@Global()
@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}
Hmm, and the actual Prisma service class?
import { Inject, Injectable, type OnModuleDestroy, type OnModuleInit, Scope } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { PrismaClient } from '@prisma/client';
import { Request } from 'express';
import { NanoId } from '~common/utils/nanoid';
const extendedPrismaClient = new PrismaClient().$extends({
query: {
customer: {
create: async ({ args, query }) => {
if (args && typeof args === 'object') {
args.data = {
...args.data,
code: new NanoId().generate('C'),
};
}
return query(args);
},
update: async ({ args, query }) => {
if (args && typeof args === 'object' && args.data.deletedBy) {
args.data = {
...args.data,
deletedAt: new Date(),
};
}
return query(args);
},
},
},
});
@Injectable({
scope: Scope.REQUEST,
})
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
constructor(@Inject(REQUEST) private request: Request) {
super();
Object.assign(this, extendedPrismaClient);
}
async onModuleInit() {
await this.$connect();
}
async onModuleDestroy() {
await this.$disconnect();
}
}
Request scoped, right there
oh thanks
I try to get the request to fill automatically the fields createdBy, updatedBy etc
Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).
but didn't work
A quick question, how I can type the user inside request.user?
I try this
declare global {
namespace Express {
interface Request {
user?: {...}
}
}
}
but didn't work
What's the problem with accessing it?