How to end a session in ExpressJS - session

I feel like this has to be buried somewhere in the documentation, but I can't find it.
How do you close or end or kill (whatever) a session in ExpressJS?

Express 4.x Updated Answer
Session handling is no longer built into Express. This answer refers to the standard session module: https://github.com/expressjs/session
To clear the session data, simply use:
req.session.destroy();
The documentation is a bit useless on this. It says:
Destroys the session, removing req.session, will be re-generated next request.
req.session.destroy(function(err) {
// cannot access session here
})
This does not mean that the current session will be re-loaded on the next request. It means that a clean empty session will be created in your session store on next request. (Presumably the session ID isn't changing, but I have not tested that.)

Never mind, it's req.session.destroy();

The question didn't clarify what type of session store was being used. Both answers seem to be correct.
For cookie based sessions:
From http://expressjs.com/api.html#cookieSession
req.session = null // Deletes the cookie.
For Redis, etc based sessions:
req.session.destroy // Deletes the session in the database.

Session.destroy(callback)
Destroys the session and will unset the req.session property. Once complete, the callback will be invoked.
↓ Secure way ↓ ✅
req.session.destroy((err) => {
res.redirect('/') // will always fire after session is destroyed
})
↓ Unsecure way ↓ ❌
req.logout();
res.redirect('/') // can be called before logout is done

use,
delete req.session.yoursessionname;

From http://expressjs.com/api.html#cookieSession
To clear a cookie simply assign the session to null before responding:
req.session = null

To end a server-side session
https://github.com/expressjs/session#sessiondestroycallback
req.session.destroy(function(err) {
// cannot access session here
})
Note, this is essentially a wrapper around delete req.session as seen in the source code:
https://github.com/expressjs/session/blob/master/session/session.js
defineMethod(Session.prototype, 'destroy', function destroy(fn) {
delete this.req.session;
this.req.sessionStore.destroy(this.id, fn);
return this;
});
To end a cookie-session
https://github.com/expressjs/cookie-session#destroying-a-session
req.session = null;

req.session.destroy();
The above did not work for me so I did this.
req.session.cookie.expires = new Date().getTime();
By setting the expiration of the cookie to the current time, the session expired on its own.

You can retrieve the id of a session using req.session.id or req.sessionID and then pass it to req.sessionStore.destroy method like so:
const sessionID = req.session.id;
req.sessionStore.destroy(sessionID, (err) => {
// callback function. If an error occurs, it will be accessible here.
if(err){
return console.error(err)
}
console.log("The session has been destroyed!")
})
Reference to the req.sessionStore.destroy method.

As mentioned in several places, I'm also not able to get the req.session.destroy() function to work correctly.
This is my work around .. seems to do the trick, and still allows req.flash to be used
req.session = {};
If you delete or set req.session = null; , seems then you can't use req.flash

Related

Cookie-less Laravel sessions

We have a small quiz type functionality built in Laravel to be embedded in a site via an iframe served from a separate domain (to work around CMS limitations).
It uses sessions to keep track of the user's progress in the quiz. This doesn't work in Safari (Mac/iOS), I believe because Apple disable cookies issued from within an iframe.
Assuming that limitation is one we're stuck with, has anyone had any success making Laravel sessions cookie-less? I found this code on Github, which looks promising but is old enough (and incompatible with current Laravel) that I can't tell if it's going to be a solution.
In case it helps someone else, or anyone can see any silly errors in my code, this is what I did (an adaption of the Github code, to work in Laravel 9).
I extended StartSession and SessionServiceProvider (to use my new StartSession). I created an override for handleStatefulRequest in Start Session, and where it adds a cookie to the reponse (it calls addCookieToResponse) did this:
if ($request->cookies->get($session->getName())) {
$this->addCookieToResponse($response, $session);
}
else {
// Add session ID to header
$this->addIdentifierToResponse($response, $session);
}
That new function looks like this:
protected function addIdentifierToResponse(Response $response, Session $session)
{
if ($this->sessionIsPersistent($config = $this->manager->getSessionConfig())) {
$response->headers->set("X-Session-Token", $session->getId());
}
}
I also changed the getSession method to get the session ID from that newly set header (when no cookie found):
public function getSession(Request $request)
{
return tap($this->manager->driver(), function ($session) use ($request) {
if ($request->cookies->get($session->getName())) {
Log::debug('1. Set session ID from cookie');
$session->setId($request->cookies->get($session->getName()));
}
else if ($request->headers->get("X-Session-Token", $request->input("sess_id"))) {
$sessionToken = $request->headers->get("X-Session-Token", $request->input("sess_id"));
$session->setId($sessionToken);
}
});
}
I created a Github repo containing the whole thing.

What is the proper way to update attribute value in started Spring WebSession?

I have SpringBoot 2.0.0.M7 project where I am using WebSession with Redis (org.springframework.session:spring-session-data-redis:2.0.0.RELEASE).
I have a WebFlux route which supposed to do redirect to eBay with the eBay session id. Every time user is visiting that route I need to request different session id from eBay API and include it into the redirect URL. Later, eBay will redirect user back to my application where I need that session id to request token.
During testing I saw that that value of the session attribute (in my case it's ebay_session_id) can't be replaced with the new value when browser still have a cookie with the existing session ID. In the route where I am requesting again ebay_session_id I am getting old value and not the new one.
The code which store SessionID is following:
return ebayApiReactiveWrapper
.getSessionId(apiContext)
.flatMap(sessionId ->
request
.session()
.map(webSession -> {
webSession
.getAttributes()
.put("ebay_session_id", sessionId);
return sessionId;
})
)
.flatMap(sessionId -> {
final UriBuilder uriBuilder = uriBuilderFactory.uriString(
ebayApiSettings.getSignInUrl()
);
uriBuilder.queryParam("runame", ebaySettings.getRuName());
uriBuilder.queryParam("SessID", sessionId);
return ServerResponse.temporaryRedirect(redirectUri).build();
});
I tried to add webSession.save() after put method but it doesn't help.
What I am doing wrong? Thank you in advance!
UPDATE
Some new details about what is happening with the session data in Redis. When session is created (empty Redis) the data looks like that:
127.0.0.1:6379> hkeys "spring:session:sessions:cbbf8000-6ce8-4238-a427-9aab37d2702b"
1) "lastAccessedTime"
2) "maxInactiveInterval"
3) "creationTime"
4) "sessionAttr:ebay_session_id"
When I visit same route second time (session cookie still exists and the session data is still in Redis) the data is changing:
127.0.0.1:6379> hkeys "spring:session:sessions:cbbf8000-6ce8-4238-a427-9aab37d2702b"
1) "sessionAttr:ebay_session_id"
Through, sessionAttr:ebay_session_id still contains value from the first request.
The worst thing is that such structure cause NullPointerException when another route is trying to get session data. Looks like it expecting other 3 fields to be presented and fails when it's not the case.
Looks like not many people faced such issue. I found my solution to solve it.
I saw that session will not be updated if the attribute set of the session is not changed. So I am adding a new attribute every time I need to update session value. My first flatMap from the question code is changed the following way:
.flatMap(sessionId ->
request
.session()
.map(webSession -> {
// we need to check if session already started before applying "hack"
if (webSession.isStarted()) {
// "hack"
final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yMdkmsn");
webSession
.getAttributes()
.put(LocalDateTime.now().format(dateTimeFormatter),"1");
}
webSession
.getAttributes()
.put("ebay_session_id", sessionId);
return sessionId;
})
)
When new attribute is added session considered as changed and will be updated in redis.
I faced that problem and used almost the same hack, but just by updating a primitive typed session attribute.
For example:
webSession.getAttributes().put("userContext", userContext);
webSession.getAttributes().put("lastContextUpdateTime", System.currentTimeMillis());
This avoids to add a new attribute for each session update, but triggers the mutable object update in Redis too.

