Async Express-Session secret variable from AWS parameter store - express

I am trying to get the secret for express-session from AWS parameter store. This doesn't seem to create a session, there are no errors but I can't log in.
const param = require('./param');
const ssecret = param.getSecret('ss');
app.use(async (req, res, next) => {
const sessionSecret = await ssecret;
session({
proxy: true,
secret: sessionSecret,
resave: false,
saveUninitialized: true,
cookie: { secure: true }
})(req, res, next);
console.log("TEST")
});
param.js
const AWS = require("aws-sdk");
const ssm = new AWS.SSM({ region: "us-east-1" });
const getSecret = async (ss) => {
console.log(`Getting secret for ${ss}`);
const params = {
Name: ss,
WithDecryption: true
};
const result = await ssm.getParameter(params).promise();
return result.Parameter.Value;
};
module.exports = { getSecret };

You're not supposed to call session({...}) over and over. You're supposed to call it once, then save that result and use that as middleware. When you call it over and over again, you keep making new instances of the object that manages your sessions and thus they don't connect to previous sessions.
I'd suggest changing to something like this:
const param = require('./param');
param.getSecret('ss').then(sessionSecret => {
app.use(session({
proxy: true,
secret: sessionSecret,
resave: false,
saveUninitialized: true,
cookie: { secure: true }
}));
// other initialization of app here
});
This would be cleaner to code in an ESM module with top level await.
Or here's another way to implement that's a little more like you originally had:
let sessionMiddleware;
app.use(async (req, res, next) => {
const sessionSecret = await ssecret;
if (!sessionMiddleware) {
sessionMiddleware = session({
proxy: true,
secret: sessionSecret,
resave: false,
saveUninitialized: true,
cookie: { secure: true }
});
}
sessionMiddleware(req, res, next);
console.log("TEST")
});

Related

How to proper use express flash message on req.redirect

