Skip to main content

Epress authen, routing, middleware ...

https://stackoverflow.com/questions/11784233/using-passportjs-how-does-one-pass-additional-form-fields-to-the-local-authenti
https://blog.risingstack.com/node-hero-node-js-authentication-passport-js/
https://www.owasp.org/index.php/Session_Management_Cheat_Sheet
https://stackoverflow.com/questions/10306185/nodejs-best-way-to-pass-common-variables-into-separate-modules
https://www.npmjs.com/package/express-passport-session-tracker
https://stackoverflow.com/questions/16800418/how-to-properly-pass-mysql-connection-to-routes-with-express-js
https://scotch.io/tutorials/easy-node-authentication-setup-and-local
https://scotch.io/bar-talk/the-ins-and-outs-of-token-based-authentication
https://scotch.io/tutorials/the-anatomy-of-a-json-web-token
https://jonathanmh.com/express-passport-json-web-token-jwt-authentication-beginners/


https://github.com/jaredhanson/passport/blob/master/lib/strategies/session.js
https://stackoverflow.com/questions/2015232/database-design-for-audit-logging
https://stackoverflow.com/questions/26040329/how-do-you-handle-api-version-in-a-node-express-app
https://github.com/expressjs/session
https://news.ycombinator.com/item?id=11929267

https://stackoverflow.com/questions/14464873/expressjs-session-expiring-despite-activity
http://openmymind.net/2012/2/3/Node-Require-and-Exports/
https://github.com/expressjs/cookie-session
https://stackoverflow.com/questions/28789857/how-is-the-express-req-session-object-persisted
https://stackoverflow.com/questions/21939568/javascript-modules-prototype-vs-export

https://stackoverflow.com/questions/20534702/node-js-use-of-module-exports-as-a-constructor




There's an overall session data structure that stores all session info (like a global, but it could also be in a database - just something that is persistent at least across connections). Each client's session data uses one unique key to index into the session store to get the session data for that client.
Part of establishing a session for a given browser client is creating a unique client key (which will usually be stored in a cookie) that becomes the index into the global session object.
On an incoming http request, Express middleware that supports the session checks a particular client cookie and if that particular cookie is found on the http request and is found in the global session object/database, then it adds that session's stored info to the request object for the http request handler to later use.
So, here's a typical sequence:

  1. Incoming HTTP request.
  2. Middleware checks for session cookie.
  3. If session cookie not there, then create one and, in the process created a unique id to identify this http client.
  4. In the persistent session store, initialize the session for this new client.
  5. If session cookie is there, then look in the session store for the session data for this client and add that data to the request object.
  6. End of session middleware processing
  7. Later on in the Express processing of this http request, it gets to a matching request handler. The session data from the session store for this particular http client is already attached to the request object and available for the request handler to use.

+---------------------+----------------------+
| last_web_activity   | last_activity        |
+---------------------+----------------------+
| 2017-09-13 11:13:25 | 2017-09-13 04:17:57  |
| 2017-09-13 08:00:54 | 2017-09-13 08:14:50  |
| 2017-09-13 08:28:18 | 2017/9/13 @ 8:19:43  |
| 2017-09-13 08:22:38 | 2017-09-13 08:20:52  |
| 2017-09-13 07:27:11 | 2017-09-13 07:31:44  |
| NULL                | 2017/9/13 @ 7:8:41   |

| NULL                | NULL                 |


getTime is not a function().


That's because your dat1 and dat2 variables are just strings.
You should parse them to get a Date object, for that format I always use the following function:
// parse a date in yyyy-mm-dd format
function parseDate(input) {
  var parts = input.match(/(\d+)/g);
  // new Date(year, month [, date [, hours[, minutes[, seconds[, ms]]]]])
  return new Date(parts[0], parts[1]-1, parts[2]); // months are 0-based
}
I use this function because the Date.parse(string) (or new Date(string)) method is implementation dependent, and the yyyy-MM-dd format will work on modern browser but not on IE, so I prefer doing it manually.

Z stands for the timezone UTC and is defined in ISO-8601, which is your desired output format, extended by the millisecond part.

When a user record fetch from DB it convert datetime value to object type or JS format. (I saw this but not sure).
When I log user by console.log(user):
updated_at: 2017-09-11T05:44:47.000Z, last_activity: 2017-09-13T08:27:38.000Z, last_web_activity: 2017-09-11T11:34:35.000Z, last_admin_activity: null,
....

We can see that Datetime has been converted to JS format not MySQL (I use MySQL).
So when we call user.last_activity.getTime() it return unix time * miliseconds. Event datetime in MySQL that format is strange, ie: 2017/9/13 @ 7:8:41 it still work.
So the timestamp above will log in console of user object as 1505286521000. We can decode this date by type date command in bash/terminal: (remove milisecond '000')
date -d @1505286521 Wed Sep 13 07:08:41 UTC 2017

https://stackoverflow.com/questions/23571110/nodejs-responded-mysql-timezone-is-different-when-i-fetch-directly-from-mysql