result contains no cookies when using result.withCookies

please note: Play Framework newbie
Intro
I need to use session() and/or response().cookies to pass data/store data.
At first session() started with this issue, now cookies are following along.
Problem:
Given a basic example code below, I have a boolean value setting a result, followed by cookies being created and added to the result.
Here on, the myPage is rendered, which contains a POST action to an action within the same controller, UserController
In this new method, there are no cookies. This is confirmed by
Collection<Http.Cookie> cookies = response().cookies();
with a size of 0
UserController
//...
Result result;
if(b)
result = ok(myPage.render());
else
result = new MyController().index();
result = result.withCookies(
new Http.Cookie("id", "value", 86400, "", "", true, false, Http.Cookie.SameSite.LAX),
new Http.Cookie("id_2", "value_2", 3600, "", "", true, false, Http.Cookie.SameSite.LAX)
);
return result;
//...
Question:
The documentation states that a session() lasts the entire duration while the browser window is open.
Cookies are stored client side and loaded when the webpage loads, session is regarded a bunch of cookies.
Why would setting session() entries or adding cookies() as shown above, be cleared, i.e. no cookies available in the response()?
What can I look for, what would remove session() or cookie entries?
Well there was no secret answer...
This is not shown in the question, but cookies name or id may contain no spaces. This was causing my problems. After removing the spaces in the name/id (values could have spaces),
then the issue was resolved

