Internet Engineering Task Force (IETF) N. Jenkins Request for Comments: 8620 Fastmail Category: Standards Track C. Newman ISSN: 2070-1721 Oracle July 2019 The JSON Meta Application Protocol (JMAP)Abstract
This document specifies a protocol for clients to efficiently query, fetch, and modify JSON-based data objects, with support for push notification of changes and fast resynchronisation and for out-of- band binary data upload/download. Status of This Memo This is an Internet Standards Track document. This document is a product of the Internet Engineering Task Force (IETF). It represents the consensus of the IETF community. It has received public review and has been approved for publication by the Internet Engineering Steering Group (IESG). Further information on Internet Standards is available in Section 2 of RFC 7841. Information about the current status of this document, any errata, and how to provide feedback on it may be obtained at https://www.rfc-editor.org/info/rfc8620. Copyright Notice Copyright (c) 2019 IETF Trust and the persons identified as the document authors. All rights reserved. This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Simplified BSD License.
Table of Contents
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 4 1.1. Notational Conventions . . . . . . . . . . . . . . . . . 4 1.2. The Id Data Type . . . . . . . . . . . . . . . . . . . . 6 1.3. The Int and UnsignedInt Data Types . . . . . . . . . . . 6 1.4. The Date and UTCDate Data Types . . . . . . . . . . . . . 7 1.5. JSON as the Data Encoding Format . . . . . . . . . . . . 7 1.6. Terminology . . . . . . . . . . . . . . . . . . . . . . . 7 1.6.1. User . . . . . . . . . . . . . . . . . . . . . . . . 7 1.6.2. Accounts . . . . . . . . . . . . . . . . . . . . . . 7 1.6.3. Data Types and Records . . . . . . . . . . . . . . . 8 1.7. The JMAP API Model . . . . . . . . . . . . . . . . . . . 8 1.8. Vendor-Specific Extensions . . . . . . . . . . . . . . . 9 2. The JMAP Session Resource . . . . . . . . . . . . . . . . . . 9 2.1. Example . . . . . . . . . . . . . . . . . . . . . . . . . 14 2.2. Service Autodiscovery . . . . . . . . . . . . . . . . . . 15 3. Structured Data Exchange . . . . . . . . . . . . . . . . . . 16 3.1. Making an API Request . . . . . . . . . . . . . . . . . . 16 3.2. The Invocation Data Type . . . . . . . . . . . . . . . . 16 3.3. The Request Object . . . . . . . . . . . . . . . . . . . 16 3.3.1. Example Request . . . . . . . . . . . . . . . . . . . 18 3.4. The Response Object . . . . . . . . . . . . . . . . . . . 18 3.4.1. Example Response . . . . . . . . . . . . . . . . . . 19 3.5. Omitting Arguments . . . . . . . . . . . . . . . . . . . 19 3.6. Errors . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.6.1. Request-Level Errors . . . . . . . . . . . . . . . . 20 3.6.2. Method-Level Errors . . . . . . . . . . . . . . . . . 21 3.7. References to Previous Method Results . . . . . . . . . . 22 3.8. Localisation of User-Visible Strings . . . . . . . . . . 27 3.9. Security . . . . . . . . . . . . . . . . . . . . . . . . 28 3.10. Concurrency . . . . . . . . . . . . . . . . . . . . . . . 28 4. The Core/echo Method . . . . . . . . . . . . . . . . . . . . 28 4.1. Example . . . . . . . . . . . . . . . . . . . . . . . . . 28 5. Standard Methods and Naming Convention . . . . . . . . . . . 29 5.1. /get . . . . . . . . . . . . . . . . . . . . . . . . . . 29 5.2. /changes . . . . . . . . . . . . . . . . . . . . . . . . 30 5.3. /set . . . . . . . . . . . . . . . . . . . . . . . . . . 34 5.4. /copy . . . . . . . . . . . . . . . . . . . . . . . . . . 40 5.5. /query . . . . . . . . . . . . . . . . . . . . . . . . . 42 5.6. /queryChanges . . . . . . . . . . . . . . . . . . . . . . 48 5.7. Examples . . . . . . . . . . . . . . . . . . . . . . . . 51 5.8. Proxy Considerations . . . . . . . . . . . . . . . . . . 58 6. Binary Data . . . . . . . . . . . . . . . . . . . . . . . . . 58 6.1. Uploading Binary Data . . . . . . . . . . . . . . . . . . 59 6.2. Downloading Binary Data . . . . . . . . . . . . . . . . . 60 6.3. Blob/copy . . . . . . . . . . . . . . . . . . . . . . . . 61
7. Push . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 7.1. The StateChange Object . . . . . . . . . . . . . . . . . 63 7.1.1. Example . . . . . . . . . . . . . . . . . . . . . . . 64 7.2. PushSubscription . . . . . . . . . . . . . . . . . . . . 64 7.2.1. PushSubscription/get . . . . . . . . . . . . . . . . 67 7.2.2. PushSubscription/set . . . . . . . . . . . . . . . . 68 7.2.3. Example . . . . . . . . . . . . . . . . . . . . . . . 69 7.3. Event Source . . . . . . . . . . . . . . . . . . . . . . 71 8. Security Considerations . . . . . . . . . . . . . . . . . . . 73 8.1. Transport Confidentiality . . . . . . . . . . . . . . . . 73 8.2. Authentication Scheme . . . . . . . . . . . . . . . . . . 73 8.3. Service Autodiscovery . . . . . . . . . . . . . . . . . . 73 8.4. JSON Parsing . . . . . . . . . . . . . . . . . . . . . . 74 8.5. Denial of Service . . . . . . . . . . . . . . . . . . . . 74 8.6. Connection to Unknown Push Server . . . . . . . . . . . . 74 8.7. Push Encryption . . . . . . . . . . . . . . . . . . . . . 75 8.8. Traffic Analysis . . . . . . . . . . . . . . . . . . . . 76 9. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 76 9.1. Assignment of jmap Service Name . . . . . . . . . . . . . 76 9.2. Registration of Well-Known URI Suffix for JMAP . . . . . 76 9.3. Registration of the jmap URN Sub-namespace . . . . . . . 77 9.4. Creation of "JMAP Capabilities" Registry . . . . . . . . 77 9.4.1. Preliminary Community Review . . . . . . . . . . . . 77 9.4.2. Submit Request to IANA . . . . . . . . . . . . . . . 78 9.4.3. Designated Expert Review . . . . . . . . . . . . . . 78 9.4.4. Change Procedures . . . . . . . . . . . . . . . . . . 78 9.4.5. JMAP Capabilities Registry Template . . . . . . . . . 79 9.4.6. Initial Registration for JMAP Core . . . . . . . . . 79 9.4.7. Registration for JMAP Error Placeholder in JMAP Capabilities Registry . . . . . . . . . . . . . . . . 80 9.5. Creation of "JMAP Error Codes" Registry . . . . . . . . . 80 9.5.1. Expert Review . . . . . . . . . . . . . . . . . . . . 80 9.5.2. JMAP Error Codes Registry Template . . . . . . . . . 81 9.5.3. Initial Contents for the JMAP Error Codes Registry . 81 10. References . . . . . . . . . . . . . . . . . . . . . . . . . 86 10.1. Normative References . . . . . . . . . . . . . . . . . . 86 10.2. Informative References . . . . . . . . . . . . . . . . . 89 Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 90
1. Introduction
The JSON Meta Application Protocol (JMAP) is used for synchronising data, such as mail, calendars, or contacts, between a client and a server. It is optimised for mobile and web environments and aims to provide a consistent interface to different data types. This specification is for the generic mechanism of data synchronisation. Further specifications define the data models for different data types that may be synchronised via JMAP. JMAP is designed to make efficient use of limited network resources. Multiple API calls may be batched in a single request to the server, reducing round trips and improving battery life on mobile devices. Push connections remove the need for polling, and an efficient delta update mechanism ensures a minimum amount of data is transferred. JMAP is designed to be horizontally scalable to a very large number of users. This is facilitated by separate endpoints for users after login, the separation of binary and structured data, and a data model for sharing that does not allow data dependencies between accounts.1.1. Notational Conventions
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here. The underlying format used for this specification is JSON. Consequently, the terms "object" and "array" as well as the four primitive types (strings, numbers, booleans, and null) are to be interpreted as described in Section 1 of [RFC8259]. Unless otherwise noted, all the property names and values are case sensitive. Some examples in this document contain "partial" JSON documents used for illustrative purposes. In these examples, three periods "..." are used to indicate a portion of the document that has been removed for compactness. For compatibility with publishing requirements, line breaks have been inserted inside long JSON strings, with the following continuation lines indented. To form the valid JSON example, any line breaks inside a string must be replaced with a space and any other white space after the line break removed.
Unless otherwise specified, examples of API exchanges only show the methodCalls array of the Request object or the methodResponses array of the Response object. For compactness, the rest of the Request/ Response object is omitted. Type signatures are given for all JSON values in this document. The following conventions are used: o "*" - The type is undefined (the value could be any type, although permitted values may be constrained by the context of this value). o "String" - The JSON string type. o "Number" - The JSON number type. o "Boolean" - The JSON boolean type. o "A[B]" - A JSON object where the keys are all of type "A", and the values are all of type "B". o "A[]" - An array of values of type "A". o "A|B" - The value is either of type "A" or of type "B". Other types may also be given, with their representation defined elsewhere in this document. Object properties may also have a set of attributes defined along with the type signature. These have the following meanings: o "server-set" -- Only the server can set the value for this property. The client MUST NOT send this property when creating a new object of this type. o "immutable" -- The value MUST NOT change after the object is created. o "default" -- (This is followed by a JSON value). The value that will be used for this property if it is omitted in an argument or when creating a new object of this type.
1.2. The Id Data Type
All record ids are assigned by the server and are immutable. Where "Id" is given as a data type, it means a "String" of at least 1 and a maximum of 255 octets in size, and it MUST only contain characters from the "URL and Filename Safe" base64 alphabet, as defined in Section 5 of [RFC4648], excluding the pad character ("="). This means the allowed characters are the ASCII alphanumeric characters ("A-Za-z0-9"), hyphen ("-"), and underscore ("_"). These characters are safe to use in almost any context (e.g., filesystems, URIs, and IMAP atoms). For maximum safety, servers SHOULD also follow defensive allocation strategies to avoid creating risks where glob completion or data type detection may be present (e.g., on filesystems or in spreadsheets). In particular, it is wise to avoid: o Ids starting with a dash o Ids starting with digits o Ids that contain only digits o Ids that differ only by ASCII case (for example, A vs. a) o the specific sequence of three characters "NIL" (because this sequence can be confused with the IMAP protocol expression of the null value) A good solution to these issues is to prefix every id with a single alphabetical character.1.3. The Int and UnsignedInt Data Types
Where "Int" is given as a data type, it means an integer in the range -2^53+1 <= value <= 2^53-1, the safe range for integers stored in a floating-point double, represented as a JSON "Number". Where "UnsignedInt" is given as a data type, it means an "Int" where the value MUST be in the range 0 <= value <= 2^53-1.
1.4. The Date and UTCDate Data Types
Where "Date" is given as a type, it means a string in "date-time" format [RFC3339]. To ensure a normalised form, the "time-secfrac" MUST always be omitted if zero, and any letters in the string (e.g., "T" and "Z") MUST be uppercase. For example, "2014-10-30T14:12:00+08:00". Where "UTCDate" is given as a type, it means a "Date" where the "time-offset" component MUST be "Z" (i.e., it must be in UTC time). For example, "2014-10-30T06:12:00Z".1.5. JSON as the Data Encoding Format
JSON is a text-based data interchange format as specified in [RFC8259]. The Internet JSON (I-JSON) format defined in [RFC7493] is a strict subset of this, adding restrictions to avoid potentially confusing scenarios (for example, it mandates that an object MUST NOT have two members with the same name). All data sent from the client to the server or from the server to the client (except binary file upload/download) MUST be valid I-JSON according to the RFC and is therefore case sensitive and encoded in UTF-8 [RFC3629].1.6. Terminology
1.6.1. User
A user is a person accessing data via JMAP. A user has a set of permissions determining the data that they can see.1.6.2. Accounts
An account is a collection of data. A single account may contain an arbitrary set of data types, for example, a collection of mail, contacts, and calendars. Most JMAP methods take a mandatory "accountId" argument that specifies on which account the operations are to take place. An account is not the same as a user, although it is common for a primary account to directly belong to the user. For example, you may have an account that contains data for a group or business, to which multiple users have access.
A single set of credentials may provide access to multiple accounts, for example, if another user is sharing their work calendar with the authenticated user or if there is a group mailbox for a support-desk inbox. In the event of a severe internal error, a server may have to reallocate ids or do something else that violates standard JMAP data constraints for an account. In this situation, the data on the server is no longer compatible with cached data the client may have from before. The server MUST treat this as though the account has been deleted and then recreated with a new account id. Clients will then be forced to throw away any data with the old account id and refetch all data from scratch.1.6.3. Data Types and Records
JMAP provides a uniform interface for creating, retrieving, updating, and deleting various types of objects. A "data type" is a collection of named, typed properties, just like the schema for a database table. Each instance of a data type is called a "record". The id of a record is immutable and assigned by the server. The id MUST be unique among all records of the *same type* within the *same account*. Ids may clash across accounts or for two records of different types within the same account.1.7. The JMAP API Model
JMAP uses HTTP [RFC7230] to expose API, push, upload, and download resources. All HTTP requests MUST use the "https://" scheme (HTTP over TLS [RFC2818]). All HTTP requests MUST be authenticated. An authenticated client can fetch the user's Session object with details about the data and capabilities the server can provide as shown in Section 2. The client may then exchange data with the server in the following ways: 1. The client may make an API request to the server to get or set structured data. This request consists of an ordered series of method calls. These are processed by the server, which then returns an ordered series of responses. This is described in Sections 3, 4, and 5. 2. The client may download or upload binary files from/to the server. This is detailed in Section 6. 3. The client may connect to a push channel on the server, to be notified when data has changed. This is explained in Section 7.
1.8. Vendor-Specific Extensions
Individual services will have custom features they wish to expose over JMAP. This may take the form of extra data types and/or methods not in the spec, extra arguments to JMAP methods, or extra properties on existing data types (which may also appear in arguments to methods that take property names). The server can advertise custom extensions it supports by including the identifiers in the capabilities object. Identifiers for vendor extensions MUST be a URL belonging to a domain owned by the vendor, to avoid conflict. The URL SHOULD resolve to documentation for the changes the extension makes. The client MUST opt in to use an extension by passing the appropriate capability identifier in the "using" array of the Request object, as described in Section 3.3. The server MUST only follow the specifications that are opted into and behave as though it does not implement anything else when processing a request. This is to ensure compatibility with clients that don't know about a specific custom extension and for compatibility with future versions of JMAP.2. The JMAP Session Resource
You need two things to connect to a JMAP server: 1. The URL for the JMAP Session resource. This may be requested directly from the user or discovered automatically based on a username domain (see Section 2.2 below). 2. Credentials to authenticate with. How to obtain credentials is out of scope for this document. A successful authenticated GET request to the JMAP Session resource MUST return a JSON-encoded *Session* object, giving details about the data and capabilities the server can provide to the client given those credentials. It has the following properties: o capabilities: "String[Object]" An object specifying the capabilities of this server. Each key is a URI for a capability supported by the server. The value for each of these keys is an object with further information about the server's capabilities in relation to that capability. The client MUST ignore any properties it does not understand.
The capabilities object MUST include a property called "urn:ietf:params:jmap:core". The value of this property is an object that MUST contain the following information on server capabilities (suggested minimum values for limits are supplied that allow clients to make efficient use of the network): * maxSizeUpload: "UnsignedInt" The maximum file size, in octets, that the server will accept for a single file upload (for any purpose). Suggested minimum: 50,000,000. * maxConcurrentUpload: "UnsignedInt" The maximum number of concurrent requests the server will accept to the upload endpoint. Suggested minimum: 4. * maxSizeRequest: "UnsignedInt" The maximum size, in octets, that the server will accept for a single request to the API endpoint. Suggested minimum: 10,000,000. * maxConcurrentRequests: "UnsignedInt" The maximum number of concurrent requests the server will accept to the API endpoint. Suggested minimum: 4. * maxCallsInRequest: "UnsignedInt" The maximum number of method calls the server will accept in a single request to the API endpoint. Suggested minimum: 16. * maxObjectsInGet: "UnsignedInt" The maximum number of objects that the client may request in a single /get type method call. Suggested minimum: 500. * maxObjectsInSet: "UnsignedInt" The maximum number of objects the client may send to create, update, or destroy in a single /set type method call. This is the combined total, e.g., if the maximum is 10, you could not create 7 objects and destroy 6, as this would be 13 actions, which exceeds the limit. Suggested minimum: 500.
* collationAlgorithms: "String[]" A list of identifiers for algorithms registered in the collation registry, as defined in [RFC4790], that the server supports for sorting when querying records. Specifications for future capabilities will define their own properties on the capabilities object. Servers MAY advertise vendor-specific JMAP extensions, as described in Section 1.8. To avoid conflict, an identifier for a vendor-specific extension MUST be a URL with a domain owned by the vendor. Clients MUST opt in to any capability it wishes to use (see Section 3.3). o accounts: "Id[Account]" A map of an account id to an Account object for each account (see Section 1.6.2) the user has access to. An *Account* object has the following properties: * name: "String" A user-friendly string to show when presenting content from this account, e.g., the email address representing the owner of the account. * isPersonal: "Boolean" This is true if the account belongs to the authenticated user rather than a group account or a personal account of another user that has been shared with them. * isReadOnly: "Boolean" This is true if the entire account is read-only. * accountCapabilities: "String[Object]" The set of capability URIs for the methods supported in this account. Each key is a URI for a capability that has methods you can use with this account. The value for each of these keys is an object with further information about the account's permissions and restrictions with respect to this capability, as defined in the capability's specification. The client MUST ignore any properties it does not understand.
The server advertises the full list of capabilities it supports in the capabilities object, as defined above. If the capability defines new methods, the server MUST include it in the accountCapabilities object if the user may use those methods with this account. It MUST NOT include it in the accountCapabilities object if the user cannot use those methods with this account. For example, you may have access to your own account with mail, calendars, and contacts data and also a shared account that only has contacts data (a business address book, for example). In this case, the accountCapabilities property on the first account would include something like "urn:ietf:params:jmap:mail", "urn:ietf:params:jmap:calendars", and "urn:ietf:params:jmap:contacts", while the second account would just have the last of these. Attempts to use the methods defined in a capability with one of the accounts that does not support that capability are rejected with an "accountNotSupportedByMethod" error (see "Method-Level Errors", Section 3.6.2). o primaryAccounts: "String[Id]" A map of capability URIs (as found in accountCapabilities) to the account id that is considered to be the user's main or default account for data pertaining to that capability. If no account being returned belongs to the user, or in any other way there is no appropriate way to determine a default account, there MAY be no entry for a particular URI, even though that capability is supported by the server (and in the capabilities object). "urn:ietf:params:jmap:core" SHOULD NOT be present. o username: "String" The username associated with the given credentials, or the empty string if none. o apiUrl: "String" The URL to use for JMAP API requests.
o downloadUrl: "String" The URL endpoint to use when downloading files, in URI Template (level 1) format [RFC6570]. The URL MUST contain variables called "accountId", "blobId", "type", and "name". The use of these variables is described in Section 6.2. Due to potential encoding issues with slashes in content types, it is RECOMMENDED to put the "type" variable in the query section of the URL. o uploadUrl: "String" The URL endpoint to use when uploading files, in URI Template (level 1) format [RFC6570]. The URL MUST contain a variable called "accountId". The use of this variable is described in Section 6.1. o eventSourceUrl: "String" The URL to connect to for push events, as described in Section 7.3, in URI Template (level 1) format [RFC6570]. The URL MUST contain variables called "types", "closeafter", and "ping". The use of these variables is described in Section 7.3. o state: "String" A (preferably short) string representing the state of this object on the server. If the value of any other property on the Session object changes, this string will change. The current value is also returned on the API Response object (see Section 3.4), allowing clients to quickly determine if the session information has changed (e.g., an account has been added or removed), so they need to refetch the object. To ensure future compatibility, other properties MAY be included on the Session object. Clients MUST ignore any properties they are not expecting. Implementors must take care to avoid inappropriate caching of the Session object at the HTTP layer. Since the client should only refetch when it detects there is a change (via the sessionState property of an API response), it is RECOMMENDED to disable HTTP caching altogether, for example, by setting "Cache-Control: no-cache, no-store, must-revalidate" on the response.
2.1. Example
In the following example Session object, the user has access to their own mail and contacts via JMAP, as well as read-only access to shared mail from another user. The server is advertising a custom "https://example.com/apis/foobar" capability. { "capabilities": { "urn:ietf:params:jmap:core": { "maxSizeUpload": 50000000, "maxConcurrentUpload": 8, "maxSizeRequest": 10000000, "maxConcurrentRequest": 8, "maxCallsInRequest": 32, "maxObjectsInGet": 256, "maxObjectsInSet": 128, "collationAlgorithms": [ "i;ascii-numeric", "i;ascii-casemap", "i;unicode-casemap" ] }, "urn:ietf:params:jmap:mail": {} "urn:ietf:params:jmap:contacts": {}, "https://example.com/apis/foobar": { "maxFoosFinangled": 42 } }, "accounts": { "A13824": { "name": "john@example.com", "isPersonal": true, "isReadOnly": false, "accountCapabilities": { "urn:ietf:params:jmap:mail": { "maxMailboxesPerEmail": null, "maxMailboxDepth": 10, ... }, "urn:ietf:params:jmap:contacts": { ... } } },
"A97813": { "name": "jane@example.com", "isPersonal": false, "isReadOnly": true, "accountCapabilities": { "urn:ietf:params:jmap:mail": { "maxMailboxesPerEmail": 1, "maxMailboxDepth": 10, ... } } } }, "primaryAccounts": { "urn:ietf:params:jmap:mail": "A13824", "urn:ietf:params:jmap:contacts": "A13824" }, "username": "john@example.com", "apiUrl": "https://jmap.example.com/api/", "downloadUrl": "https://jmap.example.com /download/{accountId}/{blobId}/{name}?accept={type}", "uploadUrl": "https://jmap.example.com/upload/{accountId}/", "eventSourceUrl": "https://jmap.example.com /eventsource/?types={types}&closeafter={closeafter}&ping={ping}", "state": "75128aab4b1b" }2.2. Service Autodiscovery
There are two standardised autodiscovery methods in use for Internet protocols: o DNS SRV (see [RFC2782], [RFC6186], and [RFC6764]) o .well-known/servicename (see [RFC8615]) A JMAP-supporting host for the domain "example.com" SHOULD publish a SRV record "_jmap._tcp.example.com" that gives a hostname and port (usually port "443"). The JMAP Session resource is then "https://${hostname}[:${port}]/.well-known/jmap" (following any redirects). If the client has a username in the form of an email address, it MAY use the domain portion of this to attempt autodiscovery of the JMAP server.