Check user that can view my files in my server - node.js

I want to secure my files and not anyone can view my files in express, I want to check who has logged in into my website and then authenticate if he has access to view this file or not , how can I achieve that this is the code I am using to access my files url :
app.use("/profile", express.static(__dirname + '/profile'));
I want only the user that logged in, he is the only one that can view his profile image and if he is not the user then say for example you are not allowed to view this file.
I am using mongodb as a backend but i don't know if its mongodb authentication or express middleware security ?

I Discovered how to solve this :
const checkImg = async (req, res, next) => {
const token = req.cookies.jwt;
if (token) {
jwt.verify(
token,
process.env.JWTKEY,
async (err, decodedToken) => {
if (err) {
res.json({ status: false });
} else {
const user = await UserModal.findById(decodedToken.id);
const imgUrl = req.originalUrl.split('/')[2];
const imgUser = user.profileImg;
if (imgUrl === imgUser || user.admin === "ADMIN") {
next();
}
else {
return res.send("Your are not allowed to view this file")
}
}
}
);
}else{
return res.send("you are not allowed !")
}
}
and here is when i use my middleware :
app.use("/images", checkImg , express.static(__dirname + '/images'));
this code checks if the user is the same user then he can only see the data and also the admin user can display the data , otherwise send him that he is not allowed to get the data

Related

Delete User and Logout that user from all Devices

I wanted to implement a feature in my app. Where an Admin can delete the user. So basically the delete is working fine but somehow i cannot logout the logged in user. Let me explain it more briefly, Suppose there is a User A which is currently using my app and the admin decided to remove that user from the app so they can't no longer access the features of the app. To remove the user i can call an API and delete that user but if i completely delete the user it loses all the access to the API's call coz user with the certain ID isn't available anymore and the app breaks coz the API call will fail for that deleted User. So I was wondering is there anyway to logout the user after admin deletes it.
The Frontend is on ReactJs and Backend is on NodeJs. And i am using JWT for authentication. Any help will be appreciated and if this question isn't clear enough please let me know so i can explain it more.
In backend in every protected route you should verify the token and token should contain user id or email using that you will verify the token. After deleting the user throw error with no user found and in frontend make sure if there are the error no user found then it will delete the JWT token.
What comes into my mind is to put a middleware between your requests and server. By doing so, instead of trying to log out from all devices, we will not allow any action if user does not exist; in this very example, we will prevent the user to delete a place and toast a message on the front end. I will share an example of that, but you need to tweak the code according to your needs.
Http Error Model
class HttpError extends Error {
constructor(message, errorCode) {
super(message);
this.code = errorCode;
}
}
module.exports = HttpError;
Middleware
const HttpError = require('../models/http-error');
module.exports = (req, res, next) => {
try {
// Check user if exists
User.findById(req.userData.userId).exec(function (error, user) {
if (error) {
throw new Error('Authentication failed!');
}
else {
return next();
}
});
}
catch (error) {
return next(new HttpError('Authentication failed!', 403));
}
};
Route
const express = require('express');
const router = express.Router();
const checkAuth = require('../middleware/check-auth');
router.use(checkAuth);
// Put after any routes that you want the user to be logged in
router.delete('/:placeId', placesControllers.deletePlace); //e.x.
...
module.exports = router;
E.x. controller (with MongoDB)
const deletePlace = async (req, res, next) => {
const placeId = req.params.placeId;
let foundPlace;
try {
foundPlace = await Place.findById(placeId).populate('userId').exec();
}
catch (error) {
return next(new HttpError('Could not find the place, please try again', 500));
}
// Delete place
res.status(200).json({message: 'Deleted place'});
};
FRONT END PART
import toastr from 'toastr';
....
try {
const response = await fetch(url, {method, body, headers});
const data = await response.json();
if (!response.ok) {
throw new Error(data.message);
}
}
catch(error) {
// handle the error, user not found
console.log(error.message);
toastr.error(error.message, 'Error', {
closeButton: true,
positionClass: 'toast-top-right',
timeOut: 2000,
extendedTimeOut: 1,
});
}

Error while trying to login with through Steam from node.js

