express js - error handling middleware with auth0 - javascript

I have the following express server:
import * as express from "express";
import * as bodyParser from "body-parser";
import * as mongoose from "mongoose";
import { Routes } from "./routes/transactions";
import { authMiddleware } from "./middleware/auth";
import { errorMiddleware } from "./middleware/error";
class App {
public app: express.Application;
public routeProvider: Routes = new Routes();
public mongoUrl: string = "mongodb://...:...#mydomain.com:27017/mycollection"; // This should be read from github CLI
constructor() {
this.app = express();
this.config();
this.routeProvider.routes(this.app);
this.mongoSetup();
}
private config(): void {
this.app.use(bodyParser.json());
this.app.use(bodyParser.urlencoded({ extended: false }));
this.app.use(errorMiddleware);
this.app.use(authMiddleware);
}
private mongoSetup(): void {
mongoose.Promise = global.Promise;
mongoose.connect(
this.mongoUrl,
{ useNewUrlParser: true }
);
}
}
export default new App().app;
This is the authMiddleware (almost the same as the guide on the auth0 site for usage with node.js):
import * as jwt from "express-jwt";
import { expressJwtSecret } from "jwks-rsa";
export function authMiddleware() {
return jwt({
secret: expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: "..."
}),
audience: "...",
issuer: "...",
algorithms: ["RS256"]
});
}
And my error middleware:
import { Request, Response } from "express";
export function errorMiddleware(err, req: Request, res: Response, next) {
console.error(err.stack);
res.status(500).send("Something broke!");
}
Now what would expect to happen - since I broke all the auth and mongodb URLs on purpose, that I would get a status code 500 with the message Something broke!.
Instead I get the
Could not get any response
message when sending a requst using postman.
What am I doing wrong?

Related

NestJS - How to set 'Access-Control-Allow-Origin' Header for Response

