Not being able to retrieve data saved in cookie-session - node.js

I am trying to make a dynamic website in Cloud Functions for Firebase.
However, I am stuck at this point.
What I am trying to achieve is that I want to save particular form details and then push them to the DB if the user is logged in. If the user is not logged in I would like to save those forms details somewhere and then redirect the user to login. When he does login I save the form details under his userid.
Now I thought of using firebase-cookie-session for this. Since it's the only one allowed in firebase cloud functions.
However, my problem is that when the user is redirected to login. I no longer have access to the session in that request.
This is how I approached this:
I setup my express app:
var session = require('firebase-cookie-session');
app.use(session({
keys: ['cookie_secret'],
name: 'session',
sameSite:true,
proxy: true,
resave: true,
saveUninitialized: true,
maxAge: 360*12*24*36*36
}));
then in post I save data in session: -> console.log(req.session.packageDetails) works perfectly
homepageRouter.post("/save-form-details", function (req, res, next) {
const packageDetails = JSON.parse(JSON.stringify(req.body));
firebaseController.isUserSignedIn().then(signedinUser => {
if(signedinUser){
firebaseController.getCurrentUser().then(currentUser => {
firebaseController.putPackageToUser(currentUser, packageDetails).then(success => {
return success;
});
}).catch(errorMessage => {
console.log(errorMessage);
});
} else {
req.session.userSignedIn = false;
req.session.packageIncluded = true;
req.session.packageDetails = packageDetails;
console.log('Hello Hello Hello Hello Hello Hello');
console.log(req.session.packageDetails);
res.send({
userSignedIn: false,
redirectTo: '/signup-login'
});
}
console.log('User Status: ' + signedinUser);
console.log('Server Side: ' + JSON.parse(JSON.stringify(req.body)));
});
});
After the redirection from the above to below the session goes to undefined, No idea why.
signupLoginRouter.post("/login", function (req, res, next) {
const accountJSON = JSON.stringify(req.body);
let account = JSON.parse(accountJSON);
console.log('HEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEELP');
console.log(req.session.packageDetails);
}
I think I either setup something wrong or understood something wrong. If anyone can help. Thay would be appreciated

Related

Check user that can view my files in my server

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

Cookies persist in supertest/superagent test, but the user doesn't stay logged in

My Goal
I'm trying to use supertest's agent function in a jest beforeEach() to login the user before each test, as I want each test to run under the assumption that the user is signed in. For authentication, I am using passport and passport-local.
This is what I tried (with parts cut out for brevity):
Test file:
import { agent, SuperAgentTest } from 'supertest';
import app from '../../src/app';
// create a `testRequest` variable to use in the tests
// that will be refreshed in between
let testRequest: SuperAgentTest;
const fakeUser = { email: 'john#john', username: 'john', password: 'john' };
beforeEach(async () => {
// create new agent
testRequest = agent(app);
// register and login
await testRequest.post('/register').send(fakeUser).expect(302);
// other irrelevant stuff...
});
// protected route
describe('POST /campgrounds/new', () => {
it('returns 200 OK', () => {
return testRequest.get('/campgrounds/new');
})
});
/register route:
router.post('/register', async (req, res) => {
const { password, ...details } = req.body;
try {
// I am using passport-local-mongoose for this function-
// it just registers the user
const user = await User.register(new User(details), password);
req.login(user, (err) => {
// error handling and redirect
});
} catch (e) {
// error handling
}
})
This is my result
Instead of a 200 status, I get a 302 status, meaning I was redirected to the login page. To debug this, I created a test route called /current which will log the current user and session ID cookie. I then sent a GET request to this route in both the it and beforeEach function respectively.
Interestingly, they both logged the same session ID, but only the request in beforeEach had a user object attached to the request.
#1 Ensure body parser correct order
Make sure you have this before any routes or auth-related things.
app.use(express.json())
#2 Check Passport Middleware Wire-up
Ensure you call app.use(passport.initialize()) & app.use(passport.session()) before any app.use('/', aRouter), router.get, router.post, etc:
// Set up session w/ specific config
app.use(session({
secret: 'bquyqueajhbd',
resave: true,
saveUninitialized: true,
store: new FileStore({path: '/tmp/session'})
}));
// Wire up the
app.use(passport.initialize())
app.use(passport.session())
EDIT: Notes on req.user
Passport is designed to store the user ID in session.
Every request to the server must reload the user from the database.
This is the job of the middleware passport.initialize() and passport.session().
The logic there will call passport.deserializeUser to lookup the user by ID - the same ID that was saved upon login into the session by passport.serializeUser.
passport.serializeUser(function(user, done) {
done(null, user.id); // <-- Here's where the ID is saved to session.
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user); // <-- Here is where the `req.user` get's it's value from.
});
});
To debug this I'd focus on the passport.deserializeUser callback, add logs before and after the DB query.
(Note: it's been a few years since I taught this. Appologies if I'm not using the precise terms, etc.)