I'm trying to login through steam from my webapp but I'm having an hard time.
This is the code in my backend (I'm using Firebase cloud functions) that let me authenticate my user.
const steam = new SteamAuth({
realm: "https://storm.co.gg", // Site name displayed to users on logon
returnUrl: "http://localhost:5001/stormtestfordota/europe-west1/api/auth/steam/authenticate", // Your return route
apiKey: apiKey // Steam API key
});
let loggedUser = "";
const redirectSteamAuth = async (req, res) => {
loggedUser = req.user.username;
const redirectUrl = await steam.getRedirectUrl();
return res.json(redirectUrl);
}
So this is the first endpoint that the user calls when trying to login to Steam. And it works, so the steamcommunity.com opens without problem.
But when I click login in steamcommunity page I'm prompted with this error
So over the name of my account you can see "ERRORE" that stands for "ERROR"
This is the endpoint that should be called after authentication:
const loginWithSteam = async (req, res) => {
try {
const user = await steam.authenticate(req);
db.collection("users").doc(loggedUser).update({
steamId: user.steamid
})
activeDota2(loggedUser, user.steamid);
return res.redirect("https://storm.co.gg/dashboard/home");
} catch (error) {
console.error(error);
return res.status(401).json(error)
}
}
These are the two endpoints:
app.post("/auth/steam", (req, res, next) => validateFirebaseIdToken(req, res, next), redirectSteamAuth);
app.get("/auth/steam/authenticate", loginWithSteam);
I solved this issue. The problem was in the urls of the steam object AND there was a problem with CORS options, I didn't add the DNS of steamcommunity in origins accepted by CORS.

Node.js middleware to read more than one mongoose collection

I am new to authentication with node.js and am struggling to implement the following:
I currently have a middleware function that checks the access token sent with the request and pulls the user relating to that token, then appends that user onto the request so I can use their details. This works completely fine for my Users collection, however I am wanting to add a new collection for a completely different type of user, called Owners.
Based on the function I currently have, I cannot seem to find a way to have it check both collections - this is my current function that works with my one Users collection.
//
// Middleware that authenticates token and appends user to request
//
module.exports.required = function (req, res, next) {
const auth_header = req.header("authorization").split(" ");
const auth_type = auth_header[0];
const auth_token = auth_header[1] || null;
// Check token
if (auth_type !== "Bearer" || !auth_token) {
return next(HttpError(401, "Token is invalid."));
}
// Find user matching access token
return User.findOne({ access_token: auth_token })
.orFail(HttpError(401, "Token does not exist."))
.then((user) => {
try {
// Check if token has no expired
decodeToken(auth_token);
} catch (err) {
if (err.name !== "TokenExpiredError") return next(err);
// Refresh token
user.generateAccessToken();
// Save and return new user
return user.save();
}
return user;
})
.then((user) => {
// Append user object to the incoming request
req.user = user;
return next();
})
.catch(next);
};
Can anyone help me understand out how I would check both collections (Users & Owners)?

How to show logged in user info in all routes NodeJS

So this is my code that shows if user session exists and if it exists then it renders user info with the file so I can display the logged in user info there.
app.get('/', async(req, res) => {
if(req.session.user && req.cookies.user_sid){
let user = await User.findOne({username: req.session.user.username});
res.render('index', {user});
} else {
res.render('index');
}
});
But now I want to display user info and in another routes. So in my opinion it would be stupid to repeat again that if statement. What alternative could be there?
It's best to repeat that statement and make the DB call again. In the time between the first request and any subsequent requests, the user may have been logged out (such as by cookies expiring) or user data might have been changed in the database, so it's best to not cache the data and check every time.
Method 1
Add a user details middleware ,which checks if user details is available in session or not and then updates the session object if not available.This way you will avoid redundant calls to db across routes.
app.use(function (req, res, next) {
if(req.session && req.session.userDetails === undefined){
const userDetails = await User.findOne({username: req.session.user.username});
req.session.userDetails = userDetails || undefined;
//optional
req.user = {};
Object.assign(req.user, req.session.userDetails);
}
next()
})
You can pass the userDetails in all your routes with reference to req.user or req.session.userDetails, something like
app.get('/profile', async(req, res) => {
res.render('profile', {user : req.user});
})
Method 2
You can also save the user details in session when the user successfully logs in and use the session reference in all routes, something like
app.post('/authenticate', async (req, res) => {
const userDetails = await User.findOne({ username: req.body.username, password: req.body.password });
if (userDetails.length > 0) {
//when authentication is successsful
req.session.user = userDetails;
}
});

Verifying headers at top-level

I have a Node.js app built with Express.js framework.
I want to check that the user is authorized to do a certain request, I do this by requiring the clients to supply an access token in a header.
I don't want to add this to each of the individual functions that the clients have access to. Like this, for an info request about a user:
exports.info = function(req, res) {
var userId = req.params.id,
accessToken = req.headers["accesstoken"];
console.log("received request to get info for userID <"+ userId +">");
users.User.findOne({accessToken: accessToken}, function(err, user) {
if(user == null) {
...
How can I do this at a higher level? Can I set this header requirement somewhere on a global for express?
I want to do this basically for all functions except for the user login function, so all functions except for one.
You can make a small middleware:
verifyUser = function(req,res,next){
var userId = req.params.id, accessToken = req.headers["accesstoken"];
console.log("received request to get info for userID <"+ userId +">");
users.User.findOne({accessToken: accessToken}, function(err, user) {
if(user == null) {
...
}
next()
}
}
Then:
On one request:
app.get("/user/info", verifyUser, exports.info)
On a selection of requests:
app.all(SomeRegex, verifyUser)
On all resquests:
app.use(verifyUser)
You can create a middleware and set it up on each route, you need to authorize. Example:
var myAuthMiddleware = function (req, res, next) {
// Here goes your code to check if the user complies
// with the conditions. You can use req.headers, req.user, etc
if (conditionIsMet) return next(); // If the user complies, you continue the process
// The user doesn't comply
return res.send('Error');
}
Then, you use his middleware in the needed routes:
app.get('/my-route', myAuthMiddleware, myRouteHandler);
app.post('/another-route', myAuthMiddleware, myOtherRouteHandler);
// This one doesn't need auth
app.get('/', indexHandler);
Just add your function as one more of the express middleware that runs before all your request processing.
app.use(function(req, res, next) {
var userId = req.params.id,
accessToken = req.headers["accesstoken"];
console.log("received request to get info for userID <"+ userId +">");
users.User.findOne({accessToken: accessToken}, function(err, user) {
if(user != null) {
return next(); // This is ok, keep processing
} else {
// don't call next, redirect to login page, etc...
}
}
app.get('/home', ...);
apg.get('/some_other_page');
You call next to get express to process as usual, or you use redirect, or return an error and don't call next.

Resources