I am trying to set the Response header in NestJS, but keep getting the following error:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://companyName.okta.com/app/companyName_imentorlocalhost_1/exk1hp5ht4vrEzqGg0h8/sso/saml?SAMLRequest=nVPLct... (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
I tried setting the header in the controller, but that didn't work:
auth.controller:
#UseGuards(SamlAuthGuard)
#Header('Access-Control-Allow-Origin', '*')
#Get('box-utility-service/auth/login')
login(#Request() req): any {}
#UseGuards(SamlAuthGuard)
#Header('Access-Control-Allow-Origin', '*')
#Post('imentor-service/login/callback')
oktaCallback (#Request() req, #Response() res: Response): any {
return this.authService.login(req);
}
Also tried setting the header in an interceptor. Didn't work either:
header.interceptor.ts:
#Injectable()
export class HeaderInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
tap(() => {
const res = context.switchToHttp().getResponse();
res.setHeader('Access-Control-Allow-Origin', '*');
})
)
}
}
Here's my main.ts, where I enable CORS:
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(
session({
secret: 'my-secret',
resave: false,
saveUninitialized: false
}),
);
app.enableCors({
allowedHeaders: [ 'Accept', 'Accept-Version', 'Content-Type', 'Api-Version', 'Origin', 'X-Requested-With',
'Authorization' ],
origin: [ 'https://companyName.okta.com', 'http://localhost:4200', 'http://localhost' ],
credentials: true,
exposedHeaders: [ 'API-Token-Expiry' ]
});
app.useGlobalInterceptors(new HeaderInterceptor());
await app.listen(3000);
}
bootstrap();
And here is my saml-strategy.ts file, where I define the Passport strategy to be SAML:
import { Injectable } from '#nestjs/common';
import { PassportStrategy } from '#nestjs/passport';
import { AuthService } from './auth.service';
const nconf = require('nconf');
import { get } from 'lodash';
const SamlStrategy = require('passport-saml').Strategy;
import { UsersService } from '../users/users.service';
#Injectable()
export class Saml2Strategy extends PassportStrategy(SamlStrategy, 'saml') {
users = [];
constructor(
private authService: AuthService,
private usersService: UsersService
) {
super({
issuer: nconf.get('saml:issuer'),
path: nconf.get('saml:path'),
entryPoint: nconf.get('saml:entryPoint'),
cert: nconf.get('saml:cert')
});
}
async validate(payload: any) {
const oeid = payload.nameID;
let user;
if (oeid) {
try {
let userADData = await this.authService.validateUser(oeid);
userADData = get(userADData, 'data.data[0]');
if (userADData) {
user = await this.usersService.findOrCreate(userADData);
}
return user;
} catch (err) {
return err;
}
}
}
}
Any idea of what's going on? Thanks.
Turns out it was a problem with the way I was handling the OKTA login workflow. I was redirecting an XHR request (which OKTA didn't like, as it was missing the required header to do so), and was getting an error code.

Request not being processed

Im currently trying to learn more about Express and Express-Validator but currently facing the following issue: When I'm starting the server and using Postman do excess one of the endpoints the response is not completed. However, it seems like the Validation Chain is being processed but afterwards nothing happens. I have the following modules:
index.ts
import {config} from 'dotenv';
import App from './api/app';
import ControlAmbilight from './api/routes/controlAmbilight';
import AdjustLightning from './app/AdjustLightning';
const ENV_FILE = path.join(__dirname, '..', '.env');
config({path: ENV_FILE});
const PORT = process.env.port || process.env.PORT || 3000;
const ambient = new AdjustLightning();
const app = new App([
new ControlAmbilight(ambient),
], <number> PORT);
app.listen();
app.ts
import * as bodyParser from 'body-parser';
import pino from 'pino';
import expressPino from 'express-pino-logger';
import errorMiddleware from './middleware/errorMiddleware';
export default class App {
private logger: pino.Logger;
private expressLogger;
public app: express.Application;
public port: number;
constructor(controllers: any, port: number) {
this.app = express();
this.port = port;
this.logger = pino({level: process.env.LOG_LEVEL || 'info'});
this.expressLogger = expressPino({logger: this.logger});
this.initializeMiddlewares();
this.initializeControllers(controllers);
this.initializeErrorHandling();
}
private initializeMiddlewares() {
this.app.use(this.expressLogger);
this.app.use(bodyParser.json());
}
private initializeControllers(controllers: any) {
controllers.forEach((controller: any) => {
this.app.use('/', controller.router);
});
}
private initializeErrorHandling() {
this.app.use(errorMiddleware)
}
public listen() {
this.app.listen(this.port, () => {
this.logger.info(`Server running on ${this.port}`);
});
}
}
controlAmbilight.ts
import {Router, Request, Response, NextFunction} from 'express';
import {ValidationChain, check, validationResult} from 'express-validator';
import AdjustLightning from '../../app/AdjustLightning';
// eslint-disable-next-line require-jsdoc
export default class ControlAmbilight {
private ambient: AdjustLightning;
// eslint-disable-next-line new-cap
public router = Router();
public path = '/controlAmbilight';
// eslint-disable-next-line require-jsdoc
constructor(ambient: AdjustLightning) {
this.ambient = ambient;
this.initializeRoutes();
}
// eslint-disable-next-line require-jsdoc
public initializeRoutes() {
this.router.post(this.path, this.controlValidator, this.setAmbilight.bind(this));
}
private controlValidator = (): ValidationChain[] => [
check('on').notEmpty().withMessage('Field \'on\' is required'),
check('on').isBoolean().withMessage('Field \'on\' must be type boolean'),
];
// eslint-disable-next-line require-jsdoc
private setAmbilight(req: Request, res: Response): void {
const errors = validationResult(req);
if (!errors.isEmpty()) {
res.status(422).json({error: errors.array()});
} else {
const isOn = (req.body.on == 'true');
res.send(`The curent state is: ${this.ambient.getIsActive()}`);
}
}
}
I was hopping that someone could explain me what I'm missing here. It seems like I need to call express` next() middleware function, but I'm not sure where to implement it.
EDIT
As requested I'm adding the errorMiddleware:
import { NextFunction, Request, Response } from 'express';
import HttpException from '../exceptions/HttpException';
export default function errorMiddleware (error: HttpException,
request: Request, response: Response, next: NextFunction) {
const status = error.status || 500;
const message = error.message || 'Ups... This did not work :(';
response
.status(status)
.send({ status,
message });
}
And as an additional comment: When I'm adding the Validation Chain directly into the post method within controlAmbilight.ts like that:
public initializeRoutes() {
this.router.post(this.path, [
check('on').notEmpty().withMessage('Field \'on\' is required'),
check('on').isBoolean().withMessage('Field \'on\' must be type boolean'),
], this.setAmbilight.bind(this));
}
It is working as expected.

Socketio not correctly working with Nodejs/Typescript and React

I have been trying to make socket.io work with my Nodejs and Typescript backend and React + JSX frontend, this is my first experience with socket.io but it has been a nightmare.
This is the server.ts file
import Express, {Application} from 'express';
import dotenv from 'dotenv';
const app: Application = Express();
import http from 'http';
//getting the Socket Type from socket io
import {Socket} from 'socket.io';
//importing the IO object form seperate file
import {init, socket} from './utils/socketio'; // <--- seperate socketio.ts file
import teacher_route from './Routes/teacher';
import student_route from './Routes/student';
import cookie_check_route from './Routes/cookieCheck';
import bodyParser from "body-parser";
import cors from 'cors';
import cookieParser from "cookie-parser";
const server = http.createServer(app);
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
dotenv.config();
app.use(cors({
origin: "http://localhost:3000",
credentials: true,
}));
//initializing the io
init(server);
app.use(cookieParser());
app.use('/t', teacher_route);
app.use('/s', student_route);
app.use('/c', cookie_check_route);
//connecting the io
socket().on("connection", (socket: Socket)=> {
console.log("reached server")
socket.on("joined", (data: object)=> {
console.log("reached joined")
//#ts-ignore
socket.emit("joined2", {
//#ts-ignore
full_name: "student[0].full_name",
attendance: "",
average_score: "",
average_attendance: ""
})
})
})
server.listen(process.env.PORT);
SocketIO.ts file on the backend
import socketIo, {Socket} from 'socket.io';
let io: any;
//#ts-ignore
const init = (httpServer)=>{
//#ts-ignore
io = socketIo(httpServer, {
cors: true,
origins: ["http://127.0.0.1:3000"]
});
return io;
}
const getIO = () => {
if(!io){
throw new Error("Socket.io is not initialised!");
}
return io;
}
const socket = () => {
return getIO().on("connection", (socket: Socket)=> {
// console.log("inside socketio.ts file")
return socket;
})
}
export {init, getIO, socket};
This is the useEffect hook in StudentPortal.jsx file
useEffect(()=> {
setLoader(true)
socket.emit("joined"); // <--- emmiting an event
axios.get(`${config.apiEndpoint}/s/checkclasses`, {
withCredentials: true
}).then(response => {
setClasses(true);
setDisableSubmit(false);
setLoader(false)
if(response.data.class_data_array){
setClassData(response.data.class_data_array.reverse());
}
});
}, [addedClass])
And Finally this is the Class.jsx file where i want to finally get that event
socket.on("joined2", (data)=> {
// setClassData([...classData, data]);
console.log("Class.jsx socket");
})
So the gist of this is that i am emmitting an event from StudentPortal.jsx named joined and listening for it in the Server.ts and in it's body, i am emmitting another event named Joined2 and listening for it in the Class.jsx, i have tried many many different things, placed socket connection in different files, tried to connect first and then emmit, and so much more, nothing seems to work.
Any help is truly appreciated.
Thank you

express.session undefined in typescript

I am very new to typescript/javascript, I am trying to build backend rest apis with session
following is app.ts file
import express from "express";
import { applyMiddleware, applyRoutes } from "./utils";
import routes from "./services";
const app = express();
var ses= {
secret: "secret_session",
resave: true,
saveUninitialized: true,
cookie: { maxAge: 3600000,secure: false, httpOnly: true
}
if (app.get('env') === 'production') {
app.set('trust proxy', 1)
ses.cookie.secure = true
}
app.use(session(ses));
applyRoutes(routes, app);
I have started the server and applied the middlewares for error handling but those are not related to question in my opinion so I'm not adding code for it. Following is my routes.ts code where I'm trying to set the session.
import { Request, Response } from "express";
import { getAll, getByKeyword, addNewProduct } from "./productControllers";
{
path: "/api/v1/getAllProducts",
method: "get",
handler: [
(req: Request, res: Response) => {
getAll()
.then((row: any) => {
var sess = req.session;
sess.views = 1;
res.status(200).json({ data: row });
})
.catch(err => {
res.json({
message: err
});
});
}
]
}
I'm getting error at sess.views = 1;
I have tried the suggested questions before asking it, none of them were of any help to me.
EDIT:
I have created an index.ts
import searchRoutes from "./products/routes";
export default [...searchRoutes];
I have another util class
export const applyRoutes = (routes: Route[], router: Router) => {
for (const route of routes) {
const { method, path, handler } = route;
(router as any)[method](path, handler);
}
}
You are using an interface which is Request for express.js. But it doesn't have type definition for session. So typescript throws a compile error. To solve it you need to define session type under Request interface.
You could define a session.d.ts file under your project. And create required types & interfaces. Like:
declare global {
namespace Express {
interface Request {
session?: Session;
sessionID?: string;
}
}
}
interface Session{
mySessionVarible:string
}
But the good thing is we have DefinitilyTyped project which you can find many type definitions. This needs to solve your compile problem.
npm install --save-dev #types/express-session
And don't forget to change your import for Request.
import { Request, Response } from "#types/express-session";

Getting user agent access in the resolver in graphql

I am relativity new to graphQL but this is annoying me. I want to get the user agent from the request body being sent from the client side. I can get access to the user-agent in the middleware however when I call the next function with any parameter to send to the resolver, I don't get any data from it. If I don't pass any parameters into next() then the resolver works as expected however parent, args, User and Session do not contain any information about the request headers. Any help or general tips would be greatly appreciated! Thanks!
app.js
import express from 'express';
import bodyParser from 'body-parser';
import mongoose from 'mongoose';
import { graphiqlExpress, graphqlExpress } from 'apollo-server-express';
import { makeExecutableSchema } from 'graphql-tools';
import typeDefs from './Graphql/typeDefs';
import resolvers from './Graphql/resolver';
import { User } from './Mongoose/Schemas/user';
import { Session } from './Mongoose/Schemas/session';
mongoose.connect('mongodb://localhost/test');
const schema = makeExecutableSchema({
typeDefs,
resolvers,
});
const helperMiddleware = [
bodyParser.json(),
bodyParser.text({ type: 'application/graphql' }),
(req, res, next) => {
if ( req.body ) {
console.log(req.headers['user-agent']);
}
next();
},
];
const PORT = 3009;
const app = express();
app.use('/graphql', ...helperMiddleware, graphqlExpress({ schema, context: { User, Session } }));
app.use('/graphiql', graphiqlExpress({ endpointURL: '/graphql' }));
app.listen(PORT);
console.log(`Running On Port ${PORT}`);
resolver.js
Mutation: {
createUser: async (parent, args, { User, Session }) => {
const user = await new User(args).save();
user._id = user._id.toString();
const session = await new Session({
user_id: user._id,
userAgent: 'Nothing ATM',
ip: 'Nothing ATM',
}).save();
return user;
},
You need to use the callback version of creating the GraphQL server middleware, otherwise you have no way of constructing context based on the current request:
https://www.apollographql.com/docs/apollo-server/setup.html#options-function

Categories

Resources