Error creating verifyTokenAdmin middleware - node.js

I'm trying to create two middlewares, an authentication middleware and a middleware that checks if the user in question has "isAdmin" true inside its database.
The authentication middleware works perfectly, but to make the admin middleware work, I tried to put req.users = data; inside, but it returns me the error:
Property 'users' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.ts(2339)
auth middleware:
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
interface TokenPayload {
id: string;
iat: number;
exp: number;
}
export default async function authMiddleware(
req: Request, res: Response, next: NextFunction
) {
const { authorization } = req.headers;
if (!authorization) {
return res.status(401).json('Invalid Authorization');
};
const token = authorization.replace('Bearer', ' ').trim();
try {
const secret = process.env.JWT_SECRET as string;
const data = jwt.verify(token, secret);
req.users = data;
const { id } = data as TokenPayload;
req.userId = id;
return next();
} catch (err) {
return res.status(401).json(err);
}
}
In auth middleware admin, in users it returns the same error
Property 'users' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.ts(2339)
auth middleware admin
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import authMiddleware from './AuthMiddlewares';
export default async function authAdminMiddleware(
req: Request, res: Response, next: NextFunction,
) {
authMiddleware(req, res, () => {
if (req.users.isAdmin) {
next();
} else {
res.status(403).json("You are not alowed to do that!");
}
})
}

You can use declaration merging to declare the additional property users in your req object:
declare global {
namespace Express {
interface Request {
users: any
}
}
}
(See also here.)

Related

nodejs middleware typescript generic question