I am following a tutorial of how to creat a blogapp using expressjs, mongoDB and handlebars.
The instructor uses req.flash() in a req.redirect command and it works for him, but when I try it, my page does not display the message.
The problem is that when I use req.flash(type, message) with req.render it works perfectly, but my page does not reload as I need.
If I check the flash message calling it on a console log it shows me message.
// Session
app.use(session({
secret: 'secret',
resave: true,
saveUninitialized: true,
cookie: {secure: true}
}));
app.use(flash());
// Middleware
app.use((req, res, next) =\> {
res.locals.success_msg = req.flash(('success_msg')\[0\]);
res.locals.error_msg = req.flash(("error_msg")\[0\]);
next();
});
adminRouter.post('/categories/delete', async (req, res) =\> {
try {
const { id } = req.body;
await Category.deleteOne({\_id: id });
req.flash('success_msg', 'Category deleted successfully');
res.redirect("/admin/categories");
} catch (err) {
req.flash('error_msg', `Error to delete category: ${err}`);
res.redirect('admin/categories');
}
})
my render:
{{#if success_msg}}
\<div class="alert alert-success"\>{{success_msg}} \</div\>
{{else if error_msg}}
\<div class="alert alert-danger"\> {{error_msg}} \</div\>
{{/if}}
I found the error after reading this article
My problem was the session setup.
This was my solution:
// Session
var sessionStore = new session.MemoryStore;
app.use(cookieParser('secret'));
app.use(session({
cookie: { maxAge: 60000 },
store: sessionStore,
saveUninitialized: true,
resave: 'true',
secret: 'secret'
}));
app.use(flash());
// Middleware
app.use((req, res, next) => {
//res.locals.success_msg = req.flash(('success_msg')[0]);
res.locals.success_msg = req.flash('success_msg');
//res.locals.error_msg = req.flash(("error_msg")[0]);
res.locals.error_msg = req.flash("error_msg");
next();
});

How to pass ctx / context into KOA middleware that is imported (createShopifyAuth)?

I'm looking to pass the request context into a KOA middleware that is generated from a require (https://github.com/Shopify/koa-shopify-auth). I set some API keys that I need to pass into it from a previous middleware but have no access to them when I reach createShopifyAuth.
I've tried passing in the global server.context but it doesn't seem to be set from the previous middleware.
server.use(async (ctx, next) => {
await shopifyKeys;
if (url.parse(ctx.originalUrl, true).query.shop) {
const shops = url.parse(ctx.originalUrl, true).query.shop;
server.context.keys = [shopifyKeys[shops].key, shopifyKeys[shops].secret];
console.log(server.context.keys);
}
return next();
});
server.use(
createShopifyAuth({
apiKey: server.context.keys[0],
secret: server.context.keys[1],
scopes: [
'read_products',
'read_checkouts',
'read_orders',
'write_orders',
],
async afterAuth(ctx) {
const { shop, accessToken } = ctx.session;
ctx.cookies.set('shopOrigin', shop, {
httpOnly: false,
secure: true,
sameSite: 'none',
});
const registration = await registerWebhook({
address: `${HOST}/webhooks/orders/paid`,
topic: 'ORDERS_PAID',
accessToken,
shop,
apiVersion: ApiVersion.July20,
});
if (registration.success) {
console.log('Successfully registered webhook!');
} else {
console.log(
'Failed to register webhook',
registration.result.data.webhookSubscriptionCreate.userErrors,
);
}
ctx.redirect('/');
},
}),
);
Any help with figuring out how to get the context into the second server.use would be appreciated.
I am allegedly a newbie when it comes to KOA, but the only way I manage to make it was passing the data via cookies, individually. Here is an example:
server.use(
createShopifyAuth({
apiKey: SHOPIFY_API_KEY,
secret: SHOPIFY_API_SECRET_KEY,
scopes: [
"read_products",
"write_products",
"read_script_tags",
"write_script_tags",
"read_themes",
"write_themes",
],
accessMode: "offline",
afterAuth(ctx) {
const { shop, accessToken } = ctx.session;
ctx.cookies.set("shopOrigin", shop, {
httpOnly: false,
secure: true,
sameSite: "none",
});
ctx.cookies.set("accessToken", accessToken, {
httpOnly: false,
secure: true,
sameSite: "none",
});
ctx.redirect("/");
},
}),
);

Cookie not set and sent by Express Passport

index.js
const express = require('express')
const session = require('express-session');
const MongoStore = require('connect-mongo')(session);
const MongoStore = connectMongo(session);
const app = express()
const sessionStore = new MongoStore({
mongooseConnection: connection,
collection: 'sessions'
})
app.use(session({
secret: 'mysecret',
resave: false,
saveUninitialized: true,
store: sessionStore,
cookie: {
maxAge: (1000 * 60)*10
},
httpOnly: true,
secure: false
}));
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser((user, done) => {
console.log(user._id: " + user._id);
done(null, user._id)
})
const GoogleStrategy = passportGoogle.OAuth2Strategy
const strategy = app => {
const strategyOptions = {
clientID: GOOGLE_ID,
clientSecret: GOOGLE_SECRET,
callbackURL: `/auth/google/callback`
}
const verifyCallback = async (accessToken, refreshToken, profile, done) => {
let [err, user] = await to(getUserByProviderId(profile.id))
if (err || user) {
return done(err, user)
}
const [createdError, createdUser] = await to(
createUser({
provider: profile.provider,
...
})
)
return done(createdError, createdUser)
}
passport.use(new GoogleStrategy(strategyOptions, verifyCallback))
app.get('/auth/google/callback',
passport.authenticate('google'),
(req, res) => {
res.cookie('???what is default key set by PassportJS???', req.user._id);
res.redirect(303, `/loggedByGoogle.html`);
}
I succesfully
- logged in with Google,
- passport.serializeUser was invoked
- profile data was saved in MongoDB,
- redirection happened to `/loggedByGoogle.html`
but no cookies were sent to client
and there is no entry in "sessions" data store after login.
Do I have to explicitly set and send cookies myself?
Only after I added res.cookie('foo', 'bar');
ahead of
res.redirect(303, /loggedByGoogle.html);
I got some cookie on client side.
I though cookies were set internally by Passport after "serializeUser"
invocation.
Is there a default cookie name (key) set by Passport?
What am I missing?

how to use express-session in subscription

I'm using express-session as middleware for handling session and everything works just fine with queries and mutation. But I just can't figure out to do handle session for subscription.
const app = express();
const server = http.createServer(app);
const RedisStore = connectRedis(session);
const bootstrap = async () => {
await createConnection();
const schema = await createSchema();
const store = new RedisStore({
client: redis as any,
logErrors: true
});
const apolloServer = new ApolloServer({
schema,
context: ({req, res}: ExpressContext) => {
return ({req, res});
},
subscriptions: {
onConnect: (_, __, context: ConnectionContext) => {
console.log('context', context.request.headers);
}
}
});
const PORT = process.env.port || 4000;
app.use(cookieParser());
//Session middleware before resolvers
app.use(session({
store,
name: COOKIE_NAME,
secret: config.get('session_secret'),
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
maxAge: 1000 * 60 * 60 * 24 * 365 // 1 year
}
}));
apolloServer.applyMiddleware({app});
apolloServer.installSubscriptionHandlers(server);
//`listen` on the http server variable, and not on `app`.
server.listen(PORT, () => {
console.log(`Server ready at http://localhost:${PORT}${apolloServer.graphqlPath}`);
console.log(`Subscriptions ready at ws://localhost:${PORT}${apolloServer.subscriptionsPath}`);
})
};
bootstrap().catch(e => console.log(e));
This is how I get userId in resolvers
req.session.userId;
How to use similar manner in websokckets.

express-session not saving data

I have a simple MEAN app and I want to implement a simple "home-made" user authentication. My idea is to save the userId in the session when he logs in, and to check if userId exists in the session on each page request (for example, when getting the list of all users).
Backend - server.js:
const express = require("express");
const session = require("express-session");
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
var MemoryStore = session.MemoryStore;
app.use(
session({
name: "app.sid",
secret: "my_s3cr3t",
resave: true,
store: new MemoryStore(),
saveUninitialized: true
})
);
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cors());
const dbConfig = require("./config/database.config.js");
const mongoose = require("mongoose");
mongoose.Promise = global.Promise;
mongoose
.connect(dbConfig.url)
.then(() => {
// ...
})
.catch(err => {
// ...
process.exit();
});
require("./app/routes/user.routes.js")(app);
require("./app/routes/task.routes.js")(app);
require("./app/routes/login.routes.js")(app);
app.listen(3333, () => {
console.log("Server is listening on port 3333");
});
When a user clicks the Login button, a method from the frontend controller is called:
Frontend - login.controller.js:
vm.login = function() {
userService.getUserByUsername(vm.username).then(user => {
if (user.password === vm.password) {
console.log("Login ok");
loginService.login(user).then(($window.location.href = "/#!main"));
} else {
console.log("Login not ok");
}
});
};
Backend - login.controller.js:
exports.login = (req, res) => {
req.session.userId = req.body._id;
req.session.save(function(err) {
console.log(err); // prints out "undefined", so there's no error
});
console.log(req.session);
res.status(200).send({
message: "Login ok"
});
};
The frontend LoginController prints out "Login ok" (assuming that I entered correct credentials) and redirects me to the "main" page which uses main.controller.js:
In the meantime, the backend login controller prints out the following:
Session {
cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true },
userId: '5b4746cafe30b423181ad359' }
So there is definitely a userId in the session content. However, when I get redirected to the main.html and the main.controller.js gets invoked, it calls:
loginService.getSession().then(data => console.log(data));
(I just want to check if the userId is still in the session, and later I will perform some useful actions)
The getSession() method in the frontend LoginService only does the $http call:
function getSession() {
return $http.get("http://localhost:3333/session").then(
function(response) {
return response.data;
},
function(error) {
console.log(error.status);
}
);
}
This one calls the method which is defined in the backend LoginController:
exports.getSession = (req, res) => {
console.log(req.session);
if (req.session.userId) {
res
.status(200)
.send({ message: "Session existing with userId " + req.session.userId });
} else {
res.status(404).send({ message: "Session not existing" });
}
};
The frontend call prints the status code 404 in the console, while in the backend I get the following output:
Session {
cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true } }
(no userId is present...)
One more thing... In a few tutorials I saw that they are using cookie-parser. However, when I try to use it, I don't get any data from my database, only the static text is displayed. So I removed it temporarily from server.js.
EDIT:
I tried adding MongoStore to my app:
const MongoStore = require("connect-mongo")(session);
...
app.use(
session({
name: "app.sid",
secret: "G4m1F1c4T10n_#ppL1c4t10N",
resave: true,
saveUninitialized: false,
cookie: { maxAge: 600000 },
store: new MongoStore({ url: "mongodb://localhost:27017/myAppDb" })
})
);
...but nothing changed.
How can I get my sessions to work?
As I found out after talking to several people, sessions are more or less deprecated and the new way of handling these things are the tokens. So I switched to JWT and it's working great.