NestJs/Passport authentication doesn't work for routes passed asynchronously - passport.js

When I pass selected routes from the database in the promise then the authourization doesn't work. That means the requests for passed routes are always authorized.
protected applyRoutes(consumer: MiddlewaresConsumer) {
let paths = this.authPathService.findAll();
paths.then((resultPaths) => {
let result: {}[] = [];
for (let path of resultPaths) {
result.push({
path: path.path,
method: RequestMethod.ALL
})
}
consumer
.apply(passport.authenticate('jwt', { session: false }))
.forRoutes(...result);
return result;
}, (error) => {
console.log('error', error);
});
}
It works well when i pass routes in an object array
protected applyRoutes(consumer: MiddlewaresConsumer) {
consumer
.apply(passport.authenticate('jwt', { session: false }))
.forRoutes(...[
{ path: '/auth/authorized', method: RequestMethod.ALL },
{ path: '/auth/test', method: RequestMethod.ALL }]);
}

It's impossible to asynchronously apply middlewares using MiddlewaresConsumer. Instead, register an async component (https://docs.nestjs.com/fundamentals/async-components) that will fetch all paths, for example as AUTH_PATHS, then inject it to your module class, let's say AuthModule and use this array inside configure() method.

Related

Postman hangs when sending "POST" request to API

So i'm using Postman and i need to send a POST method request to my api but every time i do it hangs even if the handler does nothing except return a simple value. I'm using hapi so the syntax might differ a little bit from the standard express app.
currency.ts
//this is where i define the route methods and paths
import * as Hapi from "hapi";
import IRouteArea from "../server/IRouteArea";
import CurrencyController from "../controller/CurrencyController";
import hapiAuthJwt2 = require("hapi-auth-jwt2");
import { join } from "lodash";
import { RESOLVER } from "awilix";
const CurrencyArea = ({ currencyController }: any): IRouteArea => {
let _controller = currencyController as CurrencyController;
return {
registerRoutes(server: Hapi.Server) {
server.bind(_controller);
server.route({
method: "GET",
path: "/api/currencies",
options: {
auth: {
mode: "try"
},
plugins: { "hapi-auth-cookie": { redirectTo: false } },
handler: _controller.getCurrencies
}
});
server.route({
method: "POST", // this one is causing me problems
path: "/api/currencies/{id}",
options: {
auth: {
mode: "try"
},
plugins: { "hapi-auth-cookie": { redirectTo: false }},
handler: _controller.editCurrency
}
});
}
};
};
export default CurrencyArea;
and even though the actual handler of the request isn't doing anything special...
CurrencyController.ts
//here i define the handlers of requests sent to routes in the previous file
import * as Hapi from "hapi";
import { name } from "mustache";
import GetCurrencyListInteractor from "../../interactor/currencies/GetCurrencyListInteractor";
import Task from "../../runtime/Task";
export default class CurrencyController {
private task: Task;
constructor({ task }: any) {
this.task = task;
}
public async getCurrencies(
request: Hapi.Request,
h: Hapi.ResponseToolkit
): Promise<any> {
try {
return this.task.start<GetCurrencyListInteractor>(
"getCurrencyList",
t => t.execute()
);
} catch (error) {
return error as any;
}
}
public async editCurrency( //it's supposed to just return the parameter sent in the url
request: Hapi.Request,
h: Hapi.ResponseToolkit
): Promise<any> {
try {
return request.params.id;
} catch (error) {
return error as any;
}
}
}
it always hangs and gets stuck on the "sending request" screen when i send the request. One interesting thing is that the exact same route works as intended but only if the method defined in currency.ts is "GET". If i leave everything else the way it is and change the method from "GET" to "POST" or "PUT" or "PATCH" it stops working.
this goes on forever

DELETE method requires query parameter instead of path parameter in next.js