interface CustomRequest<T> extends Request {
user?: IUser,
body: T
}
interface CreateBody {
email:string;
}
interface deleteBody {
id:string;
}
router.post('/create', authenticate, (req:CustomRequest<CreateBody>, res) => {
Content.Create({
email:req.body.email
})
}
router.post('/delete', authenticate, (req:CustomRequest<deleteBody>, res) => {
Content.Create({
id:req.body.id
})
}
export const authenticate = async (
req: CustomRequest<null>
res: Response,
next: NextFunction,
) => {
try {
const user = await User.findToken(req.cookies.auth);
if (!user) return res.send({ success: false });
req.user = user;
next();
} catch (err) {
return res.send({ success: false });
}
};
I want to specify the request type by situation when designating middleware in nodejs.
req:CustomRequest
I've tried generics this way, but it doesn't work.
How can I define the type gracefully?
I would like to specify the type that some api have an e-mail in body and some api have a password in it.

How can I make an "isAdmin" verification middleware?

I have an authentication middleware, where I check through my route if the user is authenticated in the header with the security token
middlewareAuth
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
interface TokenPayload {
id: string;
iat: number;
exp: number;
}
export default async function authMiddleware(
req: Request, res: Response, next: NextFunction
) {
const { authorization } = req.headers;
if (!authorization) {
return res.status(401).json('Invalid Authorization');
};
const token = authorization.replace('Bearer', ' ').trim();
try {
const secret = process.env.JWT_SECRET as string;
const data = jwt.verify(token, secret);
const { id } = data as TokenPayload;
req.userId = id;
return next();
} catch (err) {
return res.status(401).json(err);
}
}
I import this middleware in my route, thus verifying as I describe below:
import { Router } from "express";
import CreatePost from "../controllers/postControllers/CreatePost";
import authMiddleware from "../middlewares/AuthMiddlewares";
const router = Router();
router.post('/', authMiddleware, CreatePost.store);
export default router;
Within my user entity, I have a boolean called "isAdmin" which by default returns false. I would like a middleware that checks if also within my profile isAdmin is true and thus authenticate and put in the route along with AuthMiddleware. For that I tried to import my AuthMiddleware and verify, but without success.
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import authMiddleware from './AuthMiddlewares';
export default async function authAdminMiddleware(
req: Request, res: Response, next: NextFunction,
) {
authMiddleware(req, res, () => {
if (req.userId.isAdmin) {
next();
} else {
res.status(403).json("You are not alowed to do that!");
}
})
}
Property 'isAdmin' does not exist on type 'string'.ts(2339)
What would be the most effective way to create this middleware? Can anyone give me some help? Thanks

Nodejs express app fails when using middleware on route

I am getting error Property 'admin' does not exist on type 'Request<{}, any, any, ParsedQs, Record<string, any>>'. Also nodejs app will not run as it fails with the same error. I am using typescript
app.get('/users', auth, (req, res) => {
console.log(` User is admin = ${req.admin}`);
console.log('Users Page');
res.send('Users Page');
});
function auth(req, res, next) {
if (req.query.admin === 'true') {
req.admin = true;
next();
} else {
res.send('No auth');
}
}
Make also sure that you properly structure the URL something like this http://localhost:3000/users?admin=true
import { NextFunction, Request, Response } from 'express';
app.get('/users', auth, (req: Request, res: Response) => {
console.log(` User is admin = ${req.admin}`);
console.log('Users Page');
res.send('Users Page');
});
declare module 'express-serve-static-core' {
export interface Request {
admin: boolean;
}
}
function auth(
req: Request<{}, {}, {}, {admin: boolean}>,
res: Response,
next: NextFunction) {
if (req.query.admin === 'true') {
req.admin = true;
next();
} else {
res.send('No auth');
}
}

Error authenticating user using express-session and Typescript

I have a small app that allows registration and login, but I'm still trying to use session-express to persist the session.
Below is server.ts where I create the session, cors, etc...
import express, { json } from "express";
import { db } from "./models/db-connection";
import { router } from "./routes";
import session from "express-session";
var cors = require("cors");
const app = express();
app.use(
cors({
origin: "http://localhost:3000",
methods: ["POST", "GET"],
credentials: true,
})
);
app.use(json());
app.use(
session({
secret: "testtest",
resave: false,
saveUninitialized: false,
})
);
app.use(router);
app.listen(3001, async () => {
try {
await db.sync();
console.log("Connected to the database");
} catch (error) {
console.error("Failed to connect to the database", error);
}
});
In the routes.ts script I use the authenticate function which will only allow a new user to be registered if an user is already logged in.
But the problem is exactly here, req.session.authenticated is never true, it is always undefined, even when I set it to true as I will show in UserController.ts.
Below is routes.ts.
import express from "express";
import UserController from "./controllers/UserController";
import "./session-data";
function authenticate(req: express.Request, res: express.Response, next: express.NextFunction) {
console.log(req.session);
if (req.session.authenticated) {
next();
} else {
res.redirect("/login");
}
}
const router = express.Router();
router.post("/users", authenticate, UserController.create);
router.get("/users/login/:login", UserController.findLogin);
export { router };
As you can see below in UserController.ts, req.session.authenticated is true when we find a match, I put in a console.log just to confirm that req.session has the authenticated property at this point, and it does, but it looks like routes.ts can't see it.
UserController.ts
import express, { Request, Response } from "express";
import { UserModel } from "../models/UserModel";
import "../session-data";
const bcrypt = require("bcryptjs");
class UserController {
async findLogin(req: express.Request, res: express.Response) {
const email = req.query.email?.toString();
const password = req.query.password?.toString();
try {
const user: any = await UserModel.findOne({
where: {
email: email,
},
});
if (user) {
const match = await bcrypt.compare(password, user.password);
if (match) {
req.session.authenticated = true;
console.log(req.session);
return res.status(204).json(user);
} else {
req.session.authenticated = false;
return res.status(200).send("invalid password");
}
} else {
req.session.authenticated = false;
return res.status(201).send("User not found");
}
} catch (error: any) {
req.session.authenticated = false;
return res.send(error.message);
}
}
}
async create(req: Request, res: Response) {
try {
const { userName, email, password } = req.body;
const user = await UserModel.create({
userName,
email,
password,
});
return res.status(201).json(user);
} catch (error: any) {
console.error(error);
return res.send(error.message);
}
}
Since I'm using Typescript, I need to create a session-data.ts file to expand req.session
session-data.ts
declare module "express-session" {
interface SessionData {
authenticated: boolean;
}
}
export {};
In the session store, the session is never created either.
Could you help me please? I don't know why req.session.authenticated isn't working, I'm new to using typescript, I imagine there's something related to that.
Thanks!
Try simplifying your code as much as possible so that the problem is still reproducible. The following works for me:
declare module "express-session" {
interface SessionData {
authenticated: boolean;
}
}
function login(req: express.Request, res: express.Response, next: express.NextFunction) {
req.session.authenticated = true;
res.end("Logged in");
}
function authenticate(req: express.Request, res: express.Response, next: express.NextFunction) {
console.log(req.session);
if (req.session.authenticated) {
next();
} else {
res.end("Not logged in");
}
}
express()
.use(session({
secret: "Se$$ion",
resave: false,
saveUninitialized: false
}))
.get("/login", login)
.get("/auth", authenticate, function(req, res) {
res.end("Welcome");
})
.listen(3001);
GET /login returns "Logged in".
Then GET /auth return "Welcome" and the session is logged:
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true },
authenticated: true
}

