Interval-Problem!

I have creted an interval-function, which runs on a public JS-file .

Something like that (short-form)…

export function START(){
  lastActionStamp=new Date();
  myInterval = setInterval(xxx, 1000*60*onlineCheckInterval);
    xxx();
  }
}
function xxx() {console.log("CheckInterval for ONLINE-USER running...");
  .......
  ......
  .......
  .......
  if(onlineTime>maxOnlineTime*60000) {
    INFO.message = "Your online-time is expired ---> time for Log-Out!!!"; clearInterval(myInterval);
  }
  else {INFO.message = "You are lucky, your time has not expired yet --> you can stay!";}
}  

Starting that interval-function from everywhere of your site → possible.
But if i try to stop it from everywhere of my site → it do not work as expected.

Yesterday, everything just seemed to work well. Testing it today → gives me headaches again.

Calling the same interval-function from two different places → (master-page & normal-page), causes two different running → intervals, although the same functions was called.

Calling the function first stops the current running interval (RESET) and starts it again.

Question: → Why it starts a second running INTERVAL (when starting it from another site-location) ?
One more time → The code is running on the PUBLIC-JS-file.

With a simple call → something like this → START();

The strange behaviour can be tested here…

https://russian-dima.wixsite.com/login-system

These are the two buttons, which RESETs the INTERVAL.

  1. One on the master-page
  2. one on the normal-page

I would guess that myInterval is not global scope. So you are creating two distincts variables that are holding setInterval() . Try assigning a random UUID to check this theory.

If you are trying to setup a expiring user time, I recommend another method using JWT (JSON Web Token). The user will have a token every time he/she logs in, and the token has an expiration. Every action on the site, you just need to check if the token is expired or not.

@bwprado
Thanks Bruno! I will have to collect more informations First to be able to realize your suggested ideas.

So in your opinion, is the coding i am trying to do possible using a interval-function?

@bwprado
Thanks Bruno! I will have to collect more informations First to be able to realize your suggested ideas.

So in your opinion, is the coding i am trying to do possible using a interval-function?

I have made some thoughts about the Exploration-Token, but it seems that my own version is a bit more precise. When a Token is expired, then it is expired right. Looks not flexible and dynamic enough.

My intention was to check as exact as possible, when a user is not active anymore on my site, givin him a predifined (modifyable) time, when the user will be logged-out from the site. This for i used the i tnterval-function, to refill the expiring time, if the user do some action on my site.
Do the user sleep to long–> Intervall ends and he wil be logged out automaticaly, do he wake up and is fast enough to deactivate the running Intervall (by doing some action on my site) → than he can stay, because expiration-interval was resetet and starts from 0 again.

Is this same functionality possible by a usage of a token? Do i get even any other atvantages, when going the → token-way???

I think I understand what you want, that the time allowed be renewed every time the user interacts with the site. It can be done via token, as many websites use the JWT method. When the user is away and tries to interact with the site, you just have to check if the token is not expired, and if the user interacts before the time expires, you just issue a new token with a renewed expiration date. But, the website will be visible while he is not interacting with it, and that might be a deal breaker for you. And, for that, you could use a auto refresh page after a while.

Ok, thanks for info. I will give it a try (although i do know almost nothing about TOKENS).

BTW ----> Congratulation!

Hey @russian-dima

You can store the token in the session storage, and update it every while, before updating, check if the token is expired, either by checking its creation time or by checking against a DB of tokens (more secure).

Note: You should only set the interval in the code in the masterPage.js file.

// masterPage.js
import { validateSession } from 'public/functions.js';

const interval_duration = 5 * 1000; // 5 sec

$w.onReady(() => {    
    setInterval(validateSession, interval_duration)
})
// Public: functions.js
import { session } from 'wix-storage';
import users from 'wix-users';

const token_lifetime = 20 * 1000 * 60; // 20 min

export function renewToken() {
    const obj = {
        token = Math.floor(Math.random() * 10000000000000000000000000),
        expiration: new Date(new Date().getTime() + token_lifetime)
    }
                 
    session.setItem(JSON.stringify(obj))
}

export function validateSession() {     
     const data = session.getItem('session');
     
     if (typeof data === 'string') {
         const obj = JSON.parse(data);
         if (new Date().getTime() >= obj.expiration_time) {
             // Log the user out.
             users.logout();
        }
    } else {
        renewToken();
    }
}

This way, you won’t need to worry about clearing the interval as it only validates the current token, but you still need to renew the token by calling the renew( ) function.

If the token doesn’t get renewed within 20 min, the user will be automatically kicked out of the website (logged out).