last_activity varchar(255) :( :(:(:(:(:(:((:

This is  it.

Æ ASP thank a ton!

Expire and Max-Age Attributes
Session management mechanisms based on cookies can make use of two types of cookies, non-persistent (or session) cookies, and persistent cookies.
When close tab/browser, app (ionic) if use non-persistence cookie then it lost. So it may be reason why session lost radomly (mostly lost).


Update 1 18-Sept-17:

https://stackoverflow.com/questions/15016551/node-js-express-passport-cookie-expiration

As this post, do not use expires, use maxAge instead.

The cause of that was here:
cookie: { expires : new Date(Date.now() + 3600000) }

The new Date was being created only once, upon server start. That was causing the expiration date to be the same every time.

But in my app I see that the expire date is changing and update not just run one time at start.
So this do not like my problem with session timeout.
My problem is that only Ionic app suffer this issue and not all the time (:( ) , some account may work well but some are not.
As described in OSWAP  Expire and Max-Age Attributes:
Session management mechanisms based on cookies can make use of two types of cookies, non-persistent (or session) cookies, and persistent cookies. If a cookie presents the “Max-Age” (that has preference over “Expires”) or “Expires” attributes, it will be considered a persistent cookie and will be stored on disk by the web browser based until the expiration time.
...
So we need digging more to the way whole thing work together from User device to Web server, browser, config etc.


Script authen with passport:
//-------------
//Set up authentication with Passport
//-------------
var userModel = require('./models/user')(db);
passport.use(new LocalStrategy(
    function(username, password, done) {
        var errorMessage = 'Incorrect username/password combination.';
        userModel.GetUserByUsername(username, function(err, user) {
            if (err) { return done(err); }
            if (!user) {
              return done(null, false, { message: errorMessage });
            }

            user.validatePassword(password, function(isPasswordCorrect) {
                if (!isPasswordCorrect)
                {
                    return done(null, false, { message: errorMessage });
                }

                //Update with login date
                userModel.UpdateUserWithLogin(username, user.currentLoginTime, function(err){
                    //if we have an error here, we should probably just log it
                    if(err)
                    {
                        console.log(err);
                    }
                });

                return done(null, user);
            });
        });
    }
));

passport.serializeUser(function(user, done) {
  done(null, user);
});

passport.deserializeUser(function(user, done) {
    userModel.GetUserByUsername(user._id, function(err, user) {
            done(err, user);
        });
});

//-------------
//Set up express and configure
//-------------
var sessionStore = new SkinStore(db);
var app = express();

app.configure(function(){
    app.set('port', process.env.PORT || 3000);
    app.set('views', __dirname + '/views');
    app.engine('html', consolidate.swig);
    app.set('view engine', 'html');
    swig.init({
        root: '.',
        allowErrors: true, // allows errors to be thrown and caught by express instead of suppressed
        autoescape: false});

    app.use(express.logger('dev'));

    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(express.cookieParser("[mysecrethere]"));
    app.use(express.session({   store: sessionStore,
                            cookie: { expires : new Date(Date.now() + 3600000) } //1 Hour
                            }));
    app.use(passport.initialize());
    app.use(passport.session());
    app.use(flash());
    app.use(expressValidator);

    app.use(express.static(path.join(__dirname, 'public')));

    //Dynamic helpers
    app.use(require('./helpers/DynamicHelpers'));

    app.use(app.router);
});

app.get('/login', routes.login);
app.post('/login', passport.authenticate('local', {failureRedirect: '/login',
                                               badRequestMessage: "Please enter username and password",
                                               failureFlash: true }),
                                               function(req, res) {
                                                    var targetUrl = req.session.pageAfterLogin;
                                                    delete req.session.pageAfterLogin;
                                                    res.redirect(targetUrl || '/account');
                                                });

app.get('/account', IsAuthenticated, routes.account.show);

https://stackoverflow.com/questions/15016551/node-js-express-passport-cookie-expiration

And the IsAuthenticated helper function:

function IsAuthenticated(req,res,next){
    if(req.isAuthenticated())
    {
        next();
    }
    else
    {
        //save the requested page and then redirected
        req.session.pageAfterLogin = req.url;
        req.flash("error", "You must be logged in first!");
        res.redirect('/login');
    }
}

What I can find by debugging is that, after successful authentication (and after a cookie has expired), I hit this logic (from above):

function(req, res) {
    var targetUrl = req.session.pageAfterLogin;
    delete req.session.pageAfterLogin;
    res.redirect(targetUrl || '/account');
}

Where I can see that the "req" has the session properly set, with Passport information stored properly. Then, the redirect happens, the new request has no session information stored, and has an entirely new Session ID. I suspected that no cookie was being set on the client, and that does appear to be the case, which should explain the lack of consistent sessions.

However, I cannot figure out why no new cookie is being set. Is there something wrong with how the app is configured that would indicate why this is happening?

I should add that restarting the Node.js instance fixes the issue, it's just not something that would be tolerable in production.

Thanks.

UPDATE: I ran Fiddler to see what was happening with HTTP/S traffic, and I can see that when it works initially, I'm getting a cookie set in the browser (I tried several) which is then passed back to the server on subsequent requests.

When it doesn't work, the browser is not passing cookies to the server, and so Node is sending a Set-Cookie header that provides a new cookie each time. So far I've had no luck determining the cause of this.


Have you checked that passport.deserializeUser is run, that user._id exists and that your model finds the user? Also serializeUser should callback an id, not the user object. – Andreas Hultgren Feb 22 '13 at 10:03
 
I was able to test this out (finally), and I can see that serializeUser is getting called. However, in subsequent redirects, deserializeUser is not getting run. In serializeUser, the user was found properly, it just doesn't seem to persist. And I will work to serialize the user._id instead of the user. I had done that originally but switched for some reason I've since forgotten.

I figured it out, although I don't love the answer.
tl;dr; - use maxAge instead of expires.

The issue was rooted in the expiration date set on each cookie (which is automatically set by Express). I noticed that every cookie that was set had the same expiration date, which eventually ended up being in the past and hence instantly expiring.
The cause of that was here:
cookie: { expires : new Date(Date.now() + 3600000) }
The new Date was being created only once, upon server start. That was causing the expiration date to be the same every time. Based on code in the original post, I can't figure out why it doesn't work and yet every example I've found online uses the exact same code. I verified this by defining a function that created this Date, and checking that it only got called upon server start.
To fix this issue, I am defining maxAge instead of "expires". maxAge takes a number of milliseconds, rather than a date, and it appears to be setting the expiration date on all cookies correctly.
I would love to hear if anyone can explain why this is happening in the first place, since others seem to use it successfully. Any thoughts?
See my working code below
app.configure(function(){
    app.set('port', process.env.PORT || 3000);
    app.set('views', __dirname + '/views');
    app.engine('html', consolidate.swig);
    app.set('view engine', 'html');
    swig.init({
        root: '.',
        allowErrors: true, // allows errors to be thrown and caught by express instead of suppressed
        autoescape: false});

    app.use(express.logger('dev'));

    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(express.cookieParser("[mysecrethere]"));
    app.use(express.session({   store: sessionStore,
                            cookie: { maxAge : 3600000 } //1 Hour
                            }));
    app.use(passport.initialize());
    app.use(passport.session());
    app.use(flash());
    app.use(expressValidator);

    app.use(express.static(path.join(__dirname, 'public')));

    //Dynamic helpers
    app.use(require('./helpers/DynamicHelpers'));

    app.use(app.router);
});
I believe I am running into this issue as well, affecting users in a timezone on the other side of the world.
Also check the documentation for "rolling" flag. rolling - Force a cookie to be set on every response. This resets the expiration date. The default value is false. github.com/expressjs/session#rolling

This is the cause of problem: In FrontEnd site not backend. So hard to try fix on Server side :(.

app.config(function ($httpProvider) {
    $httpProvider.defaults.withCredentials = true;
    //rest of route code

});

Otherwise we need some update on server too:

 router.use(function(req, res, next) {
        console.log('__________route v11 +++++++++++++++> 01 ');
        res.header("Access-Control-Allow-Credentials", true);
        res.setHeader("Access-Control-Allow-Origin", "http://localhost:8101");
        res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
        res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
        next();

    });

The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'

https://github.com/whatwg/fetch/issues/251
https://stackoverflow.com/questions/19743396/cors-cannot-use-wildcard-in-access-control-allow-origin-when-credentials-flag-i

For multiple origin allow, the logic is similar as many PHP script we've used.
https://stackoverflow.com/questions/24897801/enable-access-control-allow-origin-for-multiple-domains-in-nodejs





Comments

Popular posts from this blog

Rand mm 10

https://stackoverflow.com/questions/2447791/define-vs-const Oh const vs define, many time I got unexpected interview question. As this one, I do not know much or try to study this. My work flow, and I believe of many programmer is that search topic only when we have task or job to tackle. We ignore many 'basic', 'fundamental' documents, RTFM is boring. So I think it is a trade off between the two way of study language. And I think there are a bridge or balanced way to extract both advantage of two method. There are some huge issue with programmer like me that prevent we master some technique that take only little time if doing properly. For example, some Red Hat certificate program, lesson, course that I have learned during Collage gave our exceptional useful when it cover almost all topic while working with Linux. I remember it called something like RHEL (RedHat Enterprise Linux) Certificate... I think there are many tons of documents, guide n books about Linux bu

Martin Fowler - Software Architecture - Making Architecture matter

  https://martinfowler.com/architecture/ One can appreciate the point of this presentation when one's sense of code smell is trained, functional and utilized. Those controlling the budget as well as developer leads should understand the design stamina hypothesis, so that the appropriate focus and priority is given to internal quality - otherwise pay a high price soon. Andrew Farrell 8 months ago I love that he was able to give an important lesson on the “How?” of software architecture at the very end: delegate decisions to those with the time to focus on them. Very nice and straight-forward talk about the value of software architecture For me, architecture is the distribution of complexity in a system. And also, how subsystems communicate with each other. A battle between craftmanship and the economics and economics always win... https://hackernoon.com/applying-clean-architecture-on-web-application-with-modular-pattern-7b11f1b89011 1. Independent of Frameworks 2. Testable 3. Indepe