npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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

v1.0.1

Published

Node-RED WebSocket nodes with proxy support

Readme

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