1
2
3
4
5Internet Engineering Task Force (IETF) K. Murchison
6Request for Comments: 8887 Fastmail
7Category: Standards Track August 2020
8ISSN: 2070-1721
9
10
11 A JSON Meta Application Protocol (JMAP) Subprotocol for WebSocket
12
13Abstract
14
15 This document defines a binding for the JSON Meta Application
16 Protocol (JMAP) over a WebSocket transport layer. The WebSocket
17 binding for JMAP provides higher performance than the current HTTP
18 binding for JMAP.
19
20Status of This Memo
21
22 This is an Internet Standards Track document.
23
24 This document is a product of the Internet Engineering Task Force
25 (IETF). It represents the consensus of the IETF community. It has
26 received public review and has been approved for publication by the
27 Internet Engineering Steering Group (IESG). Further information on
28 Internet Standards is available in Section 2 of RFC 7841.
29
30 Information about the current status of this document, any errata,
31 and how to provide feedback on it may be obtained at
32 https://www.rfc-editor.org/info/rfc8887.
33
34Copyright Notice
35
36 Copyright (c) 2020 IETF Trust and the persons identified as the
37 document authors. All rights reserved.
38
39 This document is subject to BCP 78 and the IETF Trust's Legal
40 Provisions Relating to IETF Documents
41 (https://trustee.ietf.org/license-info) in effect on the date of
42 publication of this document. Please review these documents
43 carefully, as they describe your rights and restrictions with respect
44 to this document. Code Components extracted from this document must
45 include Simplified BSD License text as described in Section 4.e of
46 the Trust Legal Provisions and are provided without warranty as
47 described in the Simplified BSD License.
48
49Table of Contents
50
51 1. Introduction
52 2. Conventions Used in This Document
53 3. Discovering Support for JMAP over WebSocket
54 4. JMAP Subprotocol
55 4.1. Authentication
56 4.2. Handshake
57 4.3. WebSocket Messages
58 4.3.1. Handling Invalid Data
59 4.3.2. JMAP Requests
60 4.3.3. JMAP Responses
61 4.3.4. JMAP Request-Level Errors
62 4.3.5. JMAP Push Notifications
63 4.4. Examples
64 5. Security Considerations
65 5.1. Connection Confidentiality and Integrity
66 5.2. Non-browser Clients
67 6. IANA Considerations
68 6.1. Registration of the WebSocket JMAP Subprotocol
69 7. References
70 7.1. Normative References
71 7.2. Informative References
72 Acknowledgments
73 Author's Address
74
751. Introduction
76
77 JMAP [RFC8620] over HTTP [RFC7235] requires that every JMAP API
78 request be authenticated. Depending on the type of authentication
79 used by the JMAP client and the configuration of the JMAP server,
80 authentication could be an expensive operation both in time and
81 resources. In such circumstances, reauthenticating for every JMAP
82 API request may harm performance.
83
84 The WebSocket [RFC6455] binding for JMAP eliminates this performance
85 hit by authenticating just the WebSocket handshake request and having
86 those credentials remain in effect for the duration of the WebSocket
87 connection. This binding supports JMAP API requests and responses,
88 with optional support for push notifications.
89
90 Furthermore, the WebSocket binding for JMAP can optionally compress
91 [RFC7692] both JMAP API requests and responses. Although compression
92 of HTTP responses is ubiquitous, compression of HTTP requests has
93 very low, if any, deployment and therefore isn't a viable option for
94 JMAP API requests over HTTP.
95
962. Conventions Used in This Document
97
98 The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
99 "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
100 "OPTIONAL" in this document are to be interpreted as described in
101 BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all
102 capitals, as shown here.
103
104 This document uses the terminology defined in the core JMAP
105 specification [RFC8620].
106
1073. Discovering Support for JMAP over WebSocket
108
109 The JMAP capabilities object is returned as part of the standard JMAP
110 Session object (see Section 2 of [RFC8620]). Servers supporting this
111 specification MUST add a property named
112 "urn:ietf:params:jmap:websocket" to the capabilities object. The
113 value of this property is an object that MUST contain the following
114 information on server capabilities:
115
116 * url: "String"
117
118 The wss-URI (see Section 3 of [RFC6455]) to use for initiating a
119 JMAP-over-WebSocket handshake (the "WebSocket URL endpoint"
120 colloquially).
121
122 * supportsPush: "Boolean"
123
124 This is true if the server supports push notifications over the
125 WebSocket, as described in Section 4.3.5.
126
127 Example:
128
129 "urn:ietf:params:jmap:websocket": {
130 "url": "wss://server.example.com/jmap/ws/",
131 "supportsPush": true
132 }
133
1344. JMAP Subprotocol
135
136 The term WebSocket subprotocol refers to an application-level
137 protocol layered on top of a WebSocket connection. This document
138 specifies the WebSocket JMAP subprotocol for carrying JMAP API
139 requests, responses, and optional push notifications through a
140 WebSocket connection. Binary data is handled per Section 6 of
141 [RFC8620] (via a separate HTTP connection or stream) or per a future
142 extension to JMAP or this specification.
143
1444.1. Authentication
145
146 A JMAP WebSocket connection is authenticated by presenting a user's
147 credentials in the HTTP request [RFC7235] that initiates the
148 WebSocket handshake. See Section 8.2 of [RFC8620] for
149 recommendations regarding the selection of HTTP authentication
150 schemes.
151
1524.2. Handshake
153
154 The JMAP WebSocket client and JMAP WebSocket server negotiate the use
155 of the WebSocket JMAP subprotocol during the WebSocket handshake,
156 either via an HTTP/1.1 Upgrade request (see Section 4 of [RFC6455])
157 or an HTTP/2 Extended CONNECT request (see Section 5 of [RFC8441]).
158 The WebSocket JMAP subprotocol is also intended to run over future
159 bindings of HTTP (e.g., HTTP/3) provided that there is a defined
160 mechanism for performing a WebSocket handshake over that binding.
161
162 Regardless of the method used for the WebSocket handshake, the client
163 MUST first perform a TLS handshake on a JMAP WebSocket URL endpoint
164 (Section 3) having the "wss://" scheme (WebSocket over TLS) in
165 accordance with the requirements of running the particular binding of
166 HTTP over TLS (see [RFC2818] and Section 4.1 of [RFC6455] for
167 HTTP/1.1 and Section 9.2 of [RFC7540] for HTTP/2). If the TLS
168 handshake fails, the client MUST close the connection. Otherwise,
169 the client MUST make an authenticated HTTP request [RFC7235] on the
170 encrypted connection and MUST include the value "jmap" in the list of
171 protocols for the "Sec-WebSocket-Protocol" header field.
172
173 The reply from the server MUST also contain a corresponding "Sec-
174 WebSocket-Protocol" header field with a value of "jmap" in order for
175 a JMAP subprotocol connection to be established.
176
177 Once the handshake has successfully completed, the WebSocket
178 connection is established and can be used for JMAP API requests,
179 responses, and optional push notifications. Other message types MUST
180 NOT be transmitted over this connection.
181
182 The credentials used for authenticating the HTTP request to initiate
183 the handshake remain in effect for the duration of the WebSocket
184 connection. If the authentication credentials for the user expire,
185 the server can either treat subsequent requests as if they are
186 unauthenticated or close the WebSocket connection. In the latter
187 case, the server MAY send a Close frame with a status code of 1008
188 (Policy Violation), as defined in Section 7.4.1 of [RFC6455].
189
1904.3. WebSocket Messages
191
192 Data frame messages in the JMAP subprotocol MUST be text frames and
193 contain UTF-8 encoded data. The messages MUST be in the form of a
194 single JMAP Request object (see Section 3.3 of [RFC8620]), JMAP
195 WebSocketPushEnable object (see Section 4.3.5.2), or JMAP
196 WebSocketPushDisable object (see Section 4.3.5.3) when sent from the
197 client to the server, and MUST be in the form of a single JMAP
198 Response object, JSON Problem Details object, or JMAP StateChange
199 object (see Sections 3.4, 3.6.1, and 7.1 of [RFC8620], respectively)
200 when sent from the server to the client.
201
202 Note that fragmented WebSocket messages (split over multiple text
203 frames) MUST be coalesced prior to parsing them as JSON objects.
204
2054.3.1. Handling Invalid Data
206
207 If a client or server receives a binary frame, the endpoint can
208 either ignore the frame or close the WebSocket connection. In the
209 latter case, the endpoint MAY send a Close frame with a status code
210 of 1003 (Unsupported Data), as defined in Section 7.4.1 of [RFC6455].
211
212 If a client receives a message that is not in the form of a JSON
213 Problem Details object, a JMAP Response object, or a JMAP StateChange
214 object, the client can either ignore the message or close the
215 WebSocket connection. In the latter case, the endpoint MAY send a
216 Close frame with a status code of 1007 (Invalid frame payload data),
217 as defined in Section 7.4.1 of [RFC6455].
218
219 A server MUST return an appropriate JSON Problem Details object
220 (Section 4.3.4) for any request-level errors (e.g., an invalid JMAP
221 object, an unsupported capability or method call, or exceeding a
222 server request limit).
223
2244.3.2. JMAP Requests
225
226 The specification extends the Request object with two additional
227 arguments when used over a WebSocket:
228
229 * @type: "String"
230
231 This MUST be the string "Request".
232
233 * id: "String" (optional)
234
235 A client-specified identifier for the request to be echoed back in
236 the response to this request.
237
238 JMAP over WebSocket allows the server to process requests out of
239 order. The client-specified identifier is used as a mechanism for
240 the client to correlate requests and responses.
241
242 Additionally, the "maxConcurrentRequests" limit in the "capabilities"
243 object (see Section 2 of [RFC8620]) also applies to requests made on
244 the WebSocket connection. When using the WebSocket JMAP subprotocol
245 over a binding of HTTP that allows multiplexing of requests (e.g.,
246 HTTP/2), this limit applies to the sum of requests made on both the
247 JMAP API endpoint and the WebSocket connection.
248
2494.3.3. JMAP Responses
250
251 The specification extends the Response object with two additional
252 arguments when used over a WebSocket:
253
254 * @type: "String"
255
256 This MUST be the string "Response".
257
258 * requestId: "String" (optional; MUST be returned if an identifier
259 is included in the request)
260
261 The client-specified identifier in the corresponding request.
262
2634.3.4. JMAP Request-Level Errors
264
265 The specification extends the Problem Details object for request-
266 level errors (see Section 3.6.1 of [RFC8620]) with two additional
267 arguments when used over a WebSocket:
268
269 * @type: "String"
270
271 This MUST be the string "RequestError".
272
273 * requestId: "String" (optional; MUST be returned if given in the
274 request)
275
276 The client-specified identifier in the corresponding request.
277
2784.3.5. JMAP Push Notifications
279
280 JMAP-over-WebSocket servers that support push notifications on the
281 WebSocket will advertise a "supportsPush" property with a value of
282 true in the "urn:ietf:params:jmap:websocket" server capabilities
283 object.
284
2854.3.5.1. Notification Format
286
287 All push notifications take the form of a standard StateChange object
288 (see Section 7.1 of [RFC8620]).
289
290 The specification extends the StateChange object with one additional
291 argument when used over a WebSocket:
292
293 * pushState: "String" (optional)
294
295 A (preferably short) string that encodes the entire server state
296 visible to the user (not just the objects returned in this call).
297
298 The purpose of the "pushState" token is to allow a client to
299 immediately get any changes that occurred while it was
300 disconnected (see Section 4.3.5.2). If the server does not
301 support "pushState" tokens, the client will have to issue a series
302 of "/changes" requests (see Section 5.2 of [RFC8620]) upon
303 reconnection to update its state to match that of the server.
304
3054.3.5.2. Enabling Notifications
306
307 A client enables push notifications from the server for the current
308 connection by sending a WebSocketPushEnable object to the server. A
309 WebSocketPushEnable object has the following properties:
310
311 * @type: "String"
312
313 This MUST be the string "WebSocketPushEnable".
314
315 * dataTypes: "String[]|null"
316
317 A list of data type names (e.g., "Mailbox" or "Email") that the
318 client is interested in. A StateChange notification will only be
319 sent if the data for one of these types changes. Other types are
320 omitted from the TypeState object. If null, changes will be
321 pushed for all supported data types.
322
323 * pushState: "String" (optional)
324
325 The last "pushState" token that the client received from the
326 server. Upon receipt of a "pushState" token, the server SHOULD
327 immediately send all changes since that state token.
328
3294.3.5.3. Disabling Notifications
330
331 A client disables push notifications from the server for the current
332 connection by sending a WebSocketPushDisable object to the server. A
333 WebSocketPushDisable object has the following property:
334
335 * @type: "String"
336
337 This MUST be the string "WebSocketPushDisable".
338
3394.4. Examples
340
341 The following examples show WebSocket JMAP opening handshakes, a JMAP
342 Core/echo request and response, and a subsequent closing handshake.
343 The examples assume that the JMAP WebSocket URL endpoint has been
344 advertised in the JMAP Session object as having a path of "/jmap/ws/"
345 and that TLS negotiation has already succeeded. Note that folding of
346 header fields is for editorial purposes only.
347
348 WebSocket JMAP connection via HTTP/1.1 with push notifications for
349 mail [RFC8621] is enabled. This example assumes that the client has
350 cached pushState "aaa" from a previous connection.
351
352 [[ From Client ]] [[ From Server ]]
353
354 GET /jmap/ws/ HTTP/1.1
355 Host: server.example.com
356 Upgrade: websocket
357 Connection: Upgrade
358 Authorization: Basic Zm9vOmJhcg==
359 Sec-WebSocket-Key:
360 dGhlIHNhbXBsZSBub25jZQ==
361 Sec-WebSocket-Protocol: jmap
362 Sec-WebSocket-Version: 13
363 Origin: https://www.example.com
364
365 HTTP/1.1 101 Switching Protocols
366 Upgrade: websocket
367 Connection: Upgrade
368 Sec-WebSocket-Accept:
369 s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
370 Sec-WebSocket-Protocol: jmap
371
372 [WebSocket connection established]
373
374 WS_DATA
375 {
376 "@type": "WebSocketPushEnable",
377 "dataTypes": [ "Mailbox", "Email" ],
378 "pushState": "aaa"
379 }
380
381 WS_DATA
382 {
383 "@type": "StateChange",
384 "changed": {
385 "a456": {
386 "Mailbox": "d35ecb040aab"
387 }
388 },
389 "pushState": "bbb"
390 }
391
392 WS_DATA
393 {
394 "@type": "Request",
395 "id": "R1",
396 "using": [ "urn:ietf:params:jmap:core" ],
397 "methodCalls": [
398 [
399 "Core/echo", {
400 "hello": true,
401 "high": 5
402 },
403 "b3ff"
404 ]
405 ]
406 }
407
408 WS_DATA
409 {
410 "@type": "Response",
411 "requestId": "R1",
412 "methodResponses": [
413 [
414 "Core/echo", {
415 "hello": true,
416 "high": 5
417 },
418 "b3ff"
419 ]
420 ]
421 }
422
423 WS_DATA
424 The quick brown fox jumps
425 over the lazy dog.
426
427 WS_DATA
428 {
429 "@type": "RequestError",
430 "requestId": null,
431 "type":
432 "urn:ietf:params:jmap:error:notJSON",
433 "status": 400,
434 "detail":
435 "The request did not parse as I-JSON."
436 }
437
438 [A new email is received]
439
440 WS_DATA
441 {
442 "@type": "StateChange",
443 "changed": {
444 "a123": {
445 "Email": "0af7a512ce70"
446 }
447 }
448 "pushState": "ccc"
449 }
450
451 WS_CLOSE
452
453 WS_CLOSE
454
455 [WebSocket connection closed]
456
457 WebSocket JMAP connection on an HTTP/2 stream that also negotiates
458 compression [RFC7692]:
459
460 [[ From Client ]] [[ From Server ]]
461
462 SETTINGS
463 SETTINGS_ENABLE_CONNECT_PROTOCOL = 1
464
465 HEADERS + END_HEADERS
466 :method = CONNECT
467 :protocol = websocket
468 :scheme = https
469 :path = /jmap/ws/
470 :authority = server.example.com
471 origin: https://example.com
472 authorization = Basic Zm9vOmJhcg==
473 sec-websocket-protocol = jmap
474 sec-websocket-version = 13
475 sec-websocket-extensions =
476 permessage-deflate
477 origin = https://www.example.com
478
479 HEADERS + END_HEADERS
480 :status = 200
481 sec-websocket-protocol = jmap
482 sec-websocket-extensions =
483 permessage-deflate
484
485 [WebSocket connection established]
486
487 DATA
488 WS_DATA
489 [compressed text]
490
491 DATA
492 WS_DATA
493 [compressed text]
494
495 ...
496
497 DATA + END_STREAM
498 WS_CLOSE
499
500 DATA + END_STREAM
501 WS_CLOSE
502
503 [WebSocket connection closed]
504 [HTTP/2 stream closed]
505
5065. Security Considerations
507
508 The security considerations for both WebSocket (see Section 10 of
509 [RFC6455]) and JMAP (see Section 8 of [RFC8620]) apply to the
510 WebSocket JMAP subprotocol. Specific security considerations are
511 described below.
512
5135.1. Connection Confidentiality and Integrity
514
515 To ensure the confidentiality and integrity of data sent and received
516 via JMAP over WebSocket, the WebSocket connection MUST use TLS 1.2
517 [RFC5246] or later, following the recommendations in BCP 195
518 [RFC7525]. Servers SHOULD support TLS 1.3 [RFC8446] or later.
519
5205.2. Non-browser Clients
521
522 JMAP over WebSocket can be used by clients both running inside and
523 outside of a web browser. As such, the security considerations in
524 Sections 10.2 and 10.1 of [RFC6455] apply to those respective
525 environments.
526
5276. IANA Considerations
528
5296.1. Registration of the WebSocket JMAP Subprotocol
530
531 Per this specification, IANA has registered the following in the
532 "WebSocket Subprotocol Name Registry" within the "WebSocket Protocol
533 Registries".
534
535 Subprotocol Identifier: jmap
536
537 Subprotocol Common Name: WebSocket Transport for JMAP (JSON Meta
538 Application Protocol)
539
540 Subprotocol Definition: RFC 8887
541
5427. References
543
5447.1. Normative References
545
546 [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
547 Requirement Levels", BCP 14, RFC 2119,
548 DOI 10.17487/RFC2119, March 1997,
549 <https://www.rfc-editor.org/info/rfc2119>.
550
551 [RFC2818] Rescorla, E., "HTTP Over TLS", RFC 2818,
552 DOI 10.17487/RFC2818, May 2000,
553 <https://www.rfc-editor.org/info/rfc2818>.
554
555 [RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security
556 (TLS) Protocol Version 1.2", RFC 5246,
557 DOI 10.17487/RFC5246, August 2008,
558 <https://www.rfc-editor.org/info/rfc5246>.
559
560 [RFC6455] Fette, I. and A. Melnikov, "The WebSocket Protocol",
561 RFC 6455, DOI 10.17487/RFC6455, December 2011,
562 <https://www.rfc-editor.org/info/rfc6455>.
563
564 [RFC7235] Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer
565 Protocol (HTTP/1.1): Authentication", RFC 7235,
566 DOI 10.17487/RFC7235, June 2014,
567 <https://www.rfc-editor.org/info/rfc7235>.
568
569 [RFC7525] Sheffer, Y., Holz, R., and P. Saint-Andre,
570 "Recommendations for Secure Use of Transport Layer
571 Security (TLS) and Datagram Transport Layer Security
572 (DTLS)", BCP 195, RFC 7525, DOI 10.17487/RFC7525, May
573 2015, <https://www.rfc-editor.org/info/rfc7525>.
574
575 [RFC7540] Belshe, M., Peon, R., and M. Thomson, Ed., "Hypertext
576 Transfer Protocol Version 2 (HTTP/2)", RFC 7540,
577 DOI 10.17487/RFC7540, May 2015,
578 <https://www.rfc-editor.org/info/rfc7540>.
579
580 [RFC7692] Yoshino, T., "Compression Extensions for WebSocket",
581 RFC 7692, DOI 10.17487/RFC7692, December 2015,
582 <https://www.rfc-editor.org/info/rfc7692>.
583
584 [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
585 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
586 May 2017, <https://www.rfc-editor.org/info/rfc8174>.
587
588 [RFC8441] McManus, P., "Bootstrapping WebSockets with HTTP/2",
589 RFC 8441, DOI 10.17487/RFC8441, September 2018,
590 <https://www.rfc-editor.org/info/rfc8441>.
591
592 [RFC8446] Rescorla, E., "The Transport Layer Security (TLS) Protocol
593 Version 1.3", RFC 8446, DOI 10.17487/RFC8446, August 2018,
594 <https://www.rfc-editor.org/info/rfc8446>.
595
596 [RFC8620] Jenkins, N. and C. Newman, "The JSON Meta Application
597 Protocol (JMAP)", RFC 8620, DOI 10.17487/RFC8620, July
598 2019, <https://www.rfc-editor.org/info/rfc8620>.
599
6007.2. Informative References
601
602 [RFC8621] Jenkins, N. and C. Newman, "The JSON Meta Application
603 Protocol (JMAP) for Mail", RFC 8621, DOI 10.17487/RFC8621,
604 August 2019, <https://www.rfc-editor.org/info/rfc8621>.
605
606Acknowledgments
607
608 The author would like to thank the following individuals for
609 contributing their ideas and support for writing this specification:
610 Neil Jenkins, Robert Mueller, and Chris Newman.
611
612Author's Address
613
614 Kenneth Murchison
615 Fastmail US LLC
616 1429 Walnut Street, Suite 1201
617 Philadelphia, PA 19102
618 United States of America
619
620 Email: murch@fastmailteam.com
621 URI: http://www.fastmail.com/
622