How can I redirect a user to a different part of my web application within a function that sends a cookie to the browser?

I am trying to edit the block of code below so that it will redirect the browser to the main page of my web application ("/") after the function is executed. I have tried adding "location: '/'" to the res.status(200) object, but that did not work (nothing happens). I also tried adding res.redirect('URL') to the code, that did not work either. Any help would be very much appreciated. Please go easy on me, I'm new to this.
exports.logout = (req, res) => {
res.cookie('jwt', 'loggedout', {
expires: new Date(Date.now() + 5 * 1000),
httpOnly: true
});
res.status(200).json({ status: 'success' });
};
Adding an answer here since the comment placeholder text says to avoid answering questions in comments (and comments don't allow for neat code).
On the client side, check for the response status. If it is successful, redirect..
// Some client-side sudo code
const resp = await fetch('mywebsite.com/logout', { options });
const logoutResult = await resp.json();
if (logoutResult.status === 'success') {
// Redirect to "/" on successful logout
window.location = 'mywebsite.com';
}
EDIT:
Or you could just redirect from Express like:
exports.logout = (req, res) => {
res.cookie('jwt', 'loggedout', {
expires: new Date(Date.now() + 5 * 1000),
httpOnly: true
});
// res.status(200).json({ status: 'success' });
res.redirect(200, "/");
};

Meteor client login using LDAP and JWT

I have a big CMS built with Meteor that until now used basic-auth to login as we didn't have more than one editor. However, more people will start working with it, so I am trying to create a login functionality through LDAP so any authorised employee can login with their username/password
I tried to do my best given poor documentation of WebApp.connectHandlers and the lack of content integrating passport with Meteor. I based my LDAP login code on express examples (assuming WebApp.connectHandlers.use() was the Meteor equivalent of Express' app.use())
The login part works, that is to query and verify the user through LDAP.
However I cannot figure how to identify the logged-in user when they make a Meteor.call( ).
What I have in mind at the moment is to send a JWT token back to the authenticated user, store it in a cookie or something, and whenever a Meteor.call( ) is made, the client passes the token as a parameter. Then I would be able to identify the caller and can for example store the username in the database as the person who made a certain change.
Good to mention that I am trying to avoid using Meteor's accounts system as the requirement is to use LDAP without creating any extra tables (that's why the complication of using those several packages together).
Here's my code, with working login but no idea how to pass back the token to the user.
Maybe my whole JWT logic is wrong, I would appreciate any help/suggestions.
var basicAuth = require("basic-auth");
var passport = require("passport");
var bodyParser = require("body-parser");
var LdapStrategy = require("passport-ldapauth");
var jwt = require("jsonwebtoken");
// Example of the real LDAP config
var OPTS = {
server: {
url: "ldap://address:port",
bindDN: "admin",
bindCredentials: "adminPassword",
searchBase: "OU=Users,DC=example,DC=com",
searchFilter: "(&(objectClass=user)(sAMAccountName={{username}}))"
},
credentialsLookup: basicAuth
};
Meteor.startup(() => {
passport.use(new LdapStrategy(OPTS));
WebApp.connectHandlers.use(bodyParser.json());
WebApp.connectHandlers.use(bodyParser.urlencoded({ extended: false }));
WebApp.connectHandlers.use(passport.initialize());
WebApp.connectHandlers.use(
"/",
(req, res, next) => {
// This part before the else is to trigger a basic auth popup to enter username/password
let credentials = basicAuth(req);
if (!credentials) {
res.statusCode = 401;
res.setHeader("WWW-Authenticate", "Basic");
res.end("Access denied");
} else {
passport.authenticate(
"ldapauth",
{
session: false
},
function(err, user, info) {
if (err) {
return next(err);
}
if (user) {
var token = jwt.sign(
{ username: user.sAMAccountName },
"someSecretString"
);
console.log("token", token);
next();
}
}
)(req, res, next);
}
},
function(req, res) {
console.log("debug point#2");
res.send({ status: "ok" });
}
);
}

nodejs - Share session between express and socket.io (authentication with passport)

I am currently creating a webapplication using node.js.
When a user enters the side, a connection via socket.io is built up.
I want to block multiple connections. A user should just be able to connect one time, if he opens the page in another window, i want to display a message like "already connected, please close all other tabs", or something like that.
To do so, i create sessions for the users using "express-session". this sessions are shared with socket.io by using following code.
I am not sure, if i am doing it correctly. I have looked at some tutorials and they are all sharing the sessions in a similar way. But sometimes, i think, the session data is overridden, when i change and save a value in the socket.io-session.
var sessionStoreClient = Redis.createClient({
port: DBs.sessionStore_redis.port,
host: DBs.sessionStore_redis.host,
password: DBs.sessionStore_redis.password,
db: DBs.sessionStore_redis.database
});
var sessionStore = new RedisStore({
unset: "destroy",
client: sessionStoreClient
});
var sessionInstance = expressSession(
{
key: sessionsKey,
store: sessionStore,
secret: sessionsSecret,
unset: "destroy"
}
);
app.use(sessionInstance);
app.use(passport.initialize());
app.use(passport.session());
to share the sessions with socket.io, i am using following code
var passportSocketIo = function(app,sessionInstance, passport){
return function(socket, next){
var req = socket.request;
sessionInstance(req,req.res,
function () {
if (!req.session) {
return next(new Error("Not authorized"), false);
}
req.updateSessionValue = function (propertyName, propertyValue, callback) {
if(!req.session){
console.log("unauthorized session modification");
return;
}
req.session.reload(()=>{
req.session[propertyName] = propertyValue;
req.session.save();
if(callback)
callback(propertyName, propertyValue,req.session);
});
};
if(req.session.passport && req.session.passport.user) {
passport.deserializeUser(req.session.passport.user, req, function (err, user) {
req.user = user;
req.user.logged_in = true;
return next(null, true);
});
}else {
return next(null, true);
}
}
);
};
};
var nsp = io.of(Packages.NAMESPACES.LOBBY);
nsp .use(options.sessionMiddleware);
to edit the values of the sessions, inside of the socket.io-listeners, i use the function, which is set to the request in the code above.
_onConnectionReceived(socket) {
socket.request.updateSessionValue("isConnected",true);
}
and when the user disconnects from the socket:
_onDisconnect(socket) {
socket.request.updateSessionValue("isConnected",false);
}
For login and authentication I am currently using passport.js.
When the user logs out, he is redirected to the samepage, but as "guest"-user. For loggin out, I am using following code:
router.get('/logout',function(req, res,next){
req.logout();
req.session.destroy(
function (err) {
res.render('logout',{});
}
);
});
The problem there is, that after the session is destroyed and the user is redirected, the socket.io disconnect event is fired. Even after the reload inside of the "onDisconnected" event-listener, the session seems to exist, after the value is modified, the session seems to get recreated somehow. Also it feels like, that the session values are sometimes just overwritten - i have not exactly found out why.
Can anyone explain what i am doing wrong, or if there is a better way to share and modify the sessions? Is the "reload" function of the session really reloading its data from the sessionStore?
Another approach could be to just save the session/user ids in a hashset, and check if there is already a connection open (without saving it in the session it self), but i am curious, if my approach is correct, becaus i need the sharing and modifing of the sessions anyways.

Resources