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
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.
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
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');
}
}
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
}
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;