How to use session object in guards with nest-session - nestjs

In NestJS, using nest-session, I would like to use session object within a guard (CanActivate).
Inside a controller's action this is done by using #Session() but I can't find nor figure out how to fetch this data withing a guard.

First of all create an Interface that contains the session object and extends the express Request object because the session object is a field that nestjs creates and doesn't exists on the Request object.
import { Request } from 'express';
export interface IRequest extends Request {
session: any;
}
Then on your guard, you must import the interface, create a variable with this type then use the execution context to get the request and that's it, the req variable contain the Request object, you can use req.session to get the session object.
import { Injectable, CanActivate, ExecutionContext } from '#nestjs/common';
import { IRequest } from './app.interface'; //Import interface
#Injectable()
export class Guard implements CanActivate {
constructor() {}
canActivate(context: ExecutionContext): boolean {
const req: IRequest = context.switchToHttp().getRequest(); //Request Object
const session = req.session; //Session Object
/*
Do whatever you want with your session here ...
*/
return true;
}
}
You can find more information about the Request object here:
https://docs.nestjs.com/controllers#request-object
https://expressjs.com/en/api.html#req

Related

Is it possible to intercept calls from one controller to another one in Nestjs?

and I'm working in a backend application and I want to reuse some api writed before in other controller but its necesary that when I call a method for example from controller A to Controller B it must be intecepted by a Guard, middleware, etc. I'm using a global guard, that intercept any request call. And I tried something like the example below but just intercept the first call triggered on controller A but when call to controller B it dosent trigger
#Controller('controller-a')
export class ControllerA {
#Get()
methodA(){
const respFromB = await ControllerB.prototype.methodB({ ..some data.. });
enter code here
return '...'
}
}
#Controller('controller-b')
export class ControllerB {
#Post()
methodB(
#Body() data: any
) {
... some other code...
return 'books';
}
}
// main.ts
const reflector = app.get(Reflector);
const authService = app.get(AuthService);
const prismaClient = app.get(PrismaClient);
app.useGlobalGuards(new MyGlobalGuard(reflector, authService, prismaClient));
// MyGlobalGuard.ts
#Injectable()
export class MyGlobalGuard implements CanActivate {
public constructor(
private readonly reflector: Reflector,
private readonly authService: AuthService,
private readonly prisma: PrismaClient,
) {}
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
....
return true;
}
}
As you're the one calling from one controller to another, no it's not possible. You'd need to make an HTTP request from your server to your server to trigger the guards and interceptors again. It's Nest's internal route handler that's in charge of calling these enhancers, so you can't get to them from directly calling the class

Passing a service to an interceptor in Nestjs

I am using Nestjs and want to pass a service to the interceptor with generic types but I am getting errors that Nest cannot resolve the dependencies for the interceptor.
Here is an example:
Here is the interceptor:
#Injectable()
export class BaseInterceptor<Entity, SomeService> implements NestInterceptor {
constructor(public Service: SomeService) {}
intercept(context: ExecutionContext, next: CallHandler) {
const request = context.switchToHttp().getRequest();
const body: Entity = request.body;
if(body instanceof CreateProductDto) {
console.log("product!");
// do stuff with the service here
}
return next.handle().pipe();
}
}
Here is a class extending this:
#Injectable()
export class ProductsInterceptor extends BaseInterceptor<CreateProductDto, ProductsService> {
constructor() {
super(new ProductsService());
}
}
And I am calling it on a route:
#UseInterceptors(new ProductsInterceptor())
#Post()
....
My ultimate goal is to be able to use this interceptor on any route and then define some actions in the interceptor based on the Dto that I receive. I believe the main issue here is how to manage the dependencies for the interceptor within Nest which is what i am not sure about.

Extends the Request interface to add a fixed user property and extend any other class

I'm doing a server-side application with NestJS and TypeScript in combination with the implementation of Passport JWT.
A little bit of context first:
My JwtStrategy (no issues here):
#Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private userService: UserService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: 'hi',
});
}
async validate(payload: IJwtClaims): Promise<UserEntity> {
const { sub: id } = payload;
// Find the user's database record by its "id" and return it.
const user = await this.userService.findById(id);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
According to the documentation about the validate() method:
Passport will build a user object based on the return value of our
validate() method, and attach it as a property on the Request object.
Thanks to this behavior, I can access the user object in my handler like this:
#Get('hi')
example(#Req() request: Request) {
const userId = (request.user as UserEntity).id;
}
Did you notice that I have used a Type Assertion (tells the compiler to consider the user object as UserEntity) ? Without it, I won't have auto-completion about my entity's properties.
As a quick solution, I have created a class that extends the Request interface and include my own property of type UserEntity.
import { Request } from 'express';
import { UserEntity } from 'entities/user.entity';
export class WithUserEntityRequestDto extends Request {
user: UserEntity;
}
Now, my handler will be:
#Get('hi')
example(#Req() request: WithUserEntityRequestDto) {
const userId = request.user.id; // Nicer
}
The real issue now:
I have (and will have more) a handler that will receive a payload, let's call it for this example PasswordResetRequestDto.
export class PasswordResetRequestDto {
currentPassword: string;
newPassword: string;
}
The handler will be:
#Get('password-reset')
resetPassword(#Body() request: PasswordResetRequestDto) {
}
Now, I don't have access to the user's object. I would like to access it to know who is the user that is making this request.
What I have tried:
Use TypeScript Generics and add a new property to my previous WithUserEntityRequestDto class like this:
export class WithUserEntityRequestDto<T> extends Request {
user: UserEntity;
newProp: T;
}
And the handler will be:
#Get('password-reset')
resetPassword(#Req() request: WithUserEntityRequestDto<PasswordResetRequestDto>) {
}
But now the PasswordResetRequestDto will be under newProp, making it not a scalable solution. Any type that I pass as the generic will be under newProp. Also, I cannot extends T because a class cannot extends two classes. I don't see myself doing classes like this all the time.
What I expect to accomplish:
Pass a type to my WithUserEntityRequestDto class to include the passed type properties and also the user object by default. A way that I can do for example:
request: WithUserEntityRequestDto<AwesomeRequestDto>
request: WithUserEntityRequestDto<BankRequestDto>
And the value will be something like:
{
user: UserEntity, // As default, always present
// all the properties of the passed type (T),
// all the properties of the Request interface
}
My goal is to find an easy and scalable way to extends the Request interface and include any type/class on it, while having the user object (UserEntity) always present.
Thanks for the time and any help/advice/approach will be appreciated.
Nestjs provides an elegant solution for your problem, which is Custom decoration
it's common practice to attach properties to the request object. Then you manually extract them in each route handler,
What you have to do is create a user decorator:
//user.decorator.ts
import { createParamDecorator, ExecutionContext } from '#nestjs/common';
export const User = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
);
then you can simply use it in your controller like this:
#Get('hi')
example(#Req() request: Request,#User() user: UserEntity) {
const userId = user.id;
}

