Internet DRAFT - draft-ietf-jmap-contacts
draft-ietf-jmap-contacts
JMAP N.M. Jenkins, Ed.
Internet-Draft Fastmail
Intended status: Standards Track 20 February 2024
Expires: 23 August 2024
JMAP for Contacts
draft-ietf-jmap-contacts-04
Abstract
This document specifies a data model for synchronising contacts data
with a server using JMAP.
Status of This Memo
This Internet-Draft is submitted in full conformance with the
provisions of BCP 78 and BCP 79.
Internet-Drafts are working documents of the Internet Engineering
Task Force (IETF). Note that other groups may also distribute
working documents as Internet-Drafts. The list of current Internet-
Drafts is at https://datatracker.ietf.org/drafts/current/.
Internet-Drafts are draft documents valid for a maximum of six months
and may be updated, replaced, or obsoleted by other documents at any
time. It is inappropriate to use Internet-Drafts as reference
material or to cite them other than as "work in progress."
This Internet-Draft will expire on 23 August 2024.
Copyright Notice
Copyright (c) 2024 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 Revised BSD License text as
described in Section 4.e of the Trust Legal Provisions and are
provided without warranty as described in the Revised BSD License.
Table of Contents
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 2
Jenkins Expires 23 August 2024 [Page 1]
Internet-Draft JMAP Contacts February 2024
1.1. Notational conventions . . . . . . . . . . . . . . . . . 3
1.2. Terminology . . . . . . . . . . . . . . . . . . . . . . . 3
1.3. Data Model Overview . . . . . . . . . . . . . . . . . . . 3
1.4. Addition to the Capabilities Object . . . . . . . . . . . 3
1.4.1. urn:ietf:params:jmap:contacts . . . . . . . . . . . . 3
2. AddressBooks . . . . . . . . . . . . . . . . . . . . . . . . 4
2.1. AddressBook/get . . . . . . . . . . . . . . . . . . . . . 6
2.2. AddressBook/changes . . . . . . . . . . . . . . . . . . . 6
2.3. AddressBook/set . . . . . . . . . . . . . . . . . . . . . 6
3. ContactCards . . . . . . . . . . . . . . . . . . . . . . . . 7
3.1. ContactCard/get . . . . . . . . . . . . . . . . . . . . . 8
3.2. ContactCard/changes . . . . . . . . . . . . . . . . . . . 8
3.3. ContactCard/query . . . . . . . . . . . . . . . . . . . . 8
3.3.1. Filtering . . . . . . . . . . . . . . . . . . . . . . 8
3.3.2. Sorting . . . . . . . . . . . . . . . . . . . . . . . 10
3.4. ContactCard/queryChanges . . . . . . . . . . . . . . . . 11
3.5. ContactCard/set . . . . . . . . . . . . . . . . . . . . . 11
3.6. ContactCard/copy . . . . . . . . . . . . . . . . . . . . 11
4. Examples . . . . . . . . . . . . . . . . . . . . . . . . . . 11
4.1. Fetching initial data . . . . . . . . . . . . . . . . . . 11
4.2. Changing the default address book . . . . . . . . . . . . 13
5. Security considerations . . . . . . . . . . . . . . . . . . . 14
5.1. Privacy . . . . . . . . . . . . . . . . . . . . . . . . . 14
6. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 14
6.1. JMAP capability registration for "contacts" . . . . . . . 14
6.2. JSContact Property Registrations . . . . . . . . . . . . 14
6.2.1. id . . . . . . . . . . . . . . . . . . . . . . . . . 14
6.2.2. addressBookIds . . . . . . . . . . . . . . . . . . . 15
6.2.3. blobId . . . . . . . . . . . . . . . . . . . . . . . 15
7. Normative References . . . . . . . . . . . . . . . . . . . . 15
Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 16
1. Introduction
JMAP ([RFC8620] JSON Meta Application Protocol) is a generic protocol
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 defines a data model for synchronising contacts
between a client and a server using JMAP.
Jenkins Expires 23 August 2024 [Page 2]
Internet-Draft JMAP Contacts February 2024
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.
Type signatures, examples and property descriptions in this document
follow the conventions established in Section 1.1 of [RFC8620]. The
Id and UnsignedInt data types defined in Sections 1.2 and 1.3 of
[RFC8620] are also used in this document.
1.2. Terminology
The same terminology is used in this document as in the core JMAP
specification, see [RFC8620], Section 1.6.
The terms AddressBook and ContactCard (with these specific
capitalizations) are used to refer to the data types defined in this
document and instances of those data types.
1.3. Data Model Overview
An Account (see [RFC8620], Section 1.6.2) with support for the
contacts data model contains zero or more AddressBook objects, which
is a named collection of zero or more ContactCards. A ContactCard is
a representation of a person, company, or other entity, or a group of
such entities, in JSContact Card format, as defined in Section 2 of
[I-D.ietf-calext-jscontact]. Each ContactCard belongs to one or more
AddressBooks.
In servers with support for JMAP Sharing [I-D.ietf-jmap-sharing],
data may be shared with other users. Sharing permissions are managed
per AddressBook.
1.4. Addition to the Capabilities Object
The capabilities object is returned as part of the JMAP Session
object; see [RFC8620], Section 2. This document defines two
additional capability URIs.
1.4.1. urn:ietf:params:jmap:contacts
This represents support for the AddressBook and ContactCard data
types and associated API methods. The value of this property in the
JMAP Session capabilities property is an empty object.
Jenkins Expires 23 August 2024 [Page 3]
Internet-Draft JMAP Contacts February 2024
The value of this property in an account's accountCapabilities
property is an object that MUST contain the following information on
server capabilities and permissions for that account:
* *maxAddressBooksPerCard*: UnsignedInt|null
The maximum number of AddressBooks (see Section 2) that can be can
assigned to a single ContactCard object (see Section 3). This
MUST be an integer >= 1, or null for no limit (or rather, the
limit is always the number of AddressBooks in the account).
* *mayCreateAddressBook*: Boolean
If true, the user may create an AddressBook in this account.
2. AddressBooks
An AddressBook is a named collection of ContactCards. All
ContactCards are associated with one or more AddressBook.
A *AddressBook* object has the following properties:
* *id*: Id (immutable; server-set)
The id of the AddressBook.
* *name*: String
The user-visible name of the AddressBook. This may be any UTF-8
string of at least 1 character in length and maximum 255 octets in
size.
* *description*: String|null (default: null)
An optional longer-form description of the AddressBook, to provide
context in shared environments where users need more than just the
name.
* *sortOrder*: UnsignedInt (default: 0)
Defines the sort order of AddressBooks when presented in the
client's UI, so it is consistent between devices. The number MUST
be an integer in the range 0 <= sortOrder < 2^(31.)
An AddressBook with a lower order should be displayed before a
AddressBook with a higher order in any list of AddressBooks in the
client's UI. AddressBooks with equal order SHOULD be sorted in
alphabetical order by name. The sorting should take into account
locale-specific character order convention.
* *isDefault*: Boolean (server-set)
Jenkins Expires 23 August 2024 [Page 4]
Internet-Draft JMAP Contacts February 2024
This SHOULD be true for exactly one AddressBook in any account,
and MUST NOT be true for more than one AddressBook within an
account. The default AddressBook should be used by clients
whenever they need to choose an AddressBook for the user within
this account, and they do not have any other information on which
to make a choice. For example, if the user creates a new contact
card, the client may automatically set the card as belonging to
the default AddressBook from the user's primary account.
* *isSubscribed*: Boolean
True if the user has indicated they wish to see this AddressBook
in their client. This SHOULD default to false for AddressBooks in
shared accounts the user has access to and true for any new
AddressBooks created by the user themself.
If false, the AddressBook and its contents SHOULD only be
displayed when the user explicitly requests it or to offer it for
the user to subscribe to.
* *shareWith*: Id[AddressBookRights]|null (default: null)
A map of Principal id to rights for principals this AddressBook is
shared with. The principal to which this AddressBook belongs MUST
NOT be in this set. This is null if the AddressBook is not shared
with anyone. May be modified only if the user has the mayAdmin
right. The account id for the principals may be found in the
urn:ietf:params:jmap:principals:owner capability of the Account to
which the AddressBook belongs.
* *myRights*: AddressBookRights (server-set)
The set of access rights the user has in relation to this
AddressBook.
An *AddressBookRights* object has the following properties:
* *mayRead*: Boolean
The user may fetch the ContactCards in this AddressBook.
* *mayWrite*: Boolean
The user may create, modify or destroy all ContactCards in this
AddressBook, or move them to or from this AddressBook.
* *mayAdmin*: Boolean
The user may modify the "shareWith" property for this AddressBook.
* *mayDelete*: Boolean
The user may delete the AddressBook itself.
Jenkins Expires 23 August 2024 [Page 5]
Internet-Draft JMAP Contacts February 2024
2.1. AddressBook/get
This is a standard "/get" method as described in [RFC8620],
Section 5.1. The _ids_ argument may be null to fetch all at once.
2.2. AddressBook/changes
This is a standard "/changes" method as described in [RFC8620],
Section 5.2.
2.3. AddressBook/set
This is a standard "/set" method as described in [RFC8620],
Section 5.3 but with the following additional request argument:
* *onDestroyRemoveContents*: Boolean (default: false)
If false, any attempt to destroy an AddressBook that still has
ContactCard in it will be rejected with an addressBookHasContents
SetError. If true, any ContactCards that were in the AddressBook
will be removed from it, and if in no other AddressBooks they will
be destroyed.
* *onSuccessSetIsDefault*: Id|null
If an id is given, and all creates, updates and destroys (if any)
succeed without error, the server will try to set this AddressBook
as the default. (For references to AddressBook creations, this is
equivalent to a creation-reference, so the id will be the creation
id prefixed with a "#".)
If the id is not found, or the change is not permitted by the server
for policy reasons, it MUST be ignored and the currently default
AddressBook (if any) will remain as such. No error is returned to
the client in this case.
As per [RFC8620], Section 5.3, if the default is successfully
changed, any changed objects MUST be reported in either the "created"
or "updated" argument in the response as appropriate, with the
server-set value included.
The "shareWith" property may only be set by users that have the
mayAdmin right. When modifying the shareWith property, the user
cannot give a right to a principal if the principal did not already
have that right and the user making the change also does not have
that right. Any attempt to do so must be rejected with a forbidden
SetError.
Jenkins Expires 23 August 2024 [Page 6]
Internet-Draft JMAP Contacts February 2024
Users can subscribe or unsubscribe to an AddressBook by setting the
"isSubscribed" property. The server MAY forbid users from
subscribing to certain AddressBooks even though they have permission
to see them, rejecting the update with a forbidden SetError.
The following extra SetError types are defined:
For "destroy":
* *addressBookHasContents*: The AddressBook has at least one
ContactCard assigned to it, and the "onDestroyRemoveContents"
argument was false.
3. ContactCards
A *ContactCard* object contains information about a person, company,
or other entity, or represents a group of such entities. It is a
JSContact Card object, as defined in Section 2 of
[I-D.ietf-calext-jscontact], with the following additional
properties:
* *id*: Id (immutable; server-set)
The id of the ContactCard. The id uniquely identifies a
ContactCard with a particular "uid" within a particular account.
* *addressBookIds*: Id[Boolean]
The set of AddressBook ids this ContactCard belongs to. A card
MUST belong to at least one AddressBook at all times (until it is
destroyed). The set is represented as an object, with each key
being an AddressBook id. The value for each key in the object
MUST be true.
For any Media object in the card (see Section 2.6.4 of
[I-D.ietf-calext-jscontact]), a new property is defined:
* *blobId*: Id
An id for the Blob representing the binary contents of the
resource.
When returning ContactCards, any Media with a data: URI SHOULD return
a blobId property and omit the uri property. The "mediaType"
property MUST also be set. Similarly, when creating or updating a
ContactCard, clients MAY send a blobId instead of the uri property
for a Media object.
A contact card with a "kind" property equal to "group" represents a
group of contacts. Clients often present these separately from other
contact cards. The "members" property, as defined in Section 2.1.6
of [I-D.ietf-calext-jscontact], contains a set of UIDs for other
Jenkins Expires 23 August 2024 [Page 7]
Internet-Draft JMAP Contacts February 2024
contacts that are the members of this group. Clients should consider
the group to contain any ContactCard with a matching UID, from any
account they have access to with support for the
urn:ietf:params:jmap:contacts capability. UIDs that cannot be found
SHOULD be ignored but preserved. For example, suppose a user adds
contacts from a shared address book to their private group, then
temporarily loses access to this address book. The UIDs cannot be
resolved so the contacts will disappear from the group. However, if
they are given permission to access the data again the UIDs will be
found and the contacts will reappear.
3.1. ContactCard/get
This is a standard "/get" method as described in [RFC8620],
Section 5.1.
3.2. ContactCard/changes
This is a standard "/changes" method as described in [RFC8620],
Section 5.2.
3.3. ContactCard/query
This is a standard "/query" method as described in [RFC8620],
Section 5.5.
3.3.1. Filtering
A *FilterCondition* object has the following properties, any of which
may be omitted:
* *inAddressBook*: Id
An AddressBook id. A card must be in this address book to match
the condition.
* *uid*: String
A card must have this string exactly as its uid to match.
* *hasMember*: String
A card must have a "members" property that contains this string as
one of the uids in the set to match.
* *kind*: String
A card must have a type property that equals this string exactly
to match.
* *createdBefore*: UTCDate
The "created" date-time of the ContactCard must be before this
date-time to match the condition.
* *createdAfter*: UTCDate
The "created" date-time of the ContactCard must be the same or
after this date-time to match the condition.
Jenkins Expires 23 August 2024 [Page 8]
Internet-Draft JMAP Contacts February 2024
* *updatedBefore*: UTCDate
The "updated" date-time of the ContactCard must be before this
date-time to match the condition.
* *updatedAfter*: UTCDate
The "updated" date-time of the ContactCard must be the same or
after this date-time to match the condition.
* *text*: String
A card matches this condition if the text matches with text in the
card.
* *name*: String
A card matches this condition if the value of any NameComponent in
the "name" property, or the "full" property in the "name" property
of the card matches the value.
* *name/given*: String
A card matches this condition if the value of a NameComponent with
kind "given" inside the "name" property of the card matches the
value.
* *name/surname*: String
A card matches this condition if the value of a NameComponent with
kind "surname" inside the "name" property of the card matches the
value.
* *name/surname2*: String
A card matches this condition if the value of a NameComponent with
kind "surname2" inside the "name" property of the card matches the
value.
* *nickName*: String
A card matches this condition if the "name" of any NickName in the
"nickNames" property of the card matches the value.
* *organization*: String
A card matches this condition if the "name" of any Organization in
the "organizations" property of the card matches the value.
* *email*: String
A card matches this condition if the "address" or "label" of any
EmailAddress in the "emails" property of the card matches the
value.
* *phone*: String
A card matches this condition if the "number" or "label" of any
Phone in the "phones" property of the card matches the value.
* *onlineService*: String
A card matches this condition if the "service", "uri", "user", or
"label" of any OnlineService in the "onlineServices" property of
the card matches the value.
* *address*: String
A card matches this condition if the value of any StreetComponent
in the "street" property, or the "locality", "region", "country",
or "postcode" property in any Address in the "addresses" property
of the card matches the value.
* *note*: String
Jenkins Expires 23 August 2024 [Page 9]
Internet-Draft JMAP Contacts February 2024
A card matches this condition if the "note" of any Note in the
"notes" property of the card matches the value.
If zero properties are specified on the FilterCondition, the
condition MUST always evaluate to true. If multiple properties are
specified, ALL must apply for the condition to be true (it is
equivalent to splitting the object into one-property conditions and
making them all the child of an AND filter operator).
The exact semantics for matching String fields is *deliberately not
defined* to allow for flexibility in indexing implementation, subject
to the following:
* Text SHOULD be matched in a case-insensitive manner.
* Text contained in either (but matched) single or double quotes
SHOULD be treated as a *phrase search*, that is a match is
required for that exact sequence of words, excluding the
surrounding quotation marks. Use \", \' and \\ to match a literal
", ' and \ respectively in a phrase.
* Outside of a phrase, white-space SHOULD be treated as dividing
separate tokens that may be searched for separately in the
contact, but MUST all be present for the contact to match the
filter.
* Tokens MAY be matched on a whole-word basis using stemming (so for
example a text search for bus would match "buses" but not
"business").
3.3.2. Sorting
The following value for the "property" field on the Comparator object
MUST be supported for sorting:
* "created" - The "created" date on the ContactCard.
* "updated" - The "updated" date on the ContactCard.
The following values for the "property" field on the Comparator
object SHOULD be supported for sorting:
* "name/given" - The value of the first NameComponent in the "name"
property whose "kind" is "given".
* "name/surname" - The value of the first NameComponent in the
"name" property whose "kind" is "surname".
* "name/surname2" - The value of the first NameComponent in the
"name" property whose "kind" is "surname2".
Jenkins Expires 23 August 2024 [Page 10]
Internet-Draft JMAP Contacts February 2024
3.4. ContactCard/queryChanges
This is a standard "/queryChanges" method as described in [RFC8620],
Section 5.6.
3.5. ContactCard/set
This is a standard "/set" method as described in [RFC8620],
Section 5.3.
To set a new photo, the file must first be uploaded using the upload
mechanism as described in [RFC8620], Section 6.1. This will give the
client a valid blobId/size/type to use. The server SHOULD reject
attempts to set a file that is not a recognised image type as the
photo for a card.
3.6. ContactCard/copy
This is a standard "/copy" method as described in [RFC8620],
Section 5.4.
4. Examples
For brevity, in the following examples only the "methodCalls"
property of the Request object, and the "methodResponses" property of
the Response object is shown.
4.1. Fetching initial data
A user has authenticated and the client has fetched the JMAP Session
object. It finds a single Account with the
"urn:ietf:params:jmap:contacts" capability, with id "a0x9", and wants
to fetch all the address books and contacts. It might make the
following request:
[
["AddressBook/get", {
"accountId": "a0x9"
}, "0"],
["ContactCard/get", {
"accountId": "a0x9"
}, "1"]
]
The server might respond with something like:
Jenkins Expires 23 August 2024 [Page 11]
Internet-Draft JMAP Contacts February 2024
[
["AddressBook/get", {
"accountId": "a0x9",
"list": [{
"id": "062adcfa-105d-455c-bc60-6db68b69c3f3",
"name": "Personal",
"description": null,
"sortOrder": 0,
"isDefault": true,
"isSubscribed": true,
"shareWith": null,
"myRights": {
"mayRead": true,
"mayWrite": true,
"mayAdmin": true,
"mayDelete": false
}
}, {
"id": "cd40089d-35f9-4fd7-980b-ba3a9f1d74fe",
"name": "Autosaved",
"description": null,
"sortOrder": 1,
"isDefault": false,
"isSubscribed": true,
"shareWith": null,
"myRights": {
"mayRead": true,
"mayWrite": true,
"mayAdmin": true,
"mayDelete": false
}
}],
"notFound": [],
"state": "~4144"
}, "0"],
["Contact/get", {
"accountId": "a0x9",
"list": [{
"id": "3",
"addressBookIds": {
"062adcfa-105d-455c-bc60-6db68b69c3f3": true
},
"name": {
"components": [
{ "kind": "given", "value": "Joe" },
{ "kind": "surname", "value": "Bloggs" }
],
"isOrdered": true
Jenkins Expires 23 August 2024 [Page 12]
Internet-Draft JMAP Contacts February 2024
},
"emails": {
"0": {
"contexts": {
"private": true
},
"address": "joe.bloggs@example.com"
}
}
}],
"notFound": [],
"state": "ewarbckaqJ::112"
}, "1"]
]
4.2. Changing the default address book
The client tries to change the default address book from "Personal"
to "Autosaved" (and makes no other change):
[
["AddressBook/set", {
"accountId": "a0x9",
"onSuccessSetIsDefault": "cd40089d-35f9-4fd7-980b-ba3a9f1d74fe"
}, "0"]
]
The server allows the change, returning the following response:
[
["AddressBook/set", {
"accountId": "a0x9",
"updated": {
"cd40089d-35f9-4fd7-980b-ba3a9f1d74fe": {
"isDefault": true
},
"062adcfa-105d-455c-bc60-6db68b69c3f3": {
"isDefault": false
}
}
}, "0"]
]
Jenkins Expires 23 August 2024 [Page 13]
Internet-Draft JMAP Contacts February 2024
5. Security considerations
All security considerations of JMAP ([RFC8620]) apply to this
specification. Additional considerations specific to the data types
and functionality introduced by this document are described in the
following subsection.
5.1. Privacy
Contacts consist almost entirely of private, personally identifiable
information, and represent the social connections of users. Privacy
leaks can have real world consequences, and contacts servers and
clients MUST be mindful of the need to keep all data secure.
Servers MUST enforce the ACLs set on address books to ensure only
authorised data is shared.
6. IANA Considerations
6.1. JMAP capability registration for "contacts"
IANA will register the "contacts" JMAP Capability as follows:
Capability Name: urn:ietf:params:jmap:contacts
Specification document: this document
Intended use: common
Change Controller: IETF
Security and privacy considerations: this document, Section 5
6.2. JSContact Property Registrations
IANA will register the following additional properties in the
JSContact Properties Registry.
6.2.1. id
Property Name: id
Property Type: Id
Property Context: Card
Intended Use: Reserved
Jenkins Expires 23 August 2024 [Page 14]
Internet-Draft JMAP Contacts February 2024
Since Version: 1.0
Change Controller: IETF
6.2.2. addressBookIds
Property Name: addressBookIds
Property Type: Id
Property Context: Card
Intended Use: Reserved
Since Version: 1.0
Change Controller: IETF
6.2.3. blobId
Property Name: blobId
Property Type: Id
Property Context: Media
Intended Use: Reserved
Since Version: 1.0
Change Controller: IETF
7. Normative References
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119,
DOI 10.17487/RFC2119, March 1997,
<https://www.rfc-editor.org/info/rfc2119>.
[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
May 2017, <https://www.rfc-editor.org/info/rfc8174>.
[RFC8620] Jenkins, N. and C. Newman, "The JSON Meta Application
Protocol (JMAP)", RFC 8620, DOI 10.17487/RFC8620, July
2019, <https://www.rfc-editor.org/info/rfc8620>.
Jenkins Expires 23 August 2024 [Page 15]
Internet-Draft JMAP Contacts February 2024
[I-D.ietf-jmap-sharing]
Jenkins, N., "JMAP Sharing", Work in Progress, Internet-
Draft, draft-ietf-jmap-sharing-05, 5 February 2024,
<https://datatracker.ietf.org/doc/html/draft-ietf-jmap-
sharing-05>.
[I-D.ietf-calext-jscontact]
Stepanek, R. and M. Loffredo, "JSContact: A JSON
representation of contact data", Work in Progress,
Internet-Draft, draft-ietf-calext-jscontact-16, 8 November
2023, <https://datatracker.ietf.org/doc/html/draft-ietf-
calext-jscontact-16>.
Author's Address
Neil Jenkins (editor)
Fastmail
PO Box 234, Collins St West
Melbourne VIC 8007
Australia
Email: neilj@fastmailteam.com
URI: https://www.fastmail.com
Jenkins Expires 23 August 2024 [Page 16]