having trouble getting user authorization variables working

In am having trouble passing the token information to my controller for authorization. In the below code, I have console logged the relevant information and I am able to get the token correctly, and the decoded information, but not the req.user information. When I console.log for that information I receive null, and when I console.log for decoded.id, I get undefined. I believe this is what is hanging up my authorization, however I'm not sure what to look at to fix it? Any thoughts very helpful!
Here's a github link:https://github.com/roxanneweber/projectmanager
const jwt = require('jsonwebtoken');
const asyncHandler = require('express-async-handler');
const User = require('../models/userModel');
const protect = asyncHandler(async (req, res, next) => {
let token;
if (
req.headers.authorization &&
req.headers.authorization.startsWith('Bearer')
) {
try {
// Get token from header
token = req.headers.authorization.split(' ')[1];
console.log(token);
// Verify token
const decoded = jwt.verify(token, process.env.JWT_SECRET);
console.log(decoded);
// Get user from token
req.user = await User.findById(decoded.id).select('-password');
console.log(req.user);
console.log(decoded.id);
next();
} catch (error) {
console.log(error);
res.status(401);
throw new Error('Not authorized');
}
}
if (!token) {
res.status(401);
throw new Error('Not authorized');
}
});
module.exports = { protect };
hi i am handling the token like this in my auth.middleware.ts
import jwt from 'jsonwebtoken';
export default function (req, res, next) {
try {
const token = req.headers.authorization.split(' ')[1];
const decodedToken = jwt.verify(token, process.env.JWT_SECRET);
// add userData object to request
req.userData = {
email: decodedToken.email,
userId: decodedToken.userId,
username: decodedToken.username,
role: decodedToken.role,
};
next();
} catch (error) {
return res.status(401).json({
message: 'not authenticated',
});
}
}
then my frontend handles setting the token like this:
import { HttpInterceptor, HttpRequest, HttpHandler, HttpHeaders } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { AuthenticationService } from '../services/authentication.service';
export interface HttpConfig {
body?: any;
headers?: HttpHeaders;
observe?: any;
}
#Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private authService: AuthenticationService) {}
intercept(req: HttpRequest<any>, next: HttpHandler) {
const authToken = this.authService.getToken();
const authRequest = req.clone({
headers: req.headers.set('Authorization', 'Bearer ' + authToken),
});
return next.handle(authRequest);
}
}
then i am using the auth.middleware.ts in my backend route files like this:
import express from 'express';
import authMiddleware from '../middleware/auth.middleware';
import FooController from './foo.controller';
class FooRoutes {
router = express.Router();
fooController = FooController;
constructor() {
this.configureRoutes();
}
configureRoutes() {
this.router.post('/foo/start', authMiddleware, this.fooController.start);
this.router.put('/foo/stop/:id', authMiddleware, this.fooController.stop);
this.router.get('/foo/:userId', authMiddleware, this.fooController.getAll);
this.router.delete('/foo/delete/:id', authMiddleware, this.fooController.delete);
}
}
export default new FooRoutes().router;

Resources