How to ignore some routes from #UseGuards() in a controller?

I have a controller like this:
#ApiBearerAuth()
#UseGuards(AuthGuard('jwt'))
#ApiTags('books')
#Controller('books')
export class BooksController {
#Post()
async create(#Body() createBookVm: CreateBookVm) {
//........
}
#Get()
async all() {
//........
}
}
When I access all() rout in above controller without accessToken I get the foloowing error:
{"statusCode":401,"error":"Unauthorized"}
It is a correct behavior but I want ignore all() action from general #UseGuards of the controller. I want access it as a public rout without authorization.
The easiest way is to change Guards to routes:
#ApiBearerAuth()
#ApiTags('books')
#Controller('books')
export class BooksController {
#Post()
#UseGuards(AuthGuard('jwt'))
async create(#Body() createBookVm: CreateBookVm) {
//........
}
#Get()
async all() {
//........
}
}
To provide another answer, albeit one that requires more code, is you could create a custom decorator that assigns metadata to the class and/or the class method. This metadata, in theory, would be for telling the guard to skip the auth check on this entire class, or on this route (depending on how you set the metadata up), and return true so that the request can still flow.
I've got a decorator like this set up here that sets up metadata if you'd like to take a look at how it works.
With this kind of approach, you could bind the guard globally, and then add the #AuthSkip() (or whatever you call it) decorator to the routes or classes you don't want to authorize.
Now you'll need to extend the AuthGuard('jwt') and update the canActivate() method to check for this metadata in the current context. This means that you'll need to add the Reflector as a dependency to the guard class and use it to get the metadata from both the class and the current route (if you went so far as to make it work for ignoring classes and not just routes), and if the metadata exists, then the route was to be skipped, return true from the guard. I make that kind of check here if you'd like to see an example of that in action.
Assuming you have used the app.useGlobalGuards() method inside main.ts file, add the following code inside the auth.guard.ts file:
import { ExecutionContext, Injectable } from '#nestjs/common';
import { Reflector } from '#nestjs/core';
import { AuthGuard as PassportAuthGaurd } from '#nestjs/passport';
#Injectable()
export class AuthGuard extends PassportAuthGaurd('jwt') {
constructor(private readonly reflector: Reflector) {
super();
}
canActivate(context: ExecutionContext) {
const isPublic = this.reflector.get<boolean>(
'isPublic',
context.getHandler()
);
if (isPublic) {
return true;
}
return super.canActivate(context);
}
}
I had used pssport jwt method here, but you can alter it according to you, just remember to keep constructor and the logic of canActivate same.
Now in your main.ts modify global guard so we can use Reflectors in it:
const reflector = app.get(Reflector);
app.useGlobalGuards(new AuthGuard(reflector));
Now in order to make routes public we would use a custom decorator, for that create a file named public.decorator.ts and add the following code:
import { SetMetadata } from '#nestjs/common';
export const Public = () => SetMetadata('isPublic', true);
Here we have added a custom metadata value which is same value that we used inside our auth.guard.ts file. Now just add this #Public() decorator on the route that you want to make public:
#Get()
#Public()
async all() {
//........
}
Now your all function won't check for the token authentication.
I found this blog which does the same thing, you can check it out.

NestJs hangs with Passport Anonymous strategy

I have an anonymous passport strategy defined as:
// In anonymous.strategy.ts
import {Strategy} from 'passport-anonymous'
import {PassportStrategy} from '#nestjs/passport'
import {Injectable} from '#nestjs/common'
#Injectable()
export class AnonymousStrategy extends PassportStrategy(Strategy, 'anonymous') {
constructor() {
super()
}
// Request times out unless this function is enabled
// authenticate() {
// return this.success({})
//}
}
Which I then added to the AuthGuards() chain in the controller after 'jwt' one:
#UseGuards(AuthGuard(['jwt', 'anonymous']))
#Post('/posts')
createAd(#Req() req: Request, #Body() createPostDto: CreatePostDto) {
...
}
I can't understand why the request times out unless I enable the authenticate() function in the strategy? After all, I don't have to do this for the passport-jwt strategy. Am I missing something? Thank you.
It's working for me when I added a User object into the success method.
authenticate() {
return this.success(new User());
}

Resources