I'm trying to add passport jwt strategy to all routes that start with /api.
I cannot find in the documentation any example of this. I don't want to add the #UseGuards decorator in every single endpoint.
Thanks!
It's not immediately possible to bind a guard only to a specific route, however it would be possible to add in some logic to the guard to check if you are in a specific route (or not) and run logic (or a short circuit). Something maybe like
#Injectable()
export class APIGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
const req = context.switchToHttp().getRequest();
if (!req.url.contains('api')) {
return true;
}
const isValid = this.checkValidity(context);
return isValid;
}
private isvalid(context: ExecutionContext) {
// do your logic. Only brought to a separate method to keep the `canActivate` cleaner
}
}
Related
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
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;
}
Is it possible to use multiple auth guards on a route (in my case basic and ldap auth).
The route should be authenticated when one guard was successful.
Short answer: No, if you add more than one guard to a route, they all need to pass for the route to be able to activate.
Long answer: What you are trying to accomplish is possible however by making your LDAP guard extend the basic one. If the LDAP specific logic succeeds, return true, otherwise return the result of the call to super.canActivate(). Then, in your controller, add either the basic or LDAP guard to your routes, but not both.
basic.guard.ts
export BasicGuard implements CanActivate {
constructor(
protected readonly reflector: Reflector
) {}
async canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest();
if () {
// Do some logic and return true if access is granted
return true;
}
return false;
}
}
ldap.guard.ts
export LdapGuard extends BasicGuard implements CanActivate {
constructor(
protected readonly reflector: Reflector
) {
super(reflector);
}
async canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest();
if () {
// Do some logic and return true if access is granted
return true;
}
// Basically if this guard is false then try the super.canActivate. If its true then it would have returned already
return await super.canActivate(context);
}
}
For more information see this GitHub issue on the official NestJS repository.
According to AuthGuard it just works out of the box
AuthGuard definition
If you look at AuthGuard then you see the following definition:
(File is node_modules/#nestjs/passport/dist/auth.guard.d.ts)
export declare const AuthGuard: (type?: string | string[]) => Type<IAuthGuard>;
That means that AuthGuard can receive an array of strings.
Code
In my code I did the following:
#UseGuards(AuthGuard(["jwt", "api-key"]))
#Get()
getOrders() {
return this.orderService.getAllOrders();
}
Postman test
In Postman, the endpoint can have the api-key and the JWT.
Tested with JWT in Postman Authorization: It works
Tested with API-Key in Postman Authorization: It works
That implies there is an OR function between the 2 Guards.
You can create an abstract guard, and pass instances or references there, and return true from this guard if any of the passed guards returned true.
Let's imagine you have 2 guards: BasicGuard and LdapGuard. And you have a controller UserController with route #Get(), which should be protected by these guards.
So, we can create an abstract guard MultipleAuthorizeGuard with next code:
#Injectable()
export class MultipleAuthorizeGuard implements CanActivate {
constructor(private readonly reflector: Reflector, private readonly moduleRef: ModuleRef) {}
public canActivate(context: ExecutionContext): Observable<boolean> {
const allowedGuards = this.reflector.get<Type<CanActivate>[]>('multipleGuardsReferences', context.getHandler()) || [];
const guards = allowedGuards.map((guardReference) => this.moduleRef.get<CanActivate>(guardReference));
if (guards.length === 0) {
return of(true);
}
if (guards.length === 1) {
return guards[0].canActivate(context) as Observable<boolean>;
}
const checks$: Observable<boolean>[] = guards.map((guard) =>
(guard.canActivate(context) as Observable<boolean>).pipe(
catchError((err) => {
if (err instanceof UnauthorizedException) {
return of(false);
}
throw err;
}),
),
);
return forkJoin(checks$).pipe(map((results: boolean[]) => any(identity, results)));
}
}
As you can see, this guard doesn't contain any references to a particular guard, but only accept the list of references. In my example, all guards return Observable, so I use forkJoin to run multiple requests. But of course, it can be adopted to Promises as well.
To avoid initiating MultipleAuthorizeGuard in the controller, and pass necessary dependencies manually, I'm left this task to Nest.js and pass references via custom decorator MultipleGuardsReferences
export const MultipleGuardsReferences = (...guards: Type<CanActivate>[]) =>
SetMetadata('multipleGuardsReferences', guards);
So, in controller we can have next code:
#Get()
#MultipleGuardsReferences(BasicGuard, LdapGuard)
#UseGuards(MultipleAuthorizeGuard)
public getUser(): Observable<User> {
return this.userService.getUser();
}
You can use combo guard that injects all guards what you need and combines their logic.
There is closed github issue:
https://github.com/nestjs/nest/issues/873
There is also a npm package that address this scenario: https://www.npmjs.com/package/#nest-lab/or-guard.
Then you call a unique guard that references all the necessary guards as parameters:
guards([useGuard('basic') ,useGuard('ldap')])
Inspired from https://stackoverflow.com/a/69966319/16730890
This uses Promise instead of Observable.
import {
CanActivate,
ExecutionContext,
Injectable,
SetMetadata,
Type,
} from '#nestjs/common';
import { ModuleRef, Reflector } from '#nestjs/core';
#Injectable()
export class MultipleAuthorizeGuard implements CanActivate {
constructor(
private readonly reflector: Reflector,
private readonly moduleRef: ModuleRef,
) {}
public async canActivate(context: ExecutionContext): Promise<boolean> {
const allowedGuards =
this.reflector.get<Type<CanActivate>[]>(
'multipleGuardsReferences',
context.getHandler(),
) || [];
const guards = allowedGuards.map((guardReference) =>
this.moduleRef.get<CanActivate>(guardReference),
);
if (guards.length === 0) {
return Promise.resolve(true);
}
if (guards.length === 1) {
return guards[0].canActivate(context) as Promise<boolean>;
}
return Promise.any(
guards.map((guard) => {
return guard.canActivate(context) as Promise<boolean>;
}),
);
}
}
export const MultipleGuardsReferences = (...guards: Type<CanActivate>[]) =>
SetMetadata('multipleGuardsReferences', guards);
#Get()
#MultipleGuardsReferences(BasicGuard, LdapGuard)
#UseGuards(MultipleAuthorizeGuard)
public getUser(): Promise<User> {
return this.userService.getUser();
}
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.
Does such a thing exist or do I follow standard Mongoose procedure?
I read the docs, I spent the whole day yesterday for this, but I could only find relative ones that placed the functions inside the service component. This is not effective as if I would like to use a static model function outside of the service component (say, a custom decorator), it wouldn't reach it as DI is private.
I would have created an Issue on Github for documentation request, but I feared I may have overlooked something.
Edit 2: Please do not change the title of the post. "Nest" is not a typo for "best". It is referring to a Node.js framework called Nest.js. (See post tags and referenced documentation link)
Edit: In the MongoDB section of the docs, there's this piece of code:
constructor(#InjectModel(CatSchema) private readonly catModel: Model<Cat>) {}
but specifically, this Model<Cat> part, imports Cat from an interface that extends Mongoose Document interface. Wouldn't it be better if this Cat interface was a class instead which was capable of functions (even after transpilation)?
I use the following approach:
When defining the schema, add static methods to the Mongoose schema:
UserSchema.methods.comparePassword = async function(candidatePassword: string) {
return await bcrypt.compare(candidatePassword, this.password);
};
Also include the method in the object's interface definition:
export interface User {
firstName: string;
...
comparePassword(candidatePassword: string): Promise<boolean>;
}
as well as the UserDocument interface
export interface UserDocument extends User, Document { }
So now my UsersService:
export class UsersService {
constructor(#InjectModel(Schemas.User) private readonly userRepository: Model<UserDocument>,
private readonly walletService: WalletsService,
#Inject(Modules.Logger) private readonly logger: Logger) {}
async findByEmail(email: string): Promise<UserDocument> {
return await this.userRepository.findOne({ email }).select('password');
}
...
}
And to tie it all together, when a user tries to log in, the Auth service retrieves a user object by id, and invokes that user object's instance method of comparePassword:
#Injectable()
export class AuthService {
constructor(
private readonly usersService: UsersService,
private readonly jwtService: JwtService,
) { }
async signIn({ email, password }: SignInDto): Promise<LoginResponse> {
const user = await this.usersService.findByEmail(email);
if (!user) { throw new UnauthorizedException('Invalid Username or Password'); }
if (await user.comparePassword(password)) {
const tokenPayload: JwtPayload = { userId: user.id };
const token = this.jwtService.sign(tokenPayload);
return ({ token, userId: user.id, status: LoginStatus.success });
} else {
throw new UnauthorizedException('Invalid Username or Password');
}
}
}
#InjectModel() is a helper decorator to inject registered component. You can always use a model class directly instead of injecting it through a constructor. Thanks to that you can use a model everywhere (but I'm not sure whether a custom decorator is a right choice). Also, Model<Cat> is redundant here. You can replace this type with anything else that fits your use-case, for example typeof X if you want to call static functions.