diff --git a/components/session.js b/components/session.js index 06ef2df..4f3469e 100644 --- a/components/session.js +++ b/components/session.js @@ -66,15 +66,18 @@ export default class Session { // Attempt to load session data from sessionStore on every call this._session = this._getSessionStore() + + //console.log("Time left till session expires in seconds: "+((this._session.expires - Date.now()) / 1000)) - if (window.session && this._session && Object.keys(this._session).length > 0 && forceUpdate !== true) { - // If we have a populated session object already AND forceUpdate is not - // set to true then return the session data we have already + // If session data exists, has not expired AND forceUpdate is not set then + // return the stored session we already have. + if (this._session && Object.keys(this._session).length > 0 && this._session.expires > Date.now() && forceUpdate !== true) { return new Promise((resolve) => { resolve(this._session) }) } else { - // If we don't have session data (or forceUpdate is true) then get it + // If we don't have session data, or it's expired, or forceUpdate is set + // to true then revalidate it by fetching it again from the server. return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest() xhr.open("GET", '/auth/session', true) @@ -84,12 +87,13 @@ export default class Session { // Update session with session info this._session = JSON.parse(xhr.responseText) + // Set a value we will use to check this client should silently + // revalidate based on the value of clientMaxAge set by the server + this._session.expires = Date.now() + this._session.clientMaxAge + // Save changes to session this._setSessionStore(this._session) - - // Save session to window.session object - window.session = this._session - + resolve(this._session) } else { reject(Error('XMLHttpRequest failed: Unable to get session')) @@ -149,6 +153,7 @@ export default class Session { // Set isLoggedIn to false and destory user object this._session.csrfToken = await Session.getCsrfToken() this._session.isLoggedIn = false + this._session.expires = Date.now() delete this._session.user // Save changes to session diff --git a/index.js b/index.js index 31eabd8..d3c3778 100644 --- a/index.js +++ b/index.js @@ -36,10 +36,10 @@ app.prepare() // Define user object const User = db.define("user", { - name : { type: "text" }, - email : { type: "text", unique: true }, - token : { type: "text" }, - verified : { type: "boolean", defaultValue: false } + name : { type: "text" }, + email : { type: "text", unique: true }, + token : { type: "text" }, + verified : { type: "boolean", defaultValue: false } }) // Create table diff --git a/package.json b/package.json index 7ebf1fb..bf4fa5f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nextjs-starter", - "version": "2.4.3", + "version": "2.4.4", "description": "A starter Next.js project", "main": "index.js", "dependencies": { diff --git a/routes/auth.js b/routes/auth.js index de3923d..85d818f 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -27,25 +27,34 @@ exports.configure = (app, server, options) => { const User = options.db.models.user // Base path for auth URLs - const path = (options.path) ? options.path : '/auth' + const path = options.path || '/auth' // Directory for auth pages - const pages = (options.pages) ? options.pages : 'auth' + const pages = options.pages || 'auth' // The secret is used to encrypt/decrypt sessions (you should pass your own!) - const secret = (options.secret) ? options.secret : 'AAAA-BBBB-CCCC-DDDD' + const secret = options.secret || 'AAAA-BBBB-CCCC-DDDD' // Configure session store (defaults to using file system) - const store = (options.store) ? options.store : new FileStore({ path: '/tmp/sessions', secret: secret }) + const store = options.store || new FileStore({ path: '/tmp/sessions', secret: secret }) - // Max cookie age (default is 4 weeks) - const maxAge = (options.maxAge) ? options.maxAge : 3600000 * 24 * 7 * 4 + // Max session age in ms (default is 4 weeks) + // NB: With 'rolling: true' passed to session() the session expiry time will + // be reset every time a user visits the site again before it expires. + const maxAge = options.maxAge || 60000 * 60 * 24 * 7 * 4 + + // How often the client should revalidate the session in ms (default 60s) + // Does not impact the session life on the server, but causes the client to + // always refetch session info after N seconds has elapsed since last checked. + // Sensible values are between 0 (always check the server) and a few minutes. + const clientMaxAge = options.clientMaxAge || 60000 - // URL of the server (e.g. "http://www.example.com"), autodetects if null - const serverUrl = (options.serverUrl) ? options.serverUrl : null + // URL of the server (e.g. "http://www.example.com"). Used when sending + // sign-in emails. Autodetects current server hostname / domain if null. + const serverUrl = options.serverUrl || null - // Mailserver (defaults to sending from localhost) - const mailserver = (options.mailserver) ? options.mailserver : null + // Mailserver (defaults to sending from localhost if null) + const mailserver = options.mailserver || null // Load body parser to handle POST requests server.use(bodyParser.json()) @@ -77,9 +86,12 @@ exports.configure = (app, server, options) => { // Return session info server.get(path+'/session', (req, res) => { + // @TODO Instead of storing the "user" object in the sesssion, we should + // really just store the User ID and fetch the User object. return res.json({ user: req.session.user || null, isLoggedIn: (req.session.user) ? true : false, + clientMaxAge: clientMaxAge, csrfToken: res.locals._csrf }) })