CloudEvents extension for Azure Web PubSub MQTT event handler with HTTP protocol

The Web PubSub service delivers client events to the upstream webhook using the CloudEvents HTTP protocol binding.

Data sent from the Web PubSub service to the server is always in CloudEvents binary format.

Webhook Validation

The webhook validation follows CloudEvents. Each webhook endpoint should accept HTTP OPTIONS requests containing WebHook-Request-Origin: xxx.webpubsub.azure.com in the header and reply to the request by including the WebHook-Allowed-Origin header. For example:

WebHook-Allowed-Origin: *

Or:

WebHook-Allowed-Origin: xxx.webpubsub.azure.com

Currently, WebHook-Request-Rate and WebHook-Request-Callback aren't supported.

Web PubSub CloudEvents Attribute Extension

This extension defines the attributes used by Web PubSub for every event it produces.

Name Type Description Example
userId string The authenticated user ID of the connection.
hub string The hub the connection belongs to.
connectionId string The client ID in the MQTT protocol.
eventName string The name of the event without a prefix.
subprotocol string It's always mqtt.
connectionState string Defines the state of the connection. You can use the same response header to reset the value of the state. Multiple connectionState headers aren't allowed. Encode the string value in base64 if it contains complex characters; for example, use base64(jsonString) to pass complex objects using this attribute.
signature string The signature for the upstream webhook to validate if the incoming request is from the expected origin. The service calculates the value using both primary and secondary access keys as the HMAC key: Hex_encoded(HMAC_SHA256(accessKey, connectionId)). The upstream should verify if the request is valid before processing it.
physicalConnectionId string A unique ID generated by the service for each physical connection. Its format may change, so you shouldn't parse it.
sessionId string A unique ID generated by the service for each session. It doesn't exist in the connect event. Its format may change, so you shouldn't parse it.

Events

There are two types of events: blocking events, where the service waits for the response of the event to continue, and unblocking events, where the service doesn't wait for the response of such event before processing the next message.

Blocking Events

Unblocking Events

Note

  • For a TypeSpec version of this specification, see TypeSpec. You may want to open the filein the TypeSpec Playground for a better reading experience and a generated Swagger UI.
  • For a Swagger version of this specification, see Swagger. You may want to open the file in the Swagger Editor for a better reading experience and a generated Swagger UI.

System connect Event

  • ce-type: azure.webpubsub.sys.connect
  • Content-Type: application/json

Request Format

Every time the services receive a CONNECT packet from clients, it sends a connect request to upstream.

POST /upstream HTTP/1.1
Host: xxxxxx
WebHook-Request-Origin: xxx.webpubsub.azure.com
Content-Type: application/json; charset=utf-8
Content-Length: nnnn
ce-specversion: 1.0
ce-type: azure.webpubsub.sys.connect
ce-source: /hubs/{hub}/client/{clientId}/{physicalConnectionId}
ce-id: {eventId}
ce-time: 2021-01-01T00:00:00Z
ce-signature: sha256={connection-id-hash-primary},sha256={connection-id-hash-secondary}
ce-connectionId: {clientId}
ce-hub: {hub}
ce-eventName: connect
ce-physicalConnectionId: {physicalConnectionId}

{
  "mqtt": {
    "protocolVersion": 4,
    "cleanStart": true,
    "username": null,
    "password": null,
    "userProperties": null
  },
  "claims": {
    "type1": ["value1"]
  },
  "query": {
    "queryKey1": ["queryValue1"]
  },
  "headers": {
    "Connection": ["Upgrade"]
  },
  "subprotocols": ["mqtt"],
  "clientCertificates": [
    {
      "thumbprint": "3ce9b08a37566915dec4d1662cd2102121a99868",
      "content": "{string content of PEM format certificate}"
    }
  ]
}
  • mqtt.protocolVersion: {MQTT protocol version of the client} An integer; possible values are 4 (MQTT 3.1.1) and 5 (MQTT 5.0).

  • mqtt.username: {Username field in the MQTT CONNECT packet} A UTF-8 encoded string. mqtt.username and mqtt.password can be used for upstream webhook authentication of the clients.

  • mqtt.password: {Password field in the MQTT CONNECT packet} Base64-encoded binary data. Combined with the username for client authentication.

  • mqtt.userProperties: {User properties field in the MQTT CONNECT packet} A list of string key-value pairs. It's additional diagnostic or other information provided by clients whose protocols support user properties. Currently, only MQTT 5.0 supports it.

