A simple Node.js module to manage users sessions on a web application or any kind of JS apps It uses a Singleton pattern to ensure that only one instance of the module is running at a time. SessionManager is a singleton class that can be used to manage users sessions. For every user that logs in, a new session is created and stored in the database. Every session has a unique ID that is generated by the system. Every session has a setTimeout that expires after a certain time (setSessionTimeout). When a user logs out, the session is deleted from the class. Every action fires an event that can be used to listen to the session manager.
Install with:
npm i users-session-manager
Example of usage:
// Import module with ES6 syntax
import { SessionManager } from 'users-session-manager'
// or
// const SessionManager = require('users-session-manager')
// Create a new instance of the SessionManager class
const SM = new SessionManager()
// Change session Expiration time:
SM.setSessionTimeOut(6)
// Call this to initialize a new user session
SM.loadNewSession("Luca")
SM.loadNewSession("Fabio")
// You can listen to events emitted from this library through eventEmitter object exported
SM.on("activeUserDeleted", (key) => {
console.log(`User ${key} has been deleted`)
})
setInterval(() => {
console.log(SM.getLoggedUsers())
}, 5000)
// Frontend
let session_key = ""
/**
* Function to call try_login API
*
* @param {*} user username text
* @param {*} pwd password text
* @return {*} false if wrong login or the user table ROW of the selected user JSON format
*/
async function TryLogin(user, pwd) {
//console.log(ENDPOINT)
let credentials = {
"username": user,
"password": md5(pwd)
}
const rawResponse = await fetch(ENDPOINT + API_ROUTE, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'api_name': 'try_login'
},
body: JSON.stringify(credentials)
})
const user_data = await rawResponse.json()
if (user_data.length > 0)
session_key = user_data[0].session_key // save session key to the global variable.
//console.log("user_data: ", user_data)
return user_data
}
// And on the next calls, you can use the session_key to call the API
/**
* Function to call get_table_data API
*
* @param {*} siteid number
* @return {*} JSON object
*/
async function GetTableData(page) {
let body = {
page
}
const rawResponse = await fetch(ENDPOINT + API_ROUTE, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'session_key': session_key,
'api_name': 'get_table_data'
},
body: JSON.stringify(body)
})
const sectors = await rawResponse.json()
if (sectors.logout) Logout()
//console.log("sectors: ", sectors)
return sectors
}
// Backend
// API.js route (cutted from the original file)
...
case 'try_login':
response = {
accepted: false,
message: '',
user_data: {}
}
if (typeof(req.body) === 'object') {
try {
const body = req.body
const db_response = await db.tryLogin(body.username, body.password, true) // true to get the session key
if (db_response !== false) {
response.accepted = true
response.message = 'Welcome! 😘'
response.user_data = db_response
response.user_data.session_key = loadNewSession(body.username) // generate a new session key
} else {
response.accepted = false
response.message = 'Wrong username or password... Are you a f**ing HACKER? 💩💩💩'
}
} catch (error) {
response.accepted = false
response.message = 'Error in API call!'
response.user_data = null
} finally {
res.send(JSON.stringify(response))
}
}
break
case 'get_table_data':
response = {
accepted: false,
message: '',
table_data: {}
}
if (typeof(req.body) === 'object') {
try {
const body = req.body
if (await db.validateApiRequest(req.headers.session_key, "get_data")) {
const dbResponse = await db.getTableData(body.table)
if (dbResponse !== false) {
response.accepted = true
response.message = 'OK'
response.table_data = dbResponse
} else {
response.accepted = false
response.message = 'Error in API call!'
response.table_data = null
}
} else {
response.accepted = false
response.message = 'Action not allowed!'
console.warn('Action not allowed! api_name:', api_name)
}
} catch (error) {
response.accepted = false
response.message = 'Error in API call!'
response.analytics = null
} finally {
res.send(JSON.stringify(response))
}
}
break
...
// In file db.js (cutted from the original file)
...
/**
* @async
* @description Validate the session key
* @param {string} sessionKey Session key
* @param {string} action Action to validate
* @throws Will throw if query to DB will fail
* @returns {Promise<boolean>} Return true if session key is valid, false otherwise
*/
async function validateApiRequest(sessionKey, action = undefined) {
const username = getUsernameFromSessionKey(sessionKey)
if (username) {
let user_data = undefined
const query_user_id = {
text: 'SELECT users_management, dataset_management ' +
'FROM users WHERE username = $1;',
values: [username]
}
try {
const userIdRes = await pool.query(query_user_id)
// console.log('[getUserProfilePic]', userProfilePicPathRes.rows)
if (!userIdRes.rows.length) {
user_data = undefined
return false
} else {
/* This may be a string or null */
user_data = userIdRes.rows[0]
}
} catch (err) {
console.error(err)
throw err.message
}
switch (action) {
case "get_data":
{
// check data validity here
}
break
default:
return true
}
}
return false
}
...
// Import module with ES6 syntax
import { SessionManager } from '../index.js';
const http = require('http')
// Create a new instance of the SessionManager class
const SM = new SessionManager();
/**
* Create and start an ioSocket server
* @param {*} app
* "Express" handle
* @param {*} port
* Port the server should listen on
* @returns {SocketIO.Server}
* The newly created server
*/
function startServer(app, port) {
// Create an http server
const server = http.createServer(app)
server.listen(port)
server.on('error', function(error) { onError(error, port) })
server.on('listening', function() { onListening(server) })
// Create the socketIO server
const ENDPOINT = `localhost:3000`;
const { Server } = require("socket.io");
const io = new Server(server, {
cors: {
origin: ENDPOINT,
methods: ["GET", "POST"]
}
});
io.on('connection', (sk) => {
console.log('Browser Connected!')
sk.on('session_key', async function(data) {
const key = data.session_key
console.log(`User ${data.user} joined key ${key}`)
sk.join(key)
})
})
return io
}
SM.initSocketReferences(startServer(app, port)) // Initialize the socket references
SM.on("notifyClientToLogout", (io, key) => { // When a user logs out, notify the client
console.debug(`Session is expired for key ${key}... Logging out now!`)
io.in(key).emit('logout') // Emit the logout event to the client
})
eventEmitter
: Node.js Event Emitter object, is extended by the class. It fires the following events:- 'error': Called when some error happens (eg: Session is rejected)
- 'sessionDeleted': Called when a session is deleted or if expired
- 'sessionCreated': Called when a user session is created
- 'notifyClientToLogout': Called when a session timer is expired, bind this to a Socket.io server to force clients to logout
const io = require('@pm2/io') // Initialize the pm2 io module
// The PM2 IO metrics to monitor the number of connected users
const realtimeUser = io.counter({
name: 'Realtime Users',
id: 'app/realtime/users',
})
SM.on("sessionCreated", (key) => { // When a user logs out, notify the client
realtimeUser.inc() // Increment the number of active users
})
SM.on("sessionDeleted", (key) => { // When a user logs out, notify the client
realtimeUser.dec() // Decrement the number of active users
})
- SessionManager ⇐
EventEmitter
SessionManager is a class that manages the sessions of the users.
- sessions :
Object
The sessions of the users.
- MIN_SESSION_TIMEOUT :
number
The minimum session timeout.
- settings :
Object
The settings of the session manager.
- log(msg) ⇒
void
Logs a message to the console if the debug flag is set to true in the config.
SessionManager is a class that manages the sessions of the users.
Kind: global class
Extends: EventEmitter
- SessionManager ⇐
EventEmitter
- .setSessionTimeOut(sessionTimeout) ⇒
boolean
- .getSessionTimeout() ⇒
number
- .getLoggedUsers() ⇒
array
- .initSocketReference(ioRef) ⇒
boolean
- .getSocketReference() ⇒
SocketIO.Server
- .loadNewSession(username) ⇒
string
- .setSessionData(key, data) ⇒
boolean
- .getSessionData(key) ⇒
object
- .restartSessionTimer(key) ⇒
boolean
- .getSessionDetails(key) ⇒
object
|boolean
- .deleteSession(key) ⇒
boolean
- .deleteAllSessions() ⇒
boolean
- .sendLogoutMessage(key) ⇒
boolean
- .createNewSessionTimer(key, username) ⇒
NodeJS.Timeout
- .checkSessionStatus(key) ⇒
boolean
- .getUsernameFromSessionKey(key) ⇒
string
- .setSessionTimeOut(sessionTimeout) ⇒
This function is used to set the session timeout
Kind: instance method of SessionManager
Returns: boolean
- true or false: true if ok
Param | Type | Description |
---|---|---|
sessionTimeout | number |
The session timeout in seconds |
Example
setSessionTimeOut(3000) // Returns true or false
This function is used to get the session timeout
Kind: instance method of SessionManager
Returns: number
- The session timeout in seconds
Example
getSessionTimeOut() // Returns 3000
This function is used to get the list of logged users
Kind: instance method of SessionManager
Returns: array
- The list of logged users
Example
getLoggedUsers() // Returns ['Gino', 'Gino2']
Function to copy the Socket IO http server reference
Kind: instance method of SessionManager
Returns: boolean
- true or false, true if ok
Param | Type |
---|---|
ioRef | * |
Function to get the socket reference
Kind: instance method of SessionManager
Returns: SocketIO.Server
- The socket reference
Function to add users sessions in this module. Use it at login
Kind: instance method of SessionManager
Returns: string
- user unique key
Param | Type | Description |
---|---|---|
username | string |
The username provided on successful login |
Example
addSession('Gino') // Returns 'session_key'
Function to set the property 'data' of a session. Use it for example to store something in the session, like the user actions history, etc.
Kind: instance method of SessionManager
Returns: boolean
- true or false, true if ok
Throws:
Error
If the session_key is not found
Param | Type | Description |
---|---|---|
key | string |
The session_key provided on successful login |
data | object |
The data to be stored in the session |
Example
setSessionData('session_key', {'actions': ["logged in", ...]}) // Returns true or false
Function to get the property 'data' of a session. Use it for example to get the user actions history, etc.
Kind: instance method of SessionManager
Returns: object
- The data stored in the session
Throws:
Error
If the session_key is not found
Param | Type | Description |
---|---|---|
key | string |
The session_key provided on successful login |
Example
getSessionData('session_key') // Returns {'actions': ["logged in", ...]}
Function that restart the session timer. Use it after an API call to keep the session alive.
Kind: instance method of SessionManager
Returns: boolean
- true or false, true if ok
Throws:
Error
If the session key is not found
Param | Type | Description |
---|---|---|
key | string |
The session_key |
Example
restartSessionTimer('session_key') // Returns true or false
Function to get details of a session. Use it to get the username, the creation date and the data.
Kind: instance method of SessionManager
Returns: object
| boolean
- The session details or false if not found
Throws:
Error
If the session key is not found
Param | Type | Description |
---|---|---|
key | string |
The session_key |
Example
getSessionDetails('session_key') // Returns {'username': 'Gino', 'createdAt': 1523456789, 'data': {'actions': ["logged in", ...]}}
Function to delete users sessions in this module. Use it at client logout
Kind: instance method of SessionManager
Returns: boolean
- true or false, true if ok
Throws:
Error
If the session_key is not found
Param | Type | Description |
---|---|---|
key | string |
The session_key provided on successful login |
Example
deleteSession('session_key') // Returns true or false
Function to delete all sessions
Kind: instance method of SessionManager
Returns: boolean
- true or false, true if ok
Use this to notify the client to logout with WebSocket
Kind: instance method of SessionManager
Returns: boolean
- true or false, true if ok
Param | Type | Description |
---|---|---|
key | string |
The session_key |
Example
sendLogoutMessage('session_key') // Returns true or false
Function to return a new setTimeout object and start it.
Kind: instance method of SessionManager
Param | Type | Description |
---|---|---|
key | string |
The session_key |
username | string |
The username, only for logging features |
Example
createNewSessionTimer('session_key', 'username') // Returns a new setTimeout object
Use this before every API.js function execution.n the stored collection
Kind: instance method of SessionManager
Returns: boolean
- true or false: true if session is active
Throws:
Error
if the session is not validError
if the session is expired
Param | Type | Description |
---|---|---|
key | string |
the user key generated at login |
Example
checkSessionStatus('my_session_key') // true or false
Function to get the username from a session key
Kind: instance method of SessionManager
Returns: string
- The username or false if not found
Param | Type | Description |
---|---|---|
key | string |
The session key |
Example
getUsernameFromSessionKey('123456789_123456789') // 'username'
The sessions of the users.
The minimum session timeout.
The settings of the session manager.
Logs a message to the console if the debug flag is set to true in the config.
Kind: global function
Param | Type |
---|---|
msg | string |