getClientDetails(socket, node) {
        // Add page name meta to allow caches and other flows to send back to specific page
        // Note, could use socket.handshake.auth.pageName instead
        let pageName
        const headers = socket.request.headers
        const handshake = socket.handshake

        if ( handshake.auth.pathName ) pageName = getClientPageName(socket, node)
        const realClientIP = getClientRealIpAddress(socket)

        // WARNING: The socket.handshake data can only ever be changed by the client when it (re)connects
        const client = {}
        client._uib = {
            /** What was the originating uibuilder URL */
            'url': node.url,

            '_socketId': socket.id,
            /** Is this client reconnected after temp loss? */
            'recovered': socket.recovered,
            /** Do our best to get the actual IP addr of client despite any Proxies */
            'ip': realClientIP,
            /** The referring webpage, should be the full URL of the uibuilder page */
            'referer': headers.referer,

            // Let the flow know what v of uib client is in use
            'version': handshake.auth.clientVersion,
            /** What is the stable client id (set by uibuilder, retained till browser restart) */
            'clientId': handshake.auth.clientId,
            /** What is the client tab identifier (set by uibuilder modern client) */
            'tabId': handshake.auth.tabId,
            /** What was the originating page name (for SPA's) */
            'pageName': pageName,
            /** The browser's URL parameters */
            'urlParams': handshake.auth.urlParams,
            /** How many times has this client reconnected (e.g. after sleep) */
            'connections': handshake.auth.connectedNum,
            /** True if https/wss */
            'tls': handshake.secure,
            /** When the client connected to the server */
            'connectedTimestamp': (new Date(handshake.issued)).toISOString(),
            // 'browserConnectTimestamp': handshake.auth.browserConnectTimestamp,
            'connectHeaders': headers,
        }

        // @ts-ignore
        const clientTimeDifference = (new Date(handshake.issued)) - (new Date(handshake.auth.browserConnectTimestamp))
        // Only include this if The difference between the timestamps is > 1 minute - output is in milliseconds
        if (clientTimeDifference > 60000) client._uib.clientTimeDifference = clientTimeDifference

        let authProvider
        if (headers['cf-access-authenticated-user-email']) authProvider = 'CloudFlare Access'
        else if (handshake.auth?.user?.userId) authProvider = 'FlowFuse'
        else if (headers['x-user-id']) authProvider = 'Keycloak'
        else if (headers['x-authentik-uid']) authProvider = 'Authentik'
        else if (headers['remote-user'] || headers['x-remote-user']) authProvider = 'Custom'
        else if (headers['x-forwarded-user']) authProvider = 'Proxied Custom'

        const userID = headers['cf-access-user'] || headers['cf-access-authenticated-user-email'] || handshake.auth?.user?.userId || headers['x-authentik-uid'] || headers['remote-user'] || headers['x-remote-user'] || headers['x-forwarded-user'] || headers['x-user-id'] || undefined

        // client._client is ONLY added for recognised authenticated clients
        if (authProvider !== undefined && userID !== undefined) {
            const email = headers['cf-access-authenticated-user-email'] || headers['x-authentik-email'] || headers['remote-email'] || headers['x-user-email'] || undefined
            const name = headers['x-authentik-name'] || headers['remote-name'] || headers['x-remote-name'] || handshake.auth?.user?.name
            client._client = {
                userId: userID,
                socketId: socket.id,
                email: email,
                provider: authProvider,
                agent: headers['user-agent'] || null,
                ip: realClientIP,
                host: headers['host'],
                name: name,
            }
            if (headers['x-forwarded-groups']) client._client.groups = headers['x-forwarded-groups']
            if (headers['x-authentik-groups']) client._client.groups = headers['x-authentik-groups']
            if (headers['x-authentik-username']) client._client.username = headers['x-authentik-username']
            if (headers['x-user-role']) client._client.role = headers['x-user-role']
            if (handshake.auth?.user?.image) client._client.image = handshake.auth.user.image
        }

        // Run uibuilder.hooks.msgReceived hook if it exists - NOTE: msg might be amended by the hook
        // Allows client data to be amended
        this.hooks('clientDetails', { client, socket, node })

        return client
    }
Get client details for including in Node-RED messages
@param {socketio.Socket} socket Reference to client socket connection
@param {uibNode} node Reference to the uibuilder node instance
@returns {object} Extracted key client information