users-session-manager
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.
Installation
Install with:
npm i users-session-manager
Example of usage:
import { SessionManager } from 'users-session-manager'
const SM = new SessionManager()
SM.setSessionTimeOut(6)
SM.loadNewSession("Luca")
SM.loadNewSession("Fabio")
SM.on("activeUserDeleted", (key) => {
console.log(`User ${key} has been deleted`)
})
setInterval(() => {
console.log(SM.getLoggedUsers())
}, 5000)
Example of Frontend and Backend session exchange
let session_key = ""
async function TryLogin(user, pwd) {
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
return user_data
}
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()
return sectors
}
...
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)
if (db_response !== false) {
response.accepted = true
response.message = 'Welcome! 😘'
response.user_data = db_response
response.user_data.session_key = loadNewSession(body.username)
} 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
...
...
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)
if (!userIdRes.rows.length) {
user_data = undefined
return false
} else {
user_data = userIdRes.rows[0]
}
} catch (err) {
console.error(err)
throw err.message
}
switch (action) {
case "get_data":
{
}
break
default:
return true
}
}
return false
}
...
Integrate with Socket.io server to notify clients
import { SessionManager } from '../index.js';
const http = require('http')
const SM = new SessionManager();
function startServer(app, port) {
const server = http.createServer(app)
server.listen(port)
server.on('error', function(error) { onError(error, port) })
server.on('listening', function() { onListening(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))
SM.on("notifyClientToLogout", (io, key) => {
console.debug(`Session is expired for key ${key}... Logging out now!`)
io.in(key).emit('logout')
})
Exported APIs
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
Integrate with metrics tools like PM2
const io = require('@pm2/io')
const realtimeUser = io.counter({
name: 'Realtime Users',
id: 'app/realtime/users',
})
SM.on("sessionCreated", (key) => {
realtimeUser.inc()
})
SM.on("sessionDeleted", (key) => {
realtimeUser.dec()
})
Classes
- SessionManager ⇐
EventEmitter
SessionManager is a class that manages the sessions of the users.
Constants
- sessions :
Object
The sessions of the users.
- MIN_SESSION_TIMEOUT :
number
The minimum session timeout.
- settings :
Object
The settings of the session manager.
Functions
- log(msg) ⇒
void
Logs a message to the console if the debug flag is set to true in the config.
SessionManager ⇐ EventEmitter
SessionManager is a class that manages the sessions of the users.
Kind: global class
Extends: EventEmitter
sessionManager.setSessionTimeOut(sessionTimeout) ⇒ boolean
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)
sessionManager.getSessionTimeout() ⇒ number
This function is used to get the session timeout
Kind: instance method of SessionManager
Returns: number
- The session timeout in seconds
Example
getSessionTimeOut()
sessionManager.getLoggedUsers() ⇒ array
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()
sessionManager.initSocketReference(ioRef) ⇒ boolean
Function to copy the Socket IO http server reference
Kind: instance method of SessionManager
Returns: boolean
- true or false, true if ok
sessionManager.getSocketReference() ⇒ SocketIO.Server
Function to get the socket reference
Kind: instance method of SessionManager
Returns: SocketIO.Server
- The socket reference
sessionManager.loadNewSession(username) ⇒ string
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')
sessionManager.setSessionData(key, data) ⇒ boolean
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", ...]})
sessionManager.getSessionData(key) ⇒ object
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')
sessionManager.restartSessionTimer(key) ⇒ boolean
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')
sessionManager.getSessionDetails(key) ⇒ object
| boolean
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')
sessionManager.deleteSession(key) ⇒ boolean
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')
sessionManager.deleteAllSessions() ⇒ boolean
Function to delete all sessions
Kind: instance method of SessionManager
Returns: boolean
- true or false, true if ok
sessionManager.sendLogoutMessage(key) ⇒ boolean
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')
sessionManager.createNewSessionTimer(key, username) ⇒ NodeJS.Timeout
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')
sessionManager.checkSessionStatus(key) ⇒ boolean
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')
sessionManager.getUsernameFromSessionKey(key) ⇒ string
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')
sessions : Object
The sessions of the users.
Kind: global constant
MIN_SESSION_TIMEOUT : number
The minimum session timeout.
Kind: global constant
settings : Object
The settings of the session manager.
Kind: global constant
log(msg) ⇒ void
Logs a message to the console if the debug flag is set to true in the config.
Kind: global function