I have a UseGuard in my WebSocket. Actually, this guard is a JwtAuthGuard that extends AuthGuard('jwt'). The JwtAuthGuard has a Strategy class called JwtStrategy. In this class, I have a validate method. In HTTP-based requests I return payload in this method. Then nestjs attach the payload to the req. Here is my Strategy class:
#Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private authConfigService: AuthConfigService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: authConfigService.JWT_SECRET,
});
}
async validate(payload: any) {
return payload;
}
}
I want to have access to context within validate method in order to attach the payload to the WebSocket's body (or anything that I can have access to the payload). Any idea?
You don't need to make any modifications to your strategy class. Instead, you should modify your JwtAuthGuard's getRequest method (if you don't have one then you should make one) that returns an object that has a headers proeprty that is an object with a authorization property that is a string. Something like
#Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
getRequest(context: ExecutionContext) {
const ws = context.switchToWs().getClient(); // possibly `getData()` instead.
return {
headers: {
authorization: valueFromWs(ws),
}
}
}
}
You can also make this work across different context types by using an if/else or a switch statement and returning the correct object based on the contextType from context.getType(). Whatever is returned from this getRequest method is where passport will end up attaching the user property, so it may make sense to return the entire client with these extra values.
Related
I'm learning NestJS and now I'm working in a simple authentication application, at this point I configured global pipes to validations and I'm using dto classes for example to validate #Body() fields. I don't know if I can use DTO to validate #Request fields sent from login endpoint.
import { Controller, Post, Request, UseGuards } from '#nestjs/common';
import { AuthService } from './auth.service';
import { AuthGuard } from '#nestjs/passport';
#Controller()
export class AuthController {
constructor(private authService: AuthService) {}
#UseGuards(AuthGuard('local'))
#Post('auth/login')
async login(#Request() req: reqDto /* I would like to use DTO for validations*/) {
return this.authService.login(req.user);
}
}
PS: I'm using DTO to validate SingUp body In UserController.
#Post('/signup')
createUser(#Body() createUserDto: CreateUserDto) {
return this.userService.createUser(createUserDto);
}
#Request() and #Headers() are two decorators that skip validation via pipes. You can make a custom request decorator that does get called via pipes, but annotating the request object would be a lot of work. What would be better is to create a decorator that gets just the data you need off the request, and then make a DTO for that object and validate that as necessary
I have the following problem. Let's say I have an array of routes & paths to static resources, e.g.
const routes = [{ url: '/', path: 'assets/www' }]
What I would like to do is to create a set of dynamic routes to serve static resources. In express application I would do smth like:
const router = express.Router();
routes.forEach(route => {
router.use(route.url, express.static(path.join(__dirname, route.path)))
})
But is it possible to create such a logic inside a NestJS controller?
#Controller()
export class ItemsController {
constructor() {}
#Get()
findAll() {}
#Get(':id')
findOne() {}
....
}
As far as I can see all HTTP request handlers should be predefined using the corresponding decorators.
I would opt for an approach where you create your controllers fully dynamic. This is not natively supported in Nest, but you can actually create dynamic controllers and dynamic services using a mixin approach (what is actually quite common), also see this answer at GitHub:
import { Post, Get, Controller } from '#nestjs/common';
interface Options {
basePath: string
entityName: string // you could even pass down DTO classes here, for maximum flexibility.
providerName: string
}
// this service is generated dynamically as well
// preferably use a custom providerName.
interface DynamicService {
foo: () => void
bar: () => void
}
export const createDynamicController = (controllerOptions: Options) => {
#Controller(controllerOptions.basePath)
class MyController {
constructor(
#Inject(options.providerName) myProvider: DynamicProvider
){}
#Get(options.entityName)
findOne(){
return this.myProvider.foo()
}
#Post(options.entityName)
create(){
return this.myProvider.foo()
}
}
return MyController
}
Using that approach you can create all controllers in theory dynamically, but it asks a bit more about understanding of the NestJS dependency tree.
You can now create the controller like:
{
controllers: [createDynamicController({
basePath: 'foo',
entityName: 'barz',
providerName: 'custom-provider-name'
})
]
}
What you could do is have a dynamic controller -> with say Parameters identifiers -> the as if it were like an event handler id... (not really just using that as an way to think of the process.)
Then in your the connected service -> you can have the 5 crud operations however in your service you inject the static resources -> and use that Identifier to route the call.
query dto
export class QueryDto {
readonly params?: any[];
readonly body: any;
}
controller
#Post(':serviceId')
async create(#Param('serviceId') serviceId: string, #Body() queryDto: QueryDto) {
return await this.rootService.create(serviceId, queryDto);
}
inside your root service
// childService = used to route the request dynamically ->
// body = your body or if you dont need a body this is where any parameters would go. which is why it's any (for if it was a get)
// params last => incase your doing a put or something where you need a body and parameters -> but it also allows it to be optional.
async create(childService: string, data: any, params: any[]): Promise<any> {
if (!id) {
return await this[childService].create(data.body, ...data.params).exec()
}
just an idea, but if everything static then this should do it for you. just use the root service as a passthrough. from the single controller -> to the various services.
I want to save each request (path, method, and userId) that comes to the server without having to hit the database twice, and also without messing up the main logic in services files with transactions.
Initially, I was trying to use an interceptor because it gets invoked after auth guards "which attaches the user to request" and before request handlers, but I faced two issues.
first, the fact that the interceptor will call the database to save a new record and then forward the request to handlers which will again hit DB again to handle the request. Secondly, It didn't work anyway because of dependancy injection problems.
code below is not working due to dependency errors as I mentioned, but It will give u an idea about what I need to acheive.
import { Injectable,
NestInterceptor,
Inject,
ExecutionContext,
CallHandler,
HttpException,
HttpStatus } from '#nestjs/common';
import { Observable } from 'rxjs';
import { getRepositoryToken } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
import { HistoryEntity } from '../../modules/history/history.entity';
#Injectable()
export class HistoryInterceptor implements NestInterceptor {
constructor(
#Inject(getRepositoryToken(HistoryEntity))
private readonly historyRepo: Repository<HistoryEntity>
) {}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
const { user, path, method } = request
if (!user) {
throw new HttpException('something terrible happened!', HttpStatus.BAD_GATEWAY);
}
const history = this.historyRepo.create({
path,
userId: user.id,
});
this.historyRepo.save(history);
return next.handle();
}
}
PS. from a performance point of view, It would also be great to not halt the request execution to save these info in db, in other words, Is it ok to NOT use await in this particular situation? because essecntially this is a system related operation and so, Node [rocess shouldn't wait for this step to process and return response to client.
Thanks in advance.
I am trying to access to Request object from within a Validation Pipe in nestjs
In order to verify uniqueness of certain fields, I require the ID/UUID parameters supplied with PUT/PATCH request (not available in the data structure itself)
any idea?
Currently, it is not possible to access the request object at all in a pipe. If you need the request you can use a guard or an interceptor.
If you are working on verifying uniqueness, that sounds like a part of business logic more than just about anything else, so I would put it in a service and handle the query to the database there. Just my two cents.
Edit 11/17/2020
After learning way more about how the framework works as a whole, technically it is possible to get the entire request object in a pipe. There are two ways to go about it.
Make the pipe #Injectable({ scope: Scope.REQUEST }) so that it is request scoped. This will end up creating a new pipe on each request, but hey, if that's your cup of tea then great.
Make a custom parameter decorator as custom decorators get completely passed into pipes as they are. Do note, that this could impact how the ValidationPipe is functioning if that is bound globally, at the class, or method level.
We can create a Pipe and access request object. We can move further and update the Body as well, if needed.
Following is an example scenario, where createdBy field should be added to the Body dynamically. Let's say user details are available from request:
// user.pipe.ts
import { Request } from 'express'
import { REQUEST } from '#nestjs/core'
import { Injectable, Inject, Scope, PipeTransform } from '#nestjs/common'
#Injectable({ scope: Scope.REQUEST })
export class UserPipe implements PipeTransform<any> {
constructor(#Inject(REQUEST) protected readonly request: Request) {}
transform(value) {
let email = this.request["user"].email;
value['createdBy'] = email;
return value
}
}
We can now use this in controller like this:
// someentity.controller.ts
#Post()
public async create(
#Body(SetUserPipe) dto: SomeEntityDto,
): Promise<SomeEntity> {
....
}
I downloaded the 19-auth sample and add some console.log debug code to it, then found some problems.
The code in JwtAuthGuard is never executed: '2222222' was not printed to the console in the code below:
canActivate(context: ExecutionContext) {
console.log('22222222222');
// add your custom authentication logic here
// for example, call super.logIn(request) to establish a session.
return super.canActivate(context);
}
When I changed the guard to JwtAuthGuard in the AuthController:
#get('data')
#UseGuards(JwtAuthGuard)
findAll(#Req() req) {
return req.user;
// this route is restricted by AuthGuard
// JWT strategy
}
the code in JwtAuthGuard was invoked, but in the canActivate function, I can't get the user info from request. and the canActivate function was called before the JwtStrategy?
Can someone explain how the code executing for the auth module, and how to get the user info in the JwtAuthGuard?
paste the latest code and console log here:
JwtStrategy
/**
* jwt passport 调用validate方法来判断是否授权用户进行接口调用
* #param payload
*/
async validate(payload: AuthPayload) {
Logger.log(`payload is ${JSON.stringify(payload)}`, 'JwtStrategy');
const user = await this.authService.validateUser(payload.id);
if (!user) {
throw new UnauthorizedException('不存在的用户信息');
}
return user;
}
JwtAuthGuard
canActivate(context: ExecutionContext) {
// add your custom authentication logic here
// for example, call super.logIn(request) to establish a session.
// this.accessPriv = this.reflector.get<string>('accessPriv', context.getHandler());
console.log('canActivate executed 111111111111111111');
return super.canActivate(context);
}
and the console log as below:
canActivate executed 111111111111111111
[Nest] 14080 - 2019-04-01 11:19 [JwtStrategy] payload is {"userName":"fanliang","id":"1","iat":1553772641,"exp":1554377441} +2286ms
it seems that the canActivate() function of JwtAuthGuard executed before the validate() function of JwtStrategy, but the user info was attached to the request after JwtStrategy validate().
what I want is to get the user info from request in the canActivate() of custom AuthGuard such like JwtAuthGuard
I have a somewhat solution that works for me.
Calling the super.canActivate before my own logic.
seems like the population of req.user triggered by it.
An example:
import { ExecutionContext, Injectable } from "#nestjs/common";
import { AuthGuard } from "#nestjs/passport";
import { Request } from "express";
#Injectable()
export class AuthGuardWithAllowSentry extends AuthGuard("jwt") {
public async canActivate(context: ExecutionContext) {
// that code will call the passport jwt
const origCanActivate = await super.canActivate(context);
// now we have request.user!
const http = context.switchToHttp();
const request = http.getRequest<Request>();
console.log(request.user)
if (request.header("X-Sentry-Token") === "blablabla") {
if (request.method === "GET" && request.path.endsWith(".map")) {
return true;
}
}
// some random logic
return request.user.roles.includes("admin")
}
}
it feels for me more like a workaround than a real thing.
I agree that the 19-auth sample is a little bit confusing to follow. This is mainly because it includes the JWTAuthGuard (as a reference for building custom guards) but it is never actually used. Instead, the original use of plain AuthGuard is already set up to provide JWT functionality. However, both guards leverage the JWTStrategy. If you want to understand this better, you could try updating your AuthController:
#Get('data')
#UseGuards(AuthGuard())
findAll() {
// this route is restricted by AuthGuard
// JWT strategy
return {
message: 'Successfully passed AuthGuard',
};
}
#Get('custom-jwt')
#UseGuards(new JwtAuthGuard())
// this route is restricted by JWTAuthGuard custom
// JWT strategy
customJwt() {
return {
message: 'Successfully passed JWTAuthGuard',
};
}
The important part is that in order to get past either guard, you must send the request with the Authorization header properly set to the token that's returned from the token endpoint.
For example: Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InRlc3RAZW1haWwuY29tIiwiaWF0IjoxNTU0MDUyNDczLCJleHAiOjE1NTQwNTYwNzN9.3Q8_FC-qFXk1F4KmMrHVSmmNGPAyHdt2myr5c18_E-U
I find it easiest to use a tool like Postman or Insomnia for constructing requests and setting Headers, etc but you could also use CURL. Once you've set the Authorization header with a valid token you'll be able to hit both of the guarded endpoints. If you put a console.log in the JWTStrategy you'll see that both guards end up using the validate method to retrieve the user correctly.