Skip to content

Commit

Permalink
spec: add an example of how to send private updates to specific users
Browse files Browse the repository at this point in the history
  • Loading branch information
dunglas committed Jul 7, 2020
1 parent 1b405e0 commit e2581eb
Showing 1 changed file with 71 additions and 26 deletions.
97 changes: 71 additions & 26 deletions spec/mercure.md
Expand Up @@ -2,12 +2,13 @@
title = "The Mercure Protocol"
area = "Internet"
workgroup = "Network Working Group"
submissiontype = "IETF"

[seriesInfo]
name = "Internet-Draft"
value = "draft-dunglas-mercure-06"
value = "draft-dunglas-mercure-07"
stream = "IETF"
status = "informational"
status = "standard"

[[author]]
initials="K."
Expand Down Expand Up @@ -90,11 +91,11 @@ The publisher **MAY** provide the following target attributes in the Link Header
* `last-event-id`: the identifier of the last event dispatched by the publisher at the time of
the generation of this resource. If provided, it **MUST** be passed to the hub through a query
parameter called `Last-Event-ID` and will be used to ensure that possible updates having been
made during between the resource generation time and the connection to the hub are not lost. See
(#reconciliation).
made between the resource generation by the server and the connection to the hub are not lost.
See (#reconciliation).

* `content-type`: the content type of the updates that will pushed by the hub. If omitted, the
subscriber **MUST** assume that the content type will be the same as that of the original
* `content-type`: the content type of the updates that will be pushed by the hub. If omitted,
the subscriber **MUST** assume that the content type will be the same as that of the original
resource. Setting the `content-type` attribute is especially useful to hint that partial updates
will be pushed, using formats such as JSON Patch [@RFC6902] or JSON Merge Patch [@RFC7386].

Expand Down Expand Up @@ -214,8 +215,9 @@ To determine if a string matches a selector, the following steps must be followe
The subscriber subscribes to a URL exposed by a hub to receive updates from one or many topics.
To subscribe to updates, the client opens an HTTPS connection following the Server-Sent Events
specification [@!W3C.REC-eventsource-20150203] to the hub's subscription URL advertised by the
publisher. The `GET` HTTP method must be used. The connection **SHOULD** use HTTP/2 to leverage
mutliplexing and other advanced features of this protocol.
publisher. The `GET` HTTP method must be used. The connection **SHOULD** use HTTP version 2 or
superior to leverage multiplexing and other performance-oriented related features provided by these
versions.

The subscriber specifies the list of topics to get updates from by using one or several query
parameters named `topic`. The `topic` query parameters **MUST** contain topic selectors. See
Expand Down Expand Up @@ -300,11 +302,11 @@ The request **MUST** be encoded using the `application/x-www-form-urlencoded` fo

* `id` (optional): the topic's revision identifier: it will be used as the SSE's `id` property.
The provided id **MUST NOT** start with the `#` character. The provided id **SHOULD** be a valid
IRI. If omitted, the hub **MUST** generate a valid IRI [@!RFC3987]. An UUID [@RFC4122] or a DID
(@W3C.WD-did-core-20200421) **MAY** be used. Alternatively the hub **MAY** generate a relative
URI composed of a fragment (starting with `#`). This is convenient to return an offset or a
sequence that is unique for this hub. Even if provided, the hub **MAY** ignore the id provided
by the client and generate its own id.
IRI. If omitted, the hub **MUST** generate a valid IRI [@!RFC3987]. An UUID [@RFC4122] or a
[DID](https://www.w3.org/TR/did-core/) **MAY** be used. Alternatively the hub **MAY** generate a
relative URI composed of a fragment (starting with `#`). This is convenient to return an offset
or a sequence that is unique for this hub. Even if provided, the hub **MAY** ignore the id
provided by the client and generate its own id.

* `type` (optional): the SSE's `event` property (a specific event type).

Expand All @@ -314,6 +316,22 @@ In the event of success, the HTTP response's body **MUST** be the `id` associate
generated by the hub and a success HTTP status code **MUST** be returned. The publisher **MUST** be
authorized to publish updates. See (#authorization).

Example:

~~~ http
POST /.well-known/mercure HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer [snip]
topic=https://example.com/foo&data=the%20content
HTTP/1.1 200 OK
Content-type: text/plain
urn:uuid:e1ee88e2-532a-4d6f-ba70-f0f8bd584022
~~~

# Authorization

To ensure that they are authorized, both publishers and subscribers must present a valid JWS
Expand Down Expand Up @@ -388,8 +406,35 @@ To receive updates marked as `private`, the JWS presented by the subscriber **MU
claim named `mercure` with a key named `subscribe` that contains an array of topic selectors. See
(#topic-selectors).

The hub **MUST** check that at least one topic of the update to dispatch matches at least one topic
selector provided in `mercure.subscribe`.
The hub **MUST** check that at least one topic of the update to dispatch (canonical or alternate)
matches at least one topic selector provided in `mercure.subscribe`.

Using alternate topics (see (#publication)) is convenient to allow a publisher to send private
updates to subscribers (or groups of subscribers) without having to include the list of all topics
that these subscribers will receive in their JWS.

For instance, a subscriber can present to the hub a JWS with a `mercure.subscribe` claim
containing `["https://example.com/users/foo/{?topic}"]` while subscribing to the topic
`https://example.com/a-random-topic`.

The publisher can take advantage of the previously described behavior by publishing a
private update having `https://example.com/books/a-random-topic` as canonical topic and
`https://example.com/users/foo/?topic=https%3A%2F%2Fexample.com%2Fa-random-topic` as alternate topic:

~~~ http
POST /.well-known/mercure HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer [snip]
topic=https://example.com/a-random-topic&topic=https://example.com/users/foo/?topic=https%3A%2F%2Fexample.com%2Fa-random-topic&private=on
~~~

The subscriber is subscribed to `https://example.com/books/a-random-topic`. This topic doesn't match
the topic selector provided in its JWS claim `mercure.subscribe`. However, the alternate topic of
the published update (`https://example.com/users/foo/?topic=https%3A%2F%2Fexample.com%2Fa-random-topic`)
matches the provided topic selector `https://example.com/users/foo/{?topic}`. Consequently, this
private update will be received by the subscriber.

## Payload

Expand All @@ -408,7 +453,7 @@ The protocol allows to reconciliate states after a reconnection. It can also be

To allow re-establishment in case of connection lost, events dispatched by the hub **MUST** include
an `id` property. The value contained in this `id` property **SHOULD** be an IRI [@!RFC3987]. An
UUID [@RFC4122] or a DID (@W3C.WD-did-core-20200421) **MAY** be used.
UUID [@RFC4122] or a [DID](https://www.w3.org/TR/did-core/) **MAY** be used.

According to the server-sent events specification, in case of connection
lost the subscriber will try to automatically re-connect. During the
Expand All @@ -417,7 +462,7 @@ re-connection, the subscriber **MUST** send the last received event id in a

In order to fetch any update dispatched between the initial resource generation by the publisher and
the connection to the hub, the subscriber **MUST** send the event id provided during the discovery
in the `last-event-id` as the last event id. See (#discovery).
as a `Last-Event-ID` header or query parameter. See (#discovery).

`EventSource` implementations may not allow to set HTTP headers during the first connection (before
a reconnection) and implementations in web browsers don't allow to set it.
Expand Down Expand Up @@ -447,7 +492,7 @@ partial updates in the JSON Patch [@RFC6902] format, or when using the hub as an
updates lost can cause data lost.

To detect if a data lost ocurred, the subscriber **CAN** compare the value of the `Last-Event-ID`
response HTTP header with `Last-Event-ID` it requested. In case of data lost, the subscriber
response HTTP header with the `Last-Event-ID` it requested. In case of data lost, the subscriber
**SHOULD** re-fetch the original topic.

Note: Native `EventSource` implementations don't give access to headers associated with the HTTP
Expand All @@ -466,9 +511,9 @@ Variables are templated and expanded in accordance with [@!RFC6570].

## Subscription Events

If the hub supports the active subscription feature, it **MUST** publish an update when a
If the hub supports the active subscriptions feature, it **MUST** publish an update when a
subscription is created or terminated. If this feature is implemented by the hub, an update **MUST**
be dispatched every time that a subscription is created or terminated.
be dispatched every time a subscription is created or terminated.

The topic of these updates **MUST** be an expansion of
`/.well-known/mercure/subscriptions/{topic}/{subscriber}`. `{topic}` is the topic selector used for
Expand All @@ -479,8 +524,8 @@ variables, values will usually contain the `:`, `/`, `{` and `}` characters. Per
characters are reserved. They **MUST** be percent encoded during the expansion process.

If a subscriber has several subscriptions, it **SHOULD** be identified by the same
identifier. `{subscriber}` **SHOULD** be an IRI [@!RFC3987]. An UUID [@RFC4122] or a DID
(@W3C.WD-did-core-20200421) **MAY** be used.
identifier. `{subscriber}` **SHOULD** be an IRI [@!RFC3987]. An UUID [@RFC4122] or a
[DID](https://www.w3.org/TR/did-core/) **MAY** be used.

The content of the update **MUST** be a JSON-LD [@!W3C.REC-json-ld-20140116] document containing at
least the following properties:
Expand Down Expand Up @@ -533,8 +578,8 @@ The web API **MUST** expose endpoints following these patterns:

* `/.well-known/mercure/subscriptions`: the collection of subscriptions

* `/.well-known/mercure/subscriptions/{topic}`: the collection subscriptions for the given topic
selector
* `/.well-known/mercure/subscriptions/{topic}`: the collection of subscriptions for the given
topic selector

* `/.well-known/mercure/subscriptions/{topic}/{subscriber}`: a specific subscription

Expand Down Expand Up @@ -711,8 +756,8 @@ the hub.
To make sure that the message content can not be read by the hub, the publisher **MAY** encrypt the
message before sending it to the hub. The publisher **SHOULD** use JSON Web Encryption [@!RFC7516]
to encrypt the update content. The publisher **MAY** provide the URL pointing to the relevant
encryption key(s) in the `key-set` attribute of the Link HTTP header during the discovery. See
(#discovery). The `key-set` attribute **MUST** contain a key encoded using the JSON Web Key Set
encryption key(s) in the `key-set` attribute of the `Link` HTTP header during the discovery. See
(#discovery). The `key-set` attribute **MUST** link to a key encoded using the JSON Web Key Set
[@!RFC7517] format. Any other out-of-band mechanism **MAY** be used instead to share the key between
the publisher and the subscriber.

Expand Down

0 comments on commit e2581eb

Please sign in to comment.