Success Response Format

  • Header ce-connectionState: If this header exists, the connection's state will be updated to the value of the header. Note that only blocking events can update the connection state. The sample below uses a base64 encoded JSON string to store the complex state for the connection.

  • HTTP Status Code:

    • 204: Success, with no content.
    • 200: Success; the content SHOULD be in JSON format, with the following properties allowed:
HTTP/1.1 200 OK

{
  "groups": ["newGroup1"],
  "subProtocol": "mqtt",
  "userId": "userId1",
  "roles": ["webpubsub.sendToGroup", "webpubsub.joinLeaveGroup"],
  "mqtt": {
    "userProperties": [
      {
        "name": "name1",
        "value": "value1"
      }
    ]
  }
}
  • subprotocol: It should always be mqtt or null. When it's null, it defaults to mqtt.

  • userId: {authenticated user ID} As the service allows anonymous connections, it's the connect event's responsibility to tell the service the user ID of the client connection. The service will read the user ID from the response payload userId if it exists. This property takes effect only when a new session is created for the physical connection. If the physical connection is connected to an existing session, this property is ignored, and the user ID of the existing session is used.

  • groups: {groups to join} This property provides a convenient way to add the connection to one or multiple groups. In terms of MQTT, it assigns one or multiple subscriptions with default subscription options to the client, and the group names are topic filters. This property takes effect only when a new session is created for the physical connection. If the physical connection is connected to an existing session, this property is ignored.

  • roles: {roles the client has} This property provides a way for the upstream webhook to authorize the client. There are different roles to grant initial permissions for PubSub WebSocket clients. Details about the permissions are described in Client Permissions. This property takes effect only when a new session is created for the physical connection. If the physical connection is connected to an existing session, this property is ignored.

  • mqtt.userProperties: {user properties that will be sent to clients in the CONNACK packet} A list of string key-value pairs. They will be converted to user properties in the CONNACK and sent to clients whose protocols support user properties. Currently, only MQTT 5.0 supports user properties. The upstream webhook can use this property for additional diagnostics or other information.

Once the service receives a success response from upstream, it sends a successful CONNACK packet to the client.

Error Response Format

  • 4xx or 5xx: Error. The content-type should be application/json. Once the service receives an error response, it sends a failed CONNACK packet to the client accordingly.
HTTP/1.1 401 Unauthorized
{
  "mqtt": {
    "code": 138,  // The CONNACK return code / reason code
    "reason": "banned by server",  // The reason string
    "userProperties": [{ "name": "name1", "value": "value1" }]
  }
}
  • mqtt.code: {return code (MQTT 3.1.1) or reason code (MQTT 5.0) that will be sent to clients in the CONNACK packet} An integer indicating the failure reason. It will be sent to the clients in the CONNACK packet as a return code (MQTT 3.1.1) or reason code (MQTT 5.0). The upstream webhook should select a valid integer value defined by the MQTT protocols according to the protocol versions of the clients. If the upstream webhook sets an invalid value, clients will receive "unspecified error" in the CONNACK packet.

  • mqtt.reason: {failure reason string} A human-readable failure reason string designed for diagnostics. It will be sent to clients whose protocols support the reason string in the CONNACK packet. Currently, only MQTT 5.0 supports it.

  • mqtt.userProperties: {user properties that will be sent to clients in the CONNACK packet} A list of string key-value pairs. They will be converted to user properties in the CONNACK packet and sent to clients whose protocols support user properties. Currently, only MQTT 5.0 supports user properties. The upstream webhook can use this property for additional diagnostics or other information.

System connected Event