I am creating a CRUD in API,
but the delete does not seems to work properly.
I get a response from this
http://localhost:3000/api/admin/categories?id=1
and not from this
http://localhost:3000/api/admin/categories/1
this is the code in next.js:
export default async (req, res) => {
const {
query: { id },
method,
} = req;
switch (method) {
case "DELETE":
return res.status(200).json({
success: true,
id: id,
});
}
in React:
axios.delete(`http://localhost:3000/api/admin/categories/`, {id: 1})
The same situation is also with "PUT" method
Folder Directory:
api
|
---admin
--------categories
--------index.js
If you want to create API Routes such as DELETE, PUT ( they require /id as path parameter), you should create a separate file in that folder.
Like this:
api
|
---admin
--------categories
--------index.js
--------[id].js
And in the [id].js file:
export default function handler(req, res) {
const { id } = req.query;
if (req.method === "DELETE") {
res.end(`Category: ${id}`);
}
}

Assign route dynamically Node/Express

I need dynamically assign a new route but it for some reason refuses to work.
When I send a request in the Postman it just keeps waiting for a response
The whole picture of what I am doing is the following:
I've got a controller with a decorator on one of its methods
#Controller()
export class Test {
#RESTful({
endpoint: '/product/test',
method: 'post',
})
async testMe() {
return {
type: 'hi'
}
}
}
export function RESTful({ endpoint, method, version }: { endpoint: string, version?: string, method: HTTPMethodTypes }) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor): void {
const originalMethod = descriptor.value
Reflect.defineMetadata(propertyKey, {
endpoint,
method,
propertyKey,
version
}, target)
return originalMethod
}
}
export function Controller() {
return function (constructor: any) {
const methods = Object.getOwnPropertyNames(constructor.prototype)
Container.set(constructor)
for (let action of methods) {
const route: RESTfulRoute = Reflect.getMetadata(action, constructor.prototype)
if (route) {
const version: string = route.version ? `/${route.version}` : '/v1'
Container.get(Express).injectRoute((instance: Application) => {
instance[route.method](`/api${version}${route.endpoint}`, async () => {
return await Reflect.getOwnPropertyDescriptor(constructor, route.propertyKey)
// return await constructor.prototype[route.propertyKey](req, res)
})
})
}
}
}
}
Is it possible to dynamically set the route in the way?
I mainly use GraphQL but sometimes I need RESTful API too. So, I want to solve this by that decorator
In order for the response to finish, there must be a res.end() or res.json(...) or similar. But I cannot see that anywhere in your code.

Import module with folder and passing data to module in nodejs

I Found The Tutorial about
Designing a clean REST API with Node.js (Express + Mongo)
project in github.
but the problem is i didn't get the concept of routing in one part.
the misundrestanding part is how is it possible to pass httpRequest data to handle method within contact-endpoint module?
because handle method is in here export default function makeContactsEndpointHandler({ contactList }) {
return async function handle(httpRequest) {
this is the index of project:
import handleContactsRequest from "./contacts";
import adaptRequest from "./helpers/adapt-request";
app.all("/contacts", contactsController);
app.get("/contacts/:id", contactsController);
function contactsController(req, res) {
const httpRequest = adaptRequest(req);
handleContactsRequest(httpRequest)
.then(({ headers, statusCode, data }) =>
res.set(headers).status(statusCode).send(data)
)
.catch((e) => res.status(500).end());
}
this is the adaptRequest:
export default function adaptRequest (req = {}) {
return Object.freeze({
path: req.path,
method: req.method,
pathParams: req.params,
queryParams: req.query,
body: req.body
})
}
this is the handleContactsRequest module:
import makeDb from "../db";
import makeContactList from "./contact-list";
import makeContactsEndpointHandler from "./contacts-endpoint";
const database = makeDb();
const contactList = makeContactList({ database });
const contactsEndpointHandler = makeContactsEndpointHandler({ contactList });
export default contactsEndpointHandler;
this is part of contact-endpoint module:
export default function makeContactsEndpointHandler({ contactList }) {
return async function handle(httpRequest) {
switch (httpRequest.method) {
case "POST":
return postContact(httpRequest);
case "GET":
return getContacts(httpRequest);
default:
return makeHttpError({
statusCode: 405,
errorMessage: `${httpRequest.method} method not allowed.`,
});
}
}
makeContactsEndpointHandler is a function that returns a function (async handle(xxx)).
In handleContactsRequest, we export the result of the call: makeContactsEndpointHandler({ contactList }). Which is therefore the function async handle(xxx) itself.
So, in index, when we call handleContactsRequest with the constant httpRequest as argument, we're actually calling that handle(xxx) function. (I wrote xxx as parameter name to highlight the difference between the two httpRequest declarations.)

hapijs - serve static files from directory handler with variable path name

I'm trying to do something like this in my routes file using hapijs + inert plugin
{
method: 'GET',
path: '/l/{path*}',
handler: {
directory: {
path: (req) => {
const defaultDir = '/public';
return getHostInfo(req.headers.host).then((d) => {
if (!d || !d.directory) return defaultDir;
return d.directory;
}).catch((e) => {
return defaultDir;
});
}
}
}
},
path parameter expects a string, array or function which returns a string or array...in my case, my function returns a promise...so it doesn't work.
I tried adding hapi-as-promised package which modifies reply function to support then method but did't work.
Basically I want to serve static assets from one directory or another depending on the host header value.
Well, the only thing i have in my mind is this hack. First, you have to make an extension point to make your async stuff:
server.ext({
type: `onPreHandler`,
method: (request, reply) => {
const defaultDir = '/public';
return getHostInfo(request.headers.host).then((d) => {
if (!d || !d.directory) {
request.app.dirPath = defaultDir;
return;
}
request.app.dirPath = d.directory;
}).catch((e) => {
request.app.dirPath = defaultDir;
}).then(() => {
return reply.continue();
});
}
});
And then the route:
{
method: `get`,
path: `/l/{path*}`,
handler: {
directory: {
path: (request) => {
return request.app.dirPath;
}
}
}
}
I don't know how right is this way, but i tested and it worked. So i hope this helps. And i gotta notice, using node to server files on production isn't a common way by some reason.

Resources