Reload page with new context in express

I have a page that lists events, in which admins are can delete individual items with an AJAX call. I want to reload the page when an event is deleted, but I am having trouble implementing it with my current understanding of express' usual req, res, and next.
Here is my current implementation (simplified):
Simple jQuery code:
$(".delete").click(function(e){
$.post("/events/delete",{del:$(this).val()})
})
in my routes file:
function eventCtrl(req,res){
Event.find({}).exec(function(err,events){
...
var context = {
events:events,
...
}
res.render('events',context);
});
}
function deleteCtrl(req,res,next){
Event.findById(req.param("del")).exec(function(err,event){
// delete my event from google calendar
...
event.remove(function(err){
...
return next();
});
});
}
app.get('/events',eventCtrl);
app.post('/events/delete',deleteCtrl,eventCtrl);
When I make a post request with AJAX all the req handlers are called, the events are deleted successfully, but nothing reloads. Am I misunderstanding what res.render() does?
I have also tried using a success handler in my jQuery code when I make the post request, with a res.redirect() from deleteCtrl, but my context is undefined in that case.
on the client side, you are using
$(".delete").click(function(e){
$.post("/events/delete",{del:$(this).val()})
})
this code does not instruct the browser to do anything when the response from the post is received. So nothing visible happens in the browser.
You problem is not located server side ; the server answers with the context object. You are simply not doing anything with this answer.
Try simply adding a successHandler.
Generally speaking this would not be a best practice. What you want to do is reconcile the data. If the delete is successful, then just splice the object out of the array it exists in client-side. One alternative would be to actually send back a refreshed data set:
res.json( /* get the refreshed data set */ );
Then client-side, in the callback, you'd actually just set the data source(s) back up based on the result:
... myCallback(res) {
// refresh the data source(s) from the result
}

How can I override the session ID for a node.js express session (using passport)

I'm implementing a passport strategy that calls our internal auth service. The internal auth service generates the session ID, so I need to make sure the connect session uses that ID instead of the default generated ID.
Is there a way to do this? Is it possible to provide my own hook function to connect to produce the session id? I can't imagine it's as simple as setting session.id or anything like that, because I don't have control over when or how connect actually creates the session.
Has anyone solved this problem?
This cannot be done with the current implementation of Connect's session middleware, but you can fork the session middleware and change how the session id is generated, namely this line:
https://github.com/senchalabs/connect/blob/master/lib/middleware/session.js#L202
By "fork" I mean copying the file above, changing the assignment of the sessionID and using your new file instead when configuring the session middleware.
UPDATE:
Here's how I would regenerate the session with a custom id (note - this is just an idea, I haven't tested it):
// this is the function you'll be calling in your routes or whatever
req.regenerateSession = function(newSid) {
// keep old session data
var oldSessionData = req.session;
// destroy current session and make a new one with your custom id
store.destroy(req.sessionID, function() {
store.generate(req, newSid);
// copy back the session data
// since you don't want to lose it probably
req.session = oldSessionData;
});
}
// replace the session store generate function to accept a custom sessionID
// https://github.com/senchalabs/connect/blob/master/lib/middleware/session.js#L201
store.generate = function(req, customID) {
req.sessionID = customID || utils.uid(24);
req.session = new Session(req);
req.session.cookie = new Cookie(req, cookie);
}

Resources