Note #1: If you want a secure (reliable way) to achieve the same, create a function on the backend to retrieve the token from the DB, and check if it’s still valid or not, and return the appropriate response to the frontend.

// Backend: sessions.jsw
import { query } from 'wix-data';
import { currentUser } from 'wix-users-backend';

export function validateSession(token) {
    return query('tokens').eq('_owner', currentUser.id).eq('token', token)
    .limit(1).find({ suppressAuth: true }).then(x => {
        if (x.length > 0) {
            const item = x.items[0];
            if (new Date().getTime() >= item.expiration_time) {
                return { type: 'success', action: 'logout' }
            } else {
                return { type: 'success', action: 'renew' }
            }
        } else {
            return { type: 'success', action: 'renew' }
        }
    })
}

Note #2: If you want to validate the token on the backend, you should consider the time zone offset on the browser as servers use UTC time instead.


Hope this helps~!
Ahmad

@russian-dima Thank you!

Nice.

This is the way I used in one of my projects:

Created a file on the backend called login.jsw :

import jwt from "jsonwebtoken"
import { getSecret } from "wix-secrets-backend"

//This function creates the payload
async function createPayload({_id, name, roles}) {
    return {
        _id: await encrypt(_id),
        name,
        roles
    }
}
//This is to sign the Token, the payload can have anything you want
export async function signToken(payload) {
    const secret = await getSecret("jwt_secret")
    return await jwt.sign(payload, secret, { expiresIn: "24h" }) //You define time here
}

//This is the function that verifies the token, if it is valid, and if it is not expired.
export async function verifyToken(token) {
    const secret = await getSecret("jwt_secret")
    const verifiedToken = await jwt.verify(token, secret, (error, decoded) => {
        if (error) return false
        else return decoded
    })
    return verifiedToken
}

//This is an example of how I use it to check the login status
export async function verifyLogin(sessionToken, permission) {
    const currentUser = await verifyToken(sessionToken) //If it is ok, it returns the payload
    if (currentUser) {
        let isAllowed = await verifyRoles(currentUser, permission)
        isAllowed = currentUser || false
        return isAllowed
    }
    return false
}

Thanks @bwprado

The actual token is only used to have a unique value in the DB when he performs a query against the DB, the actual token doesn’t have any security effect on his workflow, he just wants a way to logout inactive members (as banks and websites with sensitive data do).

THANK you guys, i will have a look on all the provided suggestions.
I will give feedback later

Big thanks!!!:+1:

Ahmad do your CODE work on the whole site? That means…

  1. You are able to renew your token right from master-page…
  2. …aswell as from the normal-page by a triggered action of a user?

I run again into the same issue :sleepy::sweat_smile::sleepy::sweat_smile:.

  1. When interval starts from MASTER-PAGE → i can’t renew the token (in my case → renew the → onlineTime) from normal page.

  2. And on oposite situation → when starting my interval from normal page, a REFRESH of the token (in my case → renew the → onlineTime), can’t be done.

I wanted be able to REFRESH from EVERYWHERE.

Here the current code: WHAT’s MY LOGICAL ERROR ???

MASTER-PAGE:

// masterPage.js
import { validateSession, set_newTimestamp } from 'public/events.js';

//------User-Interface--------------------
var OnlineCheckClocking = 5; //(sec)
//------User-Interface--------------------

$w.onReady(() => {set_newTimestamp(); //<<-- sets the first timeStamp
   let myInterval = setInterval(validateSession, OnlineCheckClocking*1000); //(sec)     
   $w('#button1').onClick(()=>{set_newTimestamp();}); //<-- Timestamp-Reset
})

EVENTS-PAGE:

//------User-Interface--------------
var DATA, maxOnlineTime = 0.5;
//------User-Interface--------------

export function set_newTimestamp() {console.log("Token renewed!");
  let lastActionStamp=new Date(); console.log("Last Action-Stamp: ", lastActionStamp);

  DATA = {
    token: Math.floor(Math.random() * 10000000000000000000000000),
    maxOnlineTime: maxOnlineTime,
    expiration: new Date(lastActionStamp.getTime() + 1000 * 60*maxOnlineTime)
  }; console.log(DATA);  
}

export function validateSession() {console.log("Validation running....");
  let onlineTime = Number(new Date())-DATA.expiration; 
  console.log("Current-Online-Time: ", onlineTime); 
  DATA.onlineTime = onlineTime; console.log("Max-Online-Time: ", maxOnlineTime);

  if (new Date().getTime()>=DATA.expiration) {
    console.log({onlineTime: DATA.onlineTime, expiration: DATA.expiration, type:'success', action:'log-out', msg: 'Your online-time is expired ---> time for Log-Out!!!'});
  }
    
  else {
  console.log({onlineTime: DATA.onlineTime, expiration: DATA.expiration, type:'success', action:'renew', msg: 'You are lucky, your time has not expired yet --> you can stay!'});
  }  
}