The service uses this event to notify the upstream that a new session is created. If a client connects to an existing session, there is no upstream call. In terms of MQTT, the service sends this event when it sends a Session Present flag 0 CONNACK packet to clients.

  • ce-type: azure.webpubsub.sys.connected
  • Content-Type: application/json

Request body is empty JSON.

Request Format

POST /upstream HTTP/1.1
Host: xxxxxx
WebHook-Request-Origin: xxx.webpubsub.azure.com
Content-Type: application/json; charset=utf-8
Content-Length: nnnn
ce-specversion: 1.0
ce-type: azure.webpubsub.sys.connected
ce-source: /hubs/{hub}/client/{clientId}/{physicalConnectionId}
ce-id: {eventId}
ce-time: 2021-01-01T00:00:00Z
ce-signature: sha256={connection-id-hash-primary},sha256={connection-id-hash-secondary}
ce-connectionId: {clientId}
ce-hub: {hub}
ce-eventName: connected
ce-physicalConnectionId: {physicalConnectionId}
ce-sessionId: {sessionId}

{}

Compared to the connect event, the connected event includes a new header ce-sessionId. It's a unique ID generated by the service for each session. For the rest of the event types, the session ID header is also included.

Response Format

  • 2xx: Success response.

The connected event is asynchronous. When the response status code isn't successful, the service logs an error.

HTTP/1.1 200 OK

System disconnected Event

The service uses this event to notify upstream that a session has expired or ended.

  • ce-type: azure.webpubsub.sys.disconnected
  • Content-Type: application/json

Request Format

POST /upstream HTTP/1.1
Host: xxxxxx
WebHook-Request-Origin: xxx.webpubsub.azure.com
Content-Type: application/json; charset=utf-8
Content-Length: nnnn
ce-specversion: 1.0
ce-type: azure.webpubsub.sys.disconnected
ce-source: /hubs/{hub}/client/{clientId}/{physicalConnectionId}
ce-id: {eventId}
ce-time: 2021-01-01T00:00:00Z
ce-signature: sha256={connection-id-hash-primary},sha256={connection-id-hash-secondary}
ce-connectionId: {clientId}
ce-hub: {hub}
ce-eventName: disconnected
ce-physicalConnectionId: {physicalConnectionId}
ce-sessionId: {sessionId}

{
  "reason":"",
  "mqtt":{
    "initiatedByClient": true,
    "disconnectPacket":{
      "code": 0,
      "userProperties": [{ "name": "name1", "value": "value1" }]
    }
  }
}
  • reason: {nullable string of the disconnect reason} A human-readable string describing why the client disconnected. It may be null if it's a normal disconnection. If multiple connections connect and disconnect in a session, this property stands for the reason of the last disconnection. The reason is provided by clients or the service, depending on who initiated the disconnection. For MQTT, the reason may come from the reason string in the DISCONNECT packet from MQTT 5.0 clients or from the service. MQTT 3.1.1 protocol doesn't have a reason string in the DISCONNECT packet, so this property for MQTT 3.1.1 clients may be null or provided by the service.

  • mqtt.initiatedByClient: A boolean flag indicating whether the client initiates the disconnection by sending a DISCONNECT packet. It's true when the client sends a DISCONNECT packet to end the connection; otherwise, it's false.

  • mqtt.disconnectPacket: {nullable object containing properties of the last delivered DISCONNECT packet} It's null when the connection is disconnected without the client or service sending a DISCONNECT packet, for example, due to an IO error or network failure. The upstream can use mqtt.initiatedByClient to determine who sent the DISCONNECT packet.

  • mqtt.disconnectPacket.code: {integer reason code in the DISCONNECT packet} For MQTT 3.1.1 clients, as there's no reason code in the DISCONNECT packet, this property defaults to 0. For MQTT 5.0, it's the reason code in the DISCONNECT packet sent from the client or service.

  • mqtt.disconnectPacket.userProperties: {user properties in the DISCONNECT packet} A list of string key-value pairs. Clients can use this property to send additional diagnostics or other information to the upstream. If the DISCONNECT packet is sent by the service, it's null.

