@inteli.city/node-red-contrib-websocket-plus 1.0.0

Node-RED WebSocket nodes with proxy support

npm install @inteli.city/node-red-contrib-websocket-plus

node-red-contrib-websocket-plus

Node-RED WebSocket nodes with JWT-enforced authentication, multitenant routing, and proxy support.

Drop-in replacement for the built-in Node-RED WebSocket nodes. Node types are prefixed websocket-plus to avoid conflicts with the official nodes. The nodes appear in the palette as ws.in+ and ws.out+.


Table of contents


Core guarantees

  • Connections are rejected at the WebSocket handshake if the token is missing, invalid, or expired.
  • Tenant identity is derived from a verified JWT claim and is immutable for the lifetime of the connection.
  • Message routing enforces tenant boundaries — cross-tenant delivery requires explicit configuration.
  • Flows receive pre-verified identity via msg._session; they do not need to re-authenticate.

Nodes

websocket.in+

Palette label: ws.in+ — type: websocket-plus in

Receives messages from a WebSocket connection and forwards them into the flow. Attach to a websocket-plus-listener (server) or websocket-plus-client (outbound) config node.

Every output message includes:

Property Description
msg.payload The received data.
msg._session Verified connection identity established at handshake time. See msg._session.

websocket.out+

Palette label: ws.out+ — type: websocket-plus out

Sends messages over a WebSocket connection. Routing is determined by the Message scope configured on the attached listener — not by message content.

Scope Behaviour
Session Replies only to the originating connection (msg._session.id).
Tenant Broadcasts to all connections sharing the same verified tenant identity.
Global Broadcasts to every connected client regardless of tenant. Use with care.

Tenant identity cannot be overridden via message fields — routing always uses the verified msg._session.tenantId.

Reply pattern: receive on websocket.in+ → preserve msg._session → send on websocket.out+ with scope set to Session.


websocket-plus-listener (server config node)

Configures a server-side WebSocket endpoint. Authentication and tenant identity are enforced at the HTTP upgrade handshake — connections are rejected before any WebSocket frame is exchanged.

Connection

Field Description
Path URL path relative to httpNodeRoot, e.g. /ws/events. Must not start with /debug/ws.
Send/Receive Payload only passes msg.payload; Entire message passes the full JSON object.

Security — JWT validation

The JWT is always read from the token query parameter. The parameter name is fixed and cannot be changed.

ws://host/path?token=<jwt>
Field Description
JWT validation How to verify the token.
Shared secret HMAC secret (HS256 mode only). Stored as a Node-RED credential.
JWKS URI URL of the JWKS endpoint (JWKS mode only), e.g. https://auth.example.com/.well-known/jwks.json.
JWT issuer Expected iss claim. Tokens with a non-matching issuer are rejected. Optional.
JWT audience Expected aud claim. Tokens without a matching audience are rejected. Optional.

Validation modes:

Mode Description
None No token check — any client can connect. Unsafe.
Shared secret (HS256) Token signature verified with a symmetric HMAC-SHA256 secret.
JWKS endpoint (RS256 / ES256) Token signature verified with a public key fetched from a JWKS endpoint. Recommended for production.

Token expiration (exp) is always checked when present, regardless of mode.

Tenant & scope

Field Description
Tenant from Token claim (default) reads a claim from the verified JWT. Static assigns a single hard-coded tenant to all connections.
Claim name JWT claim used as the tenant identifier. Default: tenant_id. Shown when Tenant from is Token claim.
Tenant ID Hard-coded tenant identifier. Shown when Tenant from is Static.
Message scope Broadcast reach for outbound messages: Session only, Tenant-only (default), or Global.

Advanced

Field Description
Timeout Session timeout in seconds. 0 or empty = no limit.
On token expiry Disconnect (default) — terminates the connection when the JWT exp is reached. Allow stale session — keeps the connection alive after expiry (unsafe).
Rate limit Maximum messages per second per connection. Empty = no limit. (Informational — not enforced by the node itself.)
Max connections Maximum concurrent connections per tenant. Empty = no limit. (Informational — not enforced by the node itself.)

websocket-plus-client (client config node)

Configures an outbound WebSocket connection.

Connection

Field Description
URL Remote WebSocket URL (ws:// or wss://).
TLS Configuration Shown only for wss:// URLs. Select a Node-RED TLS config node.
Subprotocol Optional WebSocket sub-protocol string.
Send/Receive Payload only or Entire message.

Authentication

Field Description
Auth mode JWT (default) or None.
JWT The token appended to the connection URL as ?token=<jwt>. Stored as a Node-RED credential and never exported with the flow.

Auth mode None connects without credentials and will be rejected by any listener with JWT validation enabled.

Advanced

Field Description
Heartbeat When enabled, sends a WebSocket ping at the configured interval (seconds).
Upgrade headers Additional HTTP headers sent with the WebSocket upgrade request. Supports static values and environment variables.

msg._session

Every message produced by a websocket.in+ node includes a _session object populated from the verified JWT claims:

{
  "type": "websocket",
  "id": "<connection-id>",
  "tenantId": "<verified-tenant-id>",
  "subject": "<jwt-sub-claim>",
  "authStatus": "verified",
  "routingScope": "tenant"
}
Field Description
type Always "websocket".
id Unique connection ID. Pass this in msg._session to a websocket.out+ node to reply to the originating client.
tenantId Tenant identity derived at connect time from a verified JWT claim (or the static ID). Immutable for the session lifetime.
subject Value of the JWT sub claim, if present.
authStatus "verified" when a JWT was validated; "none" when JWT validation mode is None.
routingScope The scope configured on the listener: "session", "tenant", or "global".

msg._session is the authoritative source of identity — flows should not recreate or modify it.


Authentication flow

Client → ws://host/path?token=<jwt>
            │
            ▼
  verifyClient (ws handshake)
    ├─ Extract token from query param "token" (fixed)
    ├─ Verify signature (HS256 / JWKS)
    ├─ Check exp, iss, aud
    ├─ Extract tenantId from claim
    └─ REJECT (401) on any failure
            │
            ▼ (accepted)
  handleConnection
    ├─ socket.nrAuth     = { claims, tenantId }
    ├─ socket.nrTenantId = tenantId
    └─ schedule terminate() at JWT exp (if authExpiry=disconnect)
            │
            ▼ (message received)
  msg._session = { id, tenantId, subject, authStatus, routingScope }
            │
            ▼ (websocket.out+)
  broadcast filtered by routingScope + tenantId

Proxy support

Outbound client connections respect standard proxy environment variables and Node-RED settings (checked in this order):

  1. RED.settings.proxyOptions.https_proxy / http_proxy / no_proxy
  2. https_proxy, HTTPS_PROXY, http_proxy, HTTP_PROXY, NO_PROXY environment variables

Installation

npm install @inteli.city/node-red-contrib-websocket-plus

License

Apache-2.0

Node Info

Version: 1.0.0
Updated 5 days ago
License: Apache-2.0
Rating: not yet rated

Categories

Actions

Rate:

Downloads

0 in the last week

Nodes

  • websocket-plus in
  • websocket-plus out
  • websocket-plus-listener
  • websocket-plus-client

Keywords

  • node-red
  • websocket
  • ws