How to use csurf middleware in tsoa express? - node.js

I am new to tsoa and I want to do CSRF implementation in my node app. I have been able to make api using app.use() but I want to write in tsoa. Is there any way?

In the pre-released version, you can use the #Middlewares() decorator.

Just put what you had in a app.use() to the #Middlewares() decorator.
You can define your Middleware / Middlewares like this:
import { Request, Response, NextFunction } from 'express';
// ... controller logic ...
// #Get('/endpoint') | #Post('/endpoint') etc.
#Middlewares([
(req: Request, res: Response, next: NextFunction) => {
console.log(req.headers);
next();
},
(req: Request, res: Response, next: NextFunction) => {
console.log('Second middleware, but we can also use only one!');
next();
},
])
// getEndpoint(): string {
// return 'Hello World!';
// }
// ... controller logic ...
Also remember to have set the experimentalDecorators to true in your tsconfig.json.1
1 https://github.com/lukeautry/tsoa/pull/1123#issuecomment-1018251162

Related

Add additional functionality to end method on Node/Express Response object?

My team and I are trying to mutate the response.end method in our Express middleware in order to have extra functionality be called just before the server responds back to the client.
Here is our attempt:
return (req: Request, res: Response, next: NextFunction): NextFunction => {
// reassign res.end in order to allow logger functionality before
// a response is sent back the client
const temp = res.end;
res.end = () => {
// instantiates PostQuery object with passed in query data from limiter middleware
const postQuery = new PostQuery(gateURI, projectID, res.locals.graphqlGate);
// our logger middleware functionality
try {
await postQuery.post();
} catch (err) {
if (err) console.log(err);
}
// our temp variable holding node's res.end definition
return temp.call(this);
};
return next();
};
Our test server throws this error when we include this function in our middleware chain:
TypeError: Cannot read properties of undefined (reading 'finished')
at end (node:_http_outgoing:856:19)
at /Users/jon/Documents/Solo Projects/OSP/graphQL-gate-logger/src/index.ts:65:25
index.ts:65 points to return temp.call(this)
We have also tried return temp() , as well as binding temp to the res object, and receive the same error in every instance.
Is there some other way we can reach this goal or do we have to start back at the drawing board?
If you don't have to execute your code BEFORE the response has been sent, but can instead do it right afterwards, then you can use the finish event on the res stream.
app.use((req, res, next) => {
res.on('finish', () => {
console.log(`got finish event for ${req.url}`);
// do your business here after a response has been sent
});
next();
});
There are also a couple problems with your existing override middleware. First off, you aren't preserving arguments that can be optionally send to res.end(). Second, res.end() is supposed to return res which makes it chainable. You aren't doing that. You have assigned it an async function which returns a promise, not res.
Though I think it would be much better to use the finish event as illustrated above and not have to override any methods, this would fix some of the problems with your override:
return (req: Request, res: Response, next: NextFunction): NextFunction => {
// reassign res.end in order to allow logger functionality before
// a response is sent back the client
const origEnd = res.end;
res.end = function(...args) {
// instantiates PostQuery object with passed in query data from limiter middleware
const postQuery = new PostQuery(gateURI, projectID, res.locals.graphqlGate);
// our logger middleware functionality
postQuery.post().catch(err => {
console.log(err);
}).finally(() => {
return origEnd.call(this, ...args);
});
return res;
};
return next();
};

Nestjs - how to get session in middleware

im trying to add graphql to nestjs app. I use postgraphile for the graphql server.
everything works, but i don't know how to protect the '/graphql' endpoint.
the login is done via session, through the nestjs app.
on the needed endpoints (in the existed nestjs app) there is a Session guard which works fine.
i want to add the same behavior to the graphql endpoint.
this is the graphql code:
export const graphqlBootstrap = async (app: INestApplication) => {
app.use(cors());
// what i want to achieve
app.use('/graphql', (req, res, next) => {
if (!req.session.user) res.send('Authetication Error`);
next();
})
app.use(
postgraphile(...)
and the nestjs code the init it:
const bootstrap = async () => {
const { app, start } = await createApplication(TearupsApplicationModule);
app.useGlobalInterceptors(new ApiResponseInterceptor());
configureHiddenFileUploadEndpoint(app.getHttpAdapter());
await Promise.all(app.get(INITIATION_SEQUENCE).map((fn) => fn()));
await graphqlBootstrap(app); // <--
await start();
app.get(EventsEmitter).init(app);
};
is it possible to use the session in a middleware?
I don't know how to reach the ExecutionContext that exist in the Guard.
I read in the docs that there is also ArgumentsHost which might hold the session, but i also don't know how to get it in simple middleware(app.use(...))
Edit 1
i tried to add a middleware to the app module, with forRoutes call.
but the middleware isn't called with the graphql route /graphql.
also, when using route: / - there is no session on the request.
middleware:
export function Logger(req: any, res: Response, next: NextFunction) {
if (req.session) console.log(`Request...`, req.session.user);
next();
}
App Module:
export class MyApp implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(Logger).forRoutes('/');
}
Edit 2
tried this now, still didn't work:
export class MyApp implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(logger)
.forRoutes({ path: 'api/tearups/graphql', method: RequestMethod.ALL });
}
}
Write a functional middleware as follow signature:
import { Request, Response, NextFunction } from 'express';
export function Logger(req: Request, res: Response, next: NextFunction) {
console.log(`Request...`, req.session.user);
next();
};
Or, a class-based middleware as:
#Injectable()
export class Logger implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log('Request...', req.session.user);
next();
}
}
Now apply the middleware as follows:
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(Logger)
.forRoutes(CatsController);
}
}