Response Format

  • 2xx: Success response.

The disconnected event is asynchronous. When the response status code isn't successful, the service logs an error.

HTTP/1.1 200 OK

User {custom_event} Event

The service converts specific messages published by MQTT clients to HTTP requests to the upstream webhook and converts the responses from the upstream to messages and sends them to clients.

Trigger Conditions

  • An MQTT client publishes a message to a topic in the format $webpubsub/server/events/{eventName}. {eventName} cannot contain the / character.
  • The MQTT client has permission to publish to that topic.
  • If the client's protocol is MQTT 5.0, and the PUBLISH packet contains a content type field, the content type value should be a valid MIME type because it will be converted to the Content-Type header of an HTTP request.

Request Format

MQTT Request Packet

The following table shows the usage of fields in an MQTT request message.

MQTT Request Fields Usage
Topic Indicates the message is a request to upstream, and specifies the event name.
Payload Be the body of the HTTP request.
Content Type Be the content type header of the HTTP request.
Correlation Data Be the correlation data field in the response message, used by sender to identify which request the response message is for.
QoS Be the level of assurance for delivery for both request and response message.
User Properties Becomes HTTP headers prefixed with mqtt- in the HTTP requests. Provides additional information between clients and upstream webhook.

The following code block shows a sample MQTT PUBLISH packet in the JSON format.

{
  "topic": "$webpubsub/server/events/{eventName}",
  "payload": "{mqtt-request-payload}",
  "content-type": "{request/MIME}",
  "correlation-data": "{correlation-data}",
  "QoS": "{qos}",
  "user-properties": [
    {
      "name": "{request-property-1}",
      "value": "{request-property-value1}"
    }
  ]
}

The following code block shows the HTTP request converted from the MQTT PUBLISH packet.

HTTP Request
POST /upstream HTTP/1.1
Host: xxxxxx
WebHook-Request-Origin: xxx.webpubsub.azure.com
Content-Type: {request/MIME}
Content-Length: nnnn
ce-specversion: 1.0
ce-type: azure.webpubsub.user.{eventName}
ce-source: /hubs/{hub}/client/{clientId}/{physicalConnectionId}
ce-id: {eventId}
ce-time: 2021-01-01T00:00:00Z
ce-signature: sha256={connection-id-hash-primary},sha256={connection-id-hash-secondary}
ce-connectionId: {clientId}
ce-hub: {hub}
ce-eventName: {eventName}
ce-physicalConnectionId: {physicalConnectionId}
ce-sessionId: {sessionId}
mqtt-{request-property-1}: {request-property-value1}

{mqtt-request-payload}

Response format

The following table shows the usage of different fields in the HTTP response.

HTTP Response Field Usage
Content Type Be the content type field of response MQTT message.
Body Be the payload of response MQTT message.
Headers prefixed with mqtt- Become user properties in the response MQTT message. Provides additional information between clients and upstream webhook.
Status Code Indicates whether the request succeeds. If it's successful (2xx), the response topic is $webpubsub/server/events/{eventName}/succeeded, otherwise $webpubsub/server/events/{eventName}/failed. It also becomes a user property named azure-status-code in the response MQTT message.

The following code block shows a sample HTTP response.

HTTP Response
HTTP/1.1 200 OK
Host: xxxxxx
Content-Type: {response/MIME}
Content-Length: nnnn
ce-connectionState: eyJrZXkiOiJhIn0=
mqtt-response-property-1: response-property-value1

{mqtt-response-payload}

MQTT Response

The following code block shows a sample MQTT response message converted from the HTTP response.

{
  "topic": "$webpubsub/server/events/{eventName}/succeeded",
  "payload": "{mqtt-response-payload}",
  "content-type": "{response/MIME}",
  "correlation-data": "{correlation-data}",
  "QoS": "{qos}",
  "user-properties": [
    {
      "name": "{response-property-1}",
      "value": "{response-property-value1}"
    }
  ]
}

Next steps

Use these resources to start building your own application: