users-session-manager

1.0.16 • Public • Published

GitHub Workflow Status Coverage Status npm version npm npm bundle size GitHub

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.

Readme Card https://nodei.co/npm/users-session-manager.png?downloads=true&downloadRank=true&stars=true

Installation

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)

Example of Frontend and Backend session exchange

// 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
}
...

Integrate with Socket.io server to notify clients

// 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
})

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') // 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
})

Classes

SessionManagerEventEmitter

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) // Returns true or false

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() // Returns 3000

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() // Returns ['Gino', 'Gino2']

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

Param Type
ioRef *

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') // Returns 'session_key'

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", ...]}) // Returns true or false

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') // Returns {'actions': ["logged in", ...]}

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') // Returns true or false

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') // Returns {'username': 'Gino', 'createdAt': 1523456789, 'data': {'actions': ["logged in", ...]}}

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') // Returns true or false

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') // Returns true or false

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') // Returns a new setTimeout object

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 valid
  • Error if the session is expired
Param Type Description
key string the user key generated at login

Example

checkSessionStatus('my_session_key') // true or false

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') // 'username'

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

Param Type
msg string

Package Sidebar

Install

npm i users-session-manager

Weekly Downloads

1

Version

1.0.16

License

SEE LICENSE IN LICENSE

Unpacked Size

47 kB

Total Files

8

Last publish

Collaborators

  • elius94