How to get respond object in service?

I want to use this package https://github.com/mashpie/i18n-node/blob/master/README.md in nestjs project.
How to get respond object in service for init module ?
i18n.init(req, res);
For request, I found it https://docs.nestjs.com/fundamentals/injection-scopes#request-provider
pass it from the controller
import {Response, Request} from 'express'
#Get(#Req() req: Request, #Res() res: Response) {
myService.method(req, res);
}

Request and Response Lifecyle in Node and Express

I have a very simple route setup for my web app,
router.get('/myTestRoute/:id', async (req: Request, res: Response, next: NextFunction) => {
await doSomeAsync(req, res);
res.json({ myObj: 32 });
return next();
}
The doSomeAsync actually performs a request to a service and takes up to 20 seconds. However the response that the user is looking for doesn't need or care about the doSomeAsync response, so I thought I could remove the await, so that it would look like this:
router.get('/myTestRoute/:id', async (req: Request, res: Response, next: NextFunction) => {
doSomeAsync(req, res);
res.json({ myObj: 32 });
return next();
}
Inside the doSomeAsync function, after the service call, we reference parameters on the req and res objects, like the parameters on the req object, and some authentication stuff on the res object.
I have found that this is not working, and I realized that I don't fully understand what happens with req and res after the response has been sent back to the user.
After I call res.json(), and then next(), what happens to the Request and Response objects, do they reset? Do they change in any way?
Should I refactor doSomeAsync() to accept the primitive values from req.params?

Wrapper for async handlers in Express with custom request properties

As demonstrated on https://strongloop.github.io/strongloop.com/strongblog/async-error-handling-expressjs-es7-promises-generators/#using-es7-asyncawait, I wanted to use a wrapper for all my async Express handlers to catch any errors happening there, but in a typed version for TypeScript.
I came up with this: (wrap.ts)
import { NextFunction, RequestHandler, Response, Request } from 'express';
type AsyncRequestHandler = (req: Request, res: Response, next: NextFunction) => Promise<any>;
/**
* Catches errors and passes them to the next callback
* #param handler Async express request handler/middleware potentially throwing errors
* #returns Async express request handler with error handling
*/
export default (handler: AsyncRequestHandler): RequestHandler => {
return (req, res, next) => {
return handler(req, res, next).catch(next);
};
};
I want to build a REST API with some endpoints like PUT /users/:userId and DELETE /users/:userId. For convenience, I don't want to query the specific user with the ID userId from the database in every handler and instead store it in req using a middleware. That means I have to use a modified Request interface for the handler definition adding a user property, e.g. UserRequest.
import express, { Request } from 'express';
import wrap from './wrap';
const app = express();
app.use('/users/:userId', wrap(async (req, res, next) => {
// set req.user ...
}));
export interface UserRequest extends Request {
user: User;
}
app.put('/users/:userId', wrap(async (req: UserRequest, res) => {
// do something with req.user ...
}));
// ...
This would be possible when not using wrap, but not with this type definition of wrap. The TypeScript compiler produces the following error:
Argument of type '(req: UserRequest, res: Response) => Promise<void>' is not assignable to parameter of type 'AsyncRequestHandler'.
Types of parameters 'req' and 'req' are incompatible.
Type 'Request' is not assignable to type 'UserRequest'.
Property 'user' is missing in type 'Request'.
What is the "TypeScript way" to accomplish this?
I somehow didn't realize that the issue appears without wrap as well (as long as the strict compiler option is enabled). My solution was to extend the express.Request interface.

Resources