WebSub Hub
  • 14 Feb 2023
  • 5 Minutes to read
  • Contributors
  • Dark
    Light
  • PDF

WebSub Hub

  • Dark
    Light
  • PDF

Article summary

WebSub Hub Nodes are Nodes that implement the Hub actor in the WebSub specification. Hubs sit between the Publisher and Subscribers. They support registration and maintenance of subscriptions to topics and the distribution of topic content updates from the Publisher to the topic's Subscribers. All content distribution is accomplished via webhooks. Any MIME type may be distributed by the Publisher.

WebSub Hubs Nodes receive echo.websub Messages from the Publisher (which is probably another Node or Nodes in your Tenant) and route those to active subscribers based upon the Topic URL. Just like all other Nodes in EchoStream, Messages that you send to the WebSub Hub Node are guaranteed to be delivered (unless you configure deliveryRetries > 0) and are guaranteed to be in order.

Authentication and authorization of new Subscribers is accomplished via the Api Authenticator Function that you provide to the WebSub Hub Node.

Content Distribution

WebSub Hub Nodes accept echo.websub Messages for WebSub content distribution. The echo.websub Message type is a JSON message and has the following attributes:

  • contentBase64: The actual content that will be distributed. Must be binary in base64 encoding. Conflicts with contentUrl, and will not be used if that field is present. If this is present and contentType is not present, text/plain is assumed as the contentType to send. The contents of this field will be sent as-is.
  • contentType: The MIME type of the content. If is not present and contentUrl is present, will use the Content-Type received from the contentUrl if present. If it is present it will always be the Content-Type sent to the subscriber.
  • contentUrl: The URL to retrive the content to send using HTTP GET. Conflicts with contentBase64; if both are present contentUrl will be used unless the provided URL returns an error code. The body retrieved from the URL will be sent to the subscriber as-is.
  • topic: The topic URL that this content is for. REQUIRED

WebSub Hub Nodes are capable of distributing any Content-Type. The Content-Type being distributed should match the content type negotiated during topic discovery.

Note - While WebSub Hubs (and Webhooks in general) are normally used to distribute text-based content (e.g. - JSON, XML, Plain text), the WebSub standard itself makes no such restrictions; in fact, the standard explicitly states that the Hub must be content unaware.

Api Authenticator Function

The Api Authenticator Function that you give the WebSub Hub Node controls how the Hub responds to new subscription requests. You may provide this Python function either directly in the Webhook Node (an inline function) or by specifying the name of a Api Authenticator Function in your Tenant's Function Library.

If you do not provide an Api Authenticator Function, all subscription requests will be denied.

Api Authenticator functions are also allowed to have the async keyword prior to their definition.

Api Authenticator functions used in WebSub Hub Nodes must conform to the following template:

def authenticator(*, context, request, **kwargs):
    from typing import TYPE_CHECKING, cast

    from fastapi import HTTPException
    from starlette.authentication import AuthCredentials, BaseUser, SimpleUser
    
    if TYPE_CHECKING:
        from echostream_function_context import Context
        from fastapi import Request

        context = cast(Context, context)
        request = cast(Request, request)

    # TODO - Perform authentication using the context and request here
    # You must either return None or return a tuple containing an AuthCredentials
    # and a subclass of BaseUser. If you return None or a Base User that returns False
    # from is_authenticated, then the Request will be rejected with a 403.
    #
    # The BaseUser subclass that you return MUST implement the identity property, as below.
    #
    # The AuthCredentials returned should contain one or more regular expressions that describe
    # the topics that the subscriber is allowed to subscribe to. In the example below, all topics are
    # authorized.
    
   class WebSubUser(SimpleUser):
       @property
       def identity(self) -> str:
           return self.username
    
    return AuthCredentials([r"http[s]?://.*"])), WebSubUser("foobar")

NOTE - you must not have any Python code outside of the single function def statement!!

Arguments

The arguments for your api authenticator functions are all keyword arguments. An explanation of the arguments are below:

ArgumentTypeDescription
contextobjectThe Context object providing execution environment information and helper objects and methods.
requestfastapi.RequestThe Request object providing all attributes of the request as received.
kwargsdictThis is present specifically to future-proof your function. If, in the future, EchoStream Webhook Nodes pass additional arguments to your api authenticator function, those additional arguments will not break the call to your function.

Return

Your processor function must return one of the following types; None, tuple[starlette.authentication.AuthCredentials, starlette.authentication.BaseUser].

  • None: The WebSub Hub Node will accept subscription requests from anyone but will deny all of them.
  • tuple[starlette.authentication.AuthCredentials, starlette.authentication.BaseUser]:
    • AuthCredentials: The WebSub Hub Node uses the scopes of the AuthCredentials to authorize subscriptions to topics. It treats the scopes as regular expressions; it the topic URL matches any of the regular expressions in scopes, that subscription is accepted; otherwise, the subscription will be denied.
    • BaseUser: The WebSub Hub Node will only accept the request if the BaseUser instance returns True from is_authenticated. Otherwise it will return a 403 to the client. The instance of BaseUser that you return must implement the identity property, as this will be used to identify the subscriber in a subscription.

NOTE - if you return any type other than those specified, the Webhook Node will throw an exception and cease processing.

Exceptions

You may raise a fastapi.HTTPException from your api authenticator. The Webhook Node will reject the request and return a response to the client as dictated by the exception.

NOTE - raising HTTPException is a effective way to get granular in your response to the client.

Any other exception raised will result in a 500 return to the client.

Requirements

You can use any package available on PyPI in your Api Authenticator function.

Simply add the requirement to the requirements of your WebSub Hub Node and it will be included in your Node. Thisis done using pip requirement specifiers.

For example, to include the most popular PostgreSQL database adapter for Python (psycopyg), you would put the following requirement in your Node's requirements:

  • For the latest release:
psycopg2
  • Pinning the release:
psycopg2 == 2.9.3
  • Ensuring a baseline release
psycopg2 >= 2.9.2

Then in your Api Authenticator function, you can use psycopg2 by importing it, as follows:

def authenticator(*, context, request, **kwargs):
    
    # Make SURE that you import INSIDE of the function!!
    import psycopg2

    ...

Subscription Settings

WebSub Hub Nodes support several settings that are beyond the scope of the WebSub standard. These settings allow for further control over the security, duration and retries for subscriptions to the Hub.

NameDescriptionDefault
defaultLeaseSecondsThe lease duration to apply to subscription requests that do not specify hub.lease_seconds864000
deliveryRetriesThe number of times to attempt delivery to a subscriptionUnlimited
maxLeaseSecondsThe maximum lease duration for a subscription864000
signatureAlgorithmThe WebSub signature algorithm used by hub subscriptions when the subscription provides a hub.secretsha1
subscriptionSecurityThe security requirements the hub is enforcing on subscription requests; you may enforce HTTPS callbacks, hub.secret, or bothNone