-I not really used the token itself —> perhaps i missunderstood something.
What for do i need the token one more time?
I also cut off the SESSION-code-part.

BTW: OUTPUT can be seen directly on the login-system.

Bruno → Your example will be the next of my victims xD, if this does not work xD!

@russian-dima the interval must be created only from the masterPage.js , the interval will be lost/reset/renewed when navigating, on contrast, the token can be renewed from anywhere, it’s separate from the interval function, therefore, it’s not affected by any means.

You trying to apply my code without understanding it well, my code doesn’t renew the token (via the interval), the interval only validates the stored token (from the session storage), to update the token, you need to call the renewToken( ) function from the appropriate event handlers, e.g. when the viewport is changed, when something is clicked or hovered.

The token itself is useless, don’t give it that much of importance, it’s only used in the backend scenario to query the DB, you can ditch it altogether if you’re running the validation on the FE.

@ahmadnasriya Ok, i will give it a second try :sweat_smile:

When i was inspecting your suggested code → i did not find…

$w.onReady(()=>{setInterval(sessionValidationFunc, interval_duration)})

Ok, will try to cut in peaces…

CHECK-POINT-1:

You trying to apply my code without understanding it well, my code doesn’t renew the token (via the interval), the interval only validates the stored token (from the session storage), to update the token, you need to call the renewToken( ) function from the appropriate event handlers, e.g. when the viewport is changed, when something is clicked or hovered.
Ok my code also do not renew the token, or to be more precise → my INTERVAL do not renew the token (or in my case → the online-time).

let myInterval = setInterval(validateSession,OnlineCheckClocking*1000);//(sec)

It’s just calling the → validateSession ← function every few (defined) seconds.
started from → MASTER-PAGE (onReady).

This is my → validateSession (running on EVENTS-PAGE)

export function validateSession() {console.log("Validation running....");
  let onlineTime = Number(new Date())-DATA.expiration; console.log("Current-Online-Time: ", onlineTime); 

  DATA.onlineTime = onlineTime;

  if (new Date().getTime()>=DATA.expiration) {
    console.log({onlineTime: DATA.onlineTime, expiration: DATA.expiration, type:'success', action:'log-out', msg: 'Your online-time is expired ---> time for Log-Out!!!'});}
  else {console.log({onlineTime: DATA.onlineTime, expiration: DATA.expiration, type:'success', action:'renew', msg: 'You are lucky, your time has not expired yet --> you can stay!'});}  
}

Just checking for → setted EXPIRATION-TIME(DATE).

I do not stop any INTERVAL and START it again anymore. I use your technique just to validate if the setted Time-Stamp + given ONLINE-TIME (EXPIRATION-TIME) is already reached.

CHECK-POINT-1 → everything ok?


CHECK-POINT-2:
To renew the expiration-time (refill the online-time), i use a simple button-click…
One from master-page → test-button → “button1” —> “b1”
And one from normal-page → “btnAnalytics”.

$w('#btnAnalytics').onClick(()=>{set_newTimestamp();});
$w('#b1').onClick(()=>{set_newTimestamp();});

The token itself is useless, don’t give it that much of importance, it’s only used in the backend scenario to query the DB
Yes i do not take any attention on the token itself → for my case not important.
Imprtant for me is just the → TIME-STAMP / defined (ONLINE-TIME).

But exactly here i have my problem. Calling the → set_newTimestamp()-function
(what is nothing than a modified function of your example) which should RENEW the Time-STAMP → (it works well when firing this function from MASTER-PAGE), it does not work from the → normal-page.

The Time-Stamp do not renew at all. The INTERVAL continues his job during this.

This is my understanding.

It is not working perhaps → because i did cut of the SESSION?
I have used …

var DATA;

… directly on the EVENTS-PAGE instead.

Could this be the issue?

EDIT: CODE-COMPARISSON…

CODE by AHMAD: (Master-Page)
Note: You should only set the interval in the code in the masterPage.js file.

import { validateSession } from 'public/functions.js';const     
interval_duration = 5 * 1000; // 5 sec
$w.onReady(() => {setInterval(sessionValidationFunc, interval_duration)})

vnCode: (Master-Page)

$w.onReady(()=> {set_newTimestamp();
    let myInterval = setInterval(validateSession, OnlineCheckClocking*1000); //(sec)
    $w('#b1').onClick(()=>{set_newTimestamp();});
})

Also did not found in your code → where do you set the first Time-Stamp ( renewToken() ), when user logs in ? For me, it seems → that i miss somethign in your provided code.

@russian-dima Replace the " sessionValidationFunc " with the imported function, I forgot to do so.

Yes, the problem is that you’re declaring a new timestamp, you should store/retrieve it from the storage to ensure the time is the same.

Here’s what my code does:
a) Validating the active time (the public method):
a.1) Retrieve the latest timestamp - create new one if it wasn’t found. (first visit).
a.2) Check if timestamp exceeded the current time, and log the user out if it did.

b) Renew the timestamp: Creating a new date by getting the current time and add the duration of the allowed inactivity.

Very simple flow.

@ahmadnasriya
Ok, will work on it…

Ok, well! I got it → i found the main reason, why i did not got the results i wanted to have!

Like right from the beginning, my code was going into the right direction.
What i wanted was just a function, which, logs-out a logged-in meber after a (specific predefined) period of time without making any action on the current site (no matter from which page the actions come from).

In short form → user is for example 10 min without any action on the site → he will be automaticaly logged-out!

My setup should be as most simple as possible, without any interaction with more build-in functionality, like NPM-MODULES (showed in example by Bruno), without any extra(separate) DBs and also, without any security-provements and tokens.

  • i do not see any case → where i need a security check (give me an example if i am wrong, or do not recognize the need for it)

-i do not need any DBs (here i would revise my statement ) when working on futher developement on the Login-System → in future i will need that functionality, thought → ANALYTICS-integration.

-i do not want to use external NPM-packages (trying to keep my project as most as possible working on it’s own)

  • at the end i really did not realize/recognize, what for i need a token :sweat_smile:SORRY
  • about SECURITY-QUESTION → an example would be great!

HOWEVER!

To generate a simple → functionality on your site for → “automatic member-logout”, would be something like this…

GENERAL-Page-CODE:

import { validateSession, set_newTimestamp } from 'public/events.js';
//------User-Interface-----------------
var OnlineCheckClocking = 10; //(sec)
//------User-Interface-----------------

$w.onReady(() => {set_newTimestamp();
  let myInterval = setInterval(validateSession, OnlineCheckClocking*1000);
  $w('#button1').onClick(()=>{set_newTimestamp();});
})

EVENT-Page-CODE:

import { session } from 'wix-storage';
//------User-Interface-------------------------
var maxOnlineTime = 0.5; //0,5-Minutes --> 30-secs! --> Logging-Out ater 30-secs
//------User-Interface-------------------------
//var DATA;  <<<<<----------THAT WAS THE PROBLEM ! ! ! 

export function set_newTimestamp() {
   let lastActionStamp=new Date(); 
   let expirationDate = 
   new Date(lastActionStamp.getTime() + 1000 * 60*maxOnlineTime); 

   let DATA={};
       DATA.maxOnlineTime = maxOnlineTime;
       DATA.expiration = expirationDate.getTime();
   session.setItem("session", JSON.stringify(DATA));  <<<<<-----THAT WAS THE SOLUTION ! ! ! 
}

export async function validateSession() {let message;
   let DATA = await JSON.parse(session.getItem('session'));
   let onlineTime = Number(new Date())-Number(DATA.expiration);  
   DATA.onlineTime = onlineTime;

   if (new Date()>=DATA.expiration) {
   	message = {
		onlineTime: DATA.onlineTime,
		expiration: DATA.expiration,  
		type:'success', action:'log-out', 
		msg: 'Your online-time is expired ---> time for Log-Out!!!'
	}

  }
  else {
	message = {
		onlineTime: DATA.onlineTime, 
		expiration: DATA.expiration, 
		type:'success', action:'renew', 
		msg: 'You are lucky, your time has not expired yet --> you can stay!'
	 }
    }  
}

Of course all expanded suggestions were/are very useful to be integrated…

SECURITY-ASPECT:

  • BACKEND-CODING + SECRETS-MANAGER

CODE-INTEGRABILITY

  • perhaps not using external coding (NPMs) ← my opinion → saves CODE-FLEXIBILITY.

EXPANDED-FUNTIONALITY
-using own created DATABSE → for more features

But this is just my sight of view.

If you disagree → let’s talk about it :laughing:

And many THANKS to you two!!! (i got some new insights and ideas and knowledge), trough your help!

Normaly i would award myself XDDDD, but the best Answer will go to Ahmad :wink::rofl::stuck_out_tongue_winking_eye:

And in summary:
The main ISSUE → was → the missing Wix-Storage-Part!
So now is clear, that saving some VALUES on PUBLIC-EVENTS-PAGE will not work from everywhere of your pages.

Using a DB or Wix-Storage → SOLVES everything!