├── .github
└── workflows
│ └── build-validate-publish.yml
├── .pr-preview.json
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── index.bs
├── security-privacy-self-assessment.md
└── w3c.json
/.github/workflows/build-validate-publish.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on:
3 | pull_request: {}
4 | push:
5 | branches: [main]
6 | paths:
7 | - 'index.bs'
8 |
9 | jobs:
10 | main:
11 | name: Build, Validate, and Publish
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v2
15 | - uses: w3c/spec-prod@v2
16 | with:
17 | SOURCE: index.bs
18 | DESTINATION: index.html
19 | TOOLCHAIN: bikeshed
20 | W3C_ECHIDNA_TOKEN: ${{ secrets.W3C_TR_TOKEN }}
21 | W3C_WG_DECISION_URL: https://lists.w3.org/Archives/Public/public-device-apis/2021May/0008.html
22 | GH_PAGES_BRANCH: gh-pages
23 | BUILD_FAIL_ON: nothing
24 | W3C_BUILD_OVERRIDE: |
25 | status: WD
--------------------------------------------------------------------------------
/.pr-preview.json:
--------------------------------------------------------------------------------
1 | {
2 | "src_file": "index.bs",
3 | "type": "bikeshed",
4 | "params": {
5 | "force": 1
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Code of Conduct
2 |
3 | All documentation, code and communication under this repository are covered by the [W3C Code of Ethics and Professional Conduct](https://www.w3.org/Consortium/cepc/).
4 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Web Platform Incubator Community Group
2 |
3 | This repository is being used for work in the W3C Web Platform Incubator Community Group, governed by the [W3C Community License
4 | Agreement (CLA)](http://www.w3.org/community/about/agreements/cla/). To make substantive contributions,
5 | you must join the CG.
6 |
7 | If you are not the sole contributor to a contribution (pull request), please identify all
8 | contributors in the pull request comment.
9 |
10 | To add a contributor (other than yourself, that's automatic), mark them one per line as follows:
11 |
12 | ```
13 | +@github_username
14 | ```
15 |
16 | If you added a contributor by mistake, you can remove them in a comment with:
17 |
18 | ```
19 | -@github_username
20 | ```
21 |
22 | If you are making a pull request on behalf of someone else but you had no part in designing the
23 | feature, you can remove yourself with the above syntax.
24 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | All Reports in this Repository are licensed by Contributors
2 | under the
3 | [W3C Software and Document License](http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document).
4 |
5 | Contributions to Specifications are made under the
6 | [W3C CLA](https://www.w3.org/community/about/agreements/cla/).
7 |
8 | Contributions to Test Suites are made under the
9 | [W3C 3-clause BSD License](https://www.w3.org/Consortium/Legal/2008/03-bsd-license.html)
10 |
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Contact Picker API
2 |
3 | [Link to spec](https://w3c.github.io/contact-picker/spec/index.html)
4 |
5 | This is a proposal for adding a Contact Picker API to the Web. Contact pickers are frequently seen in native mobile applications for a variety of [use cases](#use-cases), and in various desktop applications such as e-mail clients and calendars.
6 |
7 | Contact information is among the most sensitive information on a device, which is why we're proposing a picker rather than a lower level API. This enables user agents to offer an experience that's consistent with platform expectations, and to highlight the exact information that is to be shared with the website.
8 |
9 | This proposal does not address the source of the contacts. Many operating systems offer functionality to access the user's contacts, and various browsers allow the user to log in which might provide access to contact information as well.
10 |
11 | ## Use cases
12 | * Social networks could use contact information to bootstrap a user's social graph. This is particularly important for emerging markets, where users might prefer a PWA over a native app due to storage constraints.
13 | * An e-mail application could allow the user to select recipients for a message without needing its own address book.
14 |
15 | ## Why do we need a new built-in API?
16 | * Excluding the operating system, there is no canonical place where users maintain a list of their contacts. Developers would have to integrate with a variety of services to provide a comprehensive experience.
17 | * User agents can offer an experience to users that's consistent with other applications on the user's device. The information that will be shared with the website can be clearly highlighted.
18 | * Contact information is sensitive. Individual contacts can have [any number of properties](https://en.wikipedia.org/wiki/VCard#Properties) associated with them. Websites seeking the e-mail address of a single user should not have access to all other contacts.
19 |
20 | ## Example
21 | ```javascript
22 | selectRecipientsButton.addEventListener('click', async () => {
23 | const contacts = await navigator.contacts.select(['name', 'email'], { multiple: true });
24 |
25 | if (!contacts.length) {
26 | // Either no contacts were selected in the picker, or the picker could
27 | // not be launched. Exposure of the API implies expected availability.
28 | return;
29 | }
30 |
31 | // Use the names and e-mail addresses in |contacts| to populate the
32 | // recipients field in the website's UI.
33 | populateRecipients(contacts);
34 | });
35 | ```
36 |
37 | ## Security and Privacy
38 | Exposing contact information has a clear privacy impact. The spec proposes a picker model so that the user agent can make it clear what information is going to be shared with the website. This differs from native APIs where the permission is requested once, after which the application gets perpetual access to the user's contacts. With the picker model, the website gets a one-off list of contacts selected by the user. Furthermore, The API will only be available from secure top-level contexts.
39 |
40 | ## Abuse Scenarios
41 |
42 | ### Sharing contacts with unintended recipients
43 | The first mitigation against this abuse vector is that the API is only available from top-level contexts. This means embedded iframes can't request contact information.
44 |
45 | The spec also enforces that the picker UI shows which origin the contacts will be shared with, which contacts will be shared, and what information regarding the contacts will be shared.
46 |
47 | ### Websites forcing users to share contacts
48 | The picker UI MUST have a cancel/return option for the user to not share any contacts. The spec also recommends that the UI have a way for users to opt out of sharing certain contact information, in a way that the website cannot know whether the user opted out.
49 |
50 | Implementers should be wary of malicious website UI that can be rendered side-by-side with the picker. The UI can pretend to be part of the user agent's UI and encourage users to share more information than they intended to. A potential workaround to avoid this would be making the picker fullscreen.
51 |
52 | ### Websites spamming users with the picker
53 | To prevent abuse and user annoyance, the API has some usability restrictions. The picker can only be brought up if the API call was initiated by a user gesture. This means websites can't bring up the picker on website load. There can also be one instance of the picker at any given time, so websites can't request multiple stacked pickers on top of each other.
54 |
55 | ## Alternatives Considered
56 |
57 | ##### ``
58 | The Web Platform already supports a picker for external data through ``, which some user agents specialize based on the mime types that are being selected.
59 |
60 | Supporting contacts this way has a number of downsides:
61 | * There is much contention in file types for expressing contact information. For most use cases, a `FileReader` would be used to read and parse the file in order to access the individual properties. We can optimize for this. Libraries can be used for converting this data to a format of the developer's choosing.
62 | * Feature detection is harder, and would likely need a new API such as `HTMLInputElement.isTypeSupported()` mimicking the [`MediaRecorder`](https://www.w3.org/TR/mediastream-recording/#dom-mediarecorder-istypesupported). Unlike existing specializations of file pickers, contacts would be unlikely to gracefully degrade to a general picker.
63 | * Extensibility is harder, as it would rely on additional parameters being passed to the mime type.
64 |
65 | #### Previous Standardization Attempts
66 | There have been multiple standardization attempts to bring a Contacts API to the web. Here's a list of them and brief explanation of why they never materialized.
67 | * https://lists.w3.org/Archives/Public/public-device-apis/2009Apr/att-0001/contacts.html
68 |
69 | This attempt was _donated_ by Nokia rather than being formally put on the standardization track, and it seems to have not been picked up since.
70 |
71 | * https://www.w3.org/TR/2010/WD-contacts-api-20100121/
72 |
73 | This attempt was shelved in 2014 in favour of Web Intents, which never materialized.
74 |
75 | * https://www.w3.org/TR/2015/NOTE-contacts-manager-api-20150602/
76 |
77 | This attempt built on top of the 2010 one by adding more programmatic access to the user's address book. This seems to be quite distantly separated from today's interest from implementations.
78 |
79 | And some proprietary attempts.
80 | * https://wiki.mozilla.org/WebAPI/ContactsAPI
81 |
82 | This attempt was not put on the standardization track, and doesn't seem to have been picked up since.
83 |
84 | The current proposal differs in its approach to privacy, which was the main emphasis of the API when it was designed. Unlike previous attempts which allow for perpetual access after granted permission, or include a vague privacy model, this spec enforces UI restrictions which give users full control over shared data and limit abuse. For example, a picker model is enforced where the user always acts as an intermediary of the shared contact info with full control every time contacts are requested.
85 |
86 | ## Potential follow-up work
87 | * As mentioned, more properties can be added iteratively as use cases are identified. It is a non-goal of this API to share _all_ information stored for a particular contact without filtering.
88 | * A Contact Provider API can be considered to complement data available from the operating system or the user's account.
89 |
--------------------------------------------------------------------------------
/index.bs:
--------------------------------------------------------------------------------
1 |
2 | Title: Contact Picker API
3 | Shortname: contact-picker
4 | Status: ED
5 | Group: dap
6 | Level: none
7 | ED: https://w3c.github.io/contact-picker/
8 | TR: https://www.w3.org/TR/contact-picker/
9 | Editor: Peter Beverloo 44819, Google, beverloo@google.com
10 | Former Editor: Rayan Kanso 115452, Google, rayankans@google.com
11 | Abstract: An API to give one-off access to a user's contact information with full control over the shared data.
12 | Markup Shorthands: css no, markdown yes
13 | Indent: 2
14 | text macro: JOINT yes
15 | text macro: JOINTWEBAPPS yes
16 |
24 |
25 | # Introduction # {#intro}
26 |
27 | Contact pickers are frequently seen in various desktop and native mobile applications for a variety
28 | of use cases. This specification defines an API to bring contact pickers to the web, which will
29 | enable new use cases for web apps, such as:
30 | * Bootstrapping a user's social graph for social networks.
31 | * Selecting the recipients of a message within an e-mail application.
32 |
33 | The contact picker model was chosen to give full control to users over the shared data, allowing
34 | users to choose exactly which contacts to provide to the website. The contact picker model gives
35 | websites one-off access to a user's contacts, meaning developers have to request access to the
36 | user's contacts every time they need it. This differs from some native contact APIs, but is
37 | necessary for ensuring users' contacts are not accessed without their knowledge and explicit
38 | consent.
39 |
40 | ## Examples ## {#examples}
41 |
42 |
43 | Requesting contacts as a result of a user click.
44 |
45 |
46 | selectRecipientsButton.addEventListener('click', async () => {
47 | const contacts = await navigator.contacts.select(['name', 'email'], {multiple: true});
48 |
49 | if (!contacts.length) {
50 | // No contacts were selected in the picker.
51 | return;
52 | }
53 |
54 | // Use the names and e-mail addresses in |contacts| to populate the
55 | // recipients field in the website's UI.
56 | populateRecipients(contacts);
57 | });
58 |
59 |
60 | In the above example `selectRecipientsButton` is a {{HTMLButtonElement}}, and `populateRecipients`
61 | is a developer-defined function.
62 |
63 |
64 |
65 |
66 | Requesting an address to deliver a gift to.
67 |
68 |
69 | selectRecipientButton.addEventListener('click', async () => {
70 |
71 | // We are unsure if addresses are supported, or can be provided by the browser.
72 | if ((await navigator.contacts.getProperties()).includes('address')) {
73 | const contacts = await navigator.contacts.select(['address']);
74 |
75 | if (!contacts.length) {
76 | // No contacts were selected in the picker.
77 | return;
78 | }
79 |
80 | // length is 1 since we didn't request multiple contacts.
81 | sendGiftToAddress(contacts[0].address);
82 | }
83 |
84 | // Fallback to a form.
85 | });
86 |
87 |
88 | In the above example `selectRecipientButton` is a {{HTMLButtonElement}}, and `sendGiftToAddress`
89 | is a developer-defined function.
90 |
91 |
92 |
93 | Requesting a name and an icon.
94 |
95 |
96 | selectRecipientButton.addEventListener('click', async () => {
97 |
98 | // We are unsure if icons are supported, or can be provided by the browser.
99 | if ((await navigator.contacts.getProperties()).includes('icon')) {
100 | const contacts = await navigator.contacts.select(['name', 'icon']);
101 |
102 | if (!contacts.length) {
103 | // No contacts were selected in the picker.
104 | return;
105 | }
106 |
107 | if (!contacts[0].name.length || !contacts[0].icon.length) {
108 | // Info not found. Use fallback.
109 | return;
110 | }
111 |
112 | // We only need one name and one image.
113 | const name = contacts[0].name[0];
114 | const imgBlob = contacts[0].icon[0];
115 |
116 | // Display image.
117 | const url = URL.createObjectURL(imgBlob);
118 | imgContainer.onload = () => URL.revokeObjectURL(url);
119 | imgContainer.src = url;
120 |
121 | // Alternatively use a Bitmap.
122 | const imgBitmap = await createImageBitmap(imgBlob);
123 |
124 | // Upload icon.
125 | const response = await fetch('/contacticon', {method: 'POST', body: imgBlob});
126 | }
127 | });
128 |
129 | In the above example `selectRecipientButton` is a {{HTMLButtonElement}}, and `imgContainer`
130 | is a {{HTMLImageElement}}.
131 |
132 |
133 | # Privacy Considerations # {#privacy}
134 |
135 | Exposing contact information has a clear privacy impact, in terms of exposing PII of uninvolved
136 | parties. A picker model is enforced so that the user agent can offer a user experience that makes
137 | it clear what information is going to be shared with the website and when.
138 |
139 | The following constraints are also enforced:
140 | * A user gesture is needed to initiate the API, to disallow programmatic requests to the user's
141 | * The API is only available in a [=navigable/top-level traversable=], which must also be a
142 | [=secure context=]. These restrictions help ensure that the provided contact information reaches
143 | its intended recipient.
144 | * [=Transient activation=] is needed to initiate the API, to disallow programmatic requests to the user's
145 | contacts.
146 |
147 | # Security Considerations # {#security-considerations}
148 |
149 | * The API is only available in a [=navigable/top-level traversables=], which must also be a
150 | [=secure context=]. These restrictions help ensure that the provided contact information reaches
151 | its intended recipient.
152 |
153 | # Realms # {#realms}
154 |
155 | All platform objects are created in the [=this=]'s [=relevant Realm=] unless otherwise
156 | specified.
157 |
158 | # Infrastructure # {#infrastructure}
159 |
160 | The contact picker task source is a [=task source=].
161 |
162 |
163 | To queue a contact picker task on an optional |eventLoop| (an [=/event loop=],
164 | defaulting to the caller's [=this=]'s [=relevant settings object=]'s
165 | [=responsible event loop=]) with |steps| (steps), [=queue a task=] on |eventLoop| using the
166 | [=contact picker task source=] to run |steps|.
167 |
173 |
174 | * country, a {{DOMString}} representing the country of the address as an
175 | [[ISO3166-1]] alpha-2 code stored in its canonical uppercase form or the empty string. For
176 | example, "JP".
177 | * address line, a [=list=] of {{DOMString}}s, containing the most specific part of the
178 | address. It can include, for example, a street name, a house number, apartment number, a rural
179 | delivery route, descriptive instructions, or a post office box number.
180 | * region, a {{DOMString}} representing the top level administrative subdivision of the
181 | country. For example, this can be a state, a province, an oblast, or a prefecture.
182 | * city, a {{DOMString}} representing the city/town portion of the address.
183 | * dependent locality, a {{DOMString}} representing the dependent locality or sublocality
184 | within a city. For example, neighborhoods, boroughs, districts, or UK dependent localities.
185 | * postal code, a {{DOMString}} representing the postal code or ZIP code, also known as
186 | PIN code in India.
187 | * sorting code, a {{DOMString}} representing the sorting code system, such as the CEDEX
188 | system used in France.
189 | * organization, a {{DOMString}} representing the organization, firm, company, or
190 | institution at the address.
191 | * recipient, a {{DOMString}} representing the name of the recipient or contact person at
192 | the address.
193 | * phone number, a {{DOMString}} representing the phone number of the recipient or contact
194 | person at the address, optionally structured to adhere to [[E.164]].
195 |
196 |
197 |
198 | ## User contact ## {#infrastructure-user-contact}
199 |
200 | A user contact consists of:
201 |
202 |
203 | * names, a [=list=] of {{DOMString}}s, each [=list/item=] representing a unique name
204 | corresponding to the user.
205 | * emails, a [=list=] of {{DOMString}}s, each [=list/item=] representing a unique
206 | [=valid email address=] of the user.
207 | * numbers, a [=list=] of {{DOMString}}s, each [=list/item=] representing a unique phone
208 | number of the user.
209 | * addresses, a [=list=] of {{ContactAddress}}es, each [=list/item=] representing a
210 | unique [=physical address=] of the user.
211 | * icons, a [=list=] of {{Blob}}s, each [=list/item=] representing a unique image of the
212 | user.
213 |
214 | NOTE: An icon {{Blob}}'s {{Blob/type}} is an [=image mime type=].
215 |
216 |
217 |
218 | A [=user contact=] contains data relating to a single user.
219 |
220 | Note: The lists can be of different sizes, and entries with the same index don't need to correspond
221 | to each other.
222 |
223 | ## Contacts source ## {#infrastructure-contacts-source}
224 |
225 | The contacts source is a service that provides the user's contact information to
226 | the user agent.
227 |
228 | A [=contacts source=] consists of:
229 |
230 |
231 | * available contacts, a [=list=] of [=user contacts=].
232 | * supported properties, a [=list=] of [=available=] {{ContactProperty}} values.
233 |
234 |
235 |
236 | Note: It is up to the user agent to choose the [=contacts source=].
237 |
238 |
239 |
240 | # API Description # {#api}
241 |
242 | ## Extensions to {{Navigator}} ## {#extensions-to-navigator}
243 |
244 |
250 |
251 |
252 | A {{Navigator}} has a contacts manager (a {{ContactsManager}}), initially a new
253 | {{ContactsManager}}.
254 |
255 | The contacts attribute's getter must return the [=this=]'s
256 | [=Navigator/contacts manager=].
257 |
258 |
259 | The [=Window/navigable=] has a contact picker is showing flag, initially unset.
260 |
261 | ## {{ContactProperty}} ## {#contact-property}
262 |
263 |
266 |
267 | A {{ContactProperty}} is considered to be available if its associated [=user contact=]
268 | field can be accessed by the user agent.
269 |
270 | : "address"
271 | :: Associated with [=user contact=]'s [=user contact/addresses=].
272 | : "email"
273 | :: Associated with [=user contact=]'s [=user contact/emails=].
274 | : "icon"
275 | :: Associated with [=user contact=]'s [=user contact/icons=].
276 | : "name"
277 | :: Associated with [=user contact=]'s [=user contact/names=].
278 | : "tel"
279 | :: Associated with [=user contact=]'s [=user contact/numbers=].
280 |
281 | ## {{ContactAddress}} ## {#contact-address}
282 |
283 |
299 |
300 | The {{ContactAddress}} interface represents a [=physical address=].
301 |
302 |
303 | A {{ContactAddress}} instance has:
304 |
305 | * An address (a [=physical address=]).
306 |
307 | The city attribute's getter must return the [=this=]'s
308 | [=ContactAddress/address=]' [=physical address/city=].
309 |
310 | The country attribute's getter must return the [=this=]'s
311 | [=ContactAddress/address=]' [=physical address/country=].
312 |
313 | The dependentLocality attribute's getter must return the [=this=]'s
314 | [=ContactAddress/address=]' [=physical address/dependent locality=].
315 |
316 | The organization attribute's getter must return the [=this=]'s
317 | [=ContactAddress/address=]' [=physical address/organization=].
318 |
319 | The phone attribute's getter must return the [=this=]'s
320 | [=ContactAddress/address=]' [=physical address/phone number=].
321 |
322 | The postalCode attribute's getter must return the [=this=]'s
323 | [=ContactAddress/address=]' [=physical address/postal code=].
324 |
325 | The recipient attribute's getter must return the [=this=]'s
326 | [=ContactAddress/address=]' [=physical address/recipient=].
327 |
328 | The region attribute's getter must return the [=this=]'s
329 | [=ContactAddress/address=]' [=physical address/region=].
330 |
331 | The sortingCode attribute's getter must return the [=this=]'s
332 | [=ContactAddress/address=]' [=physical address/sorting code=].
333 |
334 | The addressLine attribute's getter must return the [=this=]'s
335 | [=ContactAddress/address=]' [=physical address/address line=].
336 |
365 | The getProperties() method, when invoked, runs these steps:
366 |
367 | 1. Let |promise| be [=a new promise=].
368 | 1. Run the following steps [=in parallel=]:
369 | 1. Resolve |promise| with [=contacts source=]'s [=contacts source/supported properties=].
370 | 1. Return |promise|.
371 |
372 |
377 | The select(|properties|, |options|) method, when invoked, runs these steps:
378 |
379 | 1. Let |global| be the [=this=]'s [=relevant global object=].
380 | 1. Let |navigable| be |global|'s [=Window/navigable=].
381 | 1. If |navigable| is not a [=navigable/top-level traversable=], then return
382 | [=a promise rejected with=] an {{InvalidStateError}} {{DOMException}}.
383 | 1. If |global| does not have [=transient activation=], then return
384 | [=a promise rejected with=] a {{SecurityError}} {{DOMException}}.
385 | 1. Otherwise, [=consume user activation=] of the |global|.
386 | 1. If |navigable|'s [=contact picker is showing flag=] is set then return
387 | [=a promise rejected with=] an {{InvalidStateError}} {{DOMException}}.
388 | 1. If |properties| is [=list/empty=], then return [=a promise rejected with=] a {{TypeError}}.
389 | 1. [=list/For each=] |property| of |properties|:
390 | 1. If [=contacts source=]'s [=contacts source/supported properties=] does not [=list/contain=]
391 | |property|, then return [=a promise rejected with=] a {{TypeError}}.
392 | 1. Set |navigable|'s [=contact picker is showing flag=].
393 | 1. Let |promise| be [=a new promise=].
394 | 1. Run the following steps [=in parallel=]:
395 | 1. Let |selectedContacts| be be the result of [=launching a contact picker=] with |options|'
396 | `multiple` member and |properties|. If this fails, then:
397 | 1. [=Queue a contact picker task=] to run these steps:
398 | 1. [=Reject=] |promise| an {{InvalidStateError}} {{DOMException}}.
399 | 1. Unset |navigable|'s [=contact picker is showing flag=].
400 | 1. Abort these steps.
401 | 1. Unset |navigable|'s [=contact picker is showing flag=].
402 | 1. [=Queue a contact picker task=] to run these steps:
403 | 1. Let |contacts| be an empty [=list=].
404 | 1. [=list/For each=] |selectedContact| in |selectedContacts|:
405 | 1. Let |contact| be a new {{ContactInfo}} with:
406 | : {{ContactInfo/address}}
407 | :: |selectedContact|'s [=user contact/addresses=] if |properties| [=list/contains=]
408 | "`address`", otherwise undefined.
409 | : {{ContactInfo/email}}
410 | :: |selectedContact|'s [=user contact/emails=] if |properties| [=list/contains=]
411 | "`email`", otherwise undefined.
412 | : {{ContactInfo/icon}}
413 | :: |selectedContact|'s [=user contact/icons=] if |properties| [=list/contains=]
414 | "`icon`", otherwise undefined.
415 | : {{ContactInfo/name}}
416 | :: |selectedContact|'s [=user contact/names=] if |properties| [=list/contains=]
417 | "`name`", otherwise undefined.
418 | : {{ContactInfo/tel}}
419 | :: |selectedContact|'s [=user contact/numbers=] if |properties| [=list/contains=]
420 | "`tel`", otherwise undefined.
421 | 1. [=list/Append=] |contact| to |contacts|.
422 | 1. Resolve |promise| with |contacts|.
423 | 1. Return |promise|.
424 |
429 | To launch a contact picker with |allowMultiple| (a
430 | [=boolean=]), and |properties| (a [=list=] of {{DOMString}}s), the user agent MUST present a user
431 | interface that follows these rules:
432 |
433 | * If presenting a user interface fails or accessing the [=contacts source=]'s
434 | [=contacts source/available contacts=] fails, then return failure.
435 | * The UI MUST prominently display the [=navigable/top-level traversable=]'s [=origin=].
436 | * The UI MUST make it clear which `properties` of the contacts are requested.
437 |
438 | NOTE: This information is derived from |properties|.
439 |
440 | * The UI SHOULD provide a way for users to opt out of sharing certain contact information.
441 |
442 | NOTE: If the user opts out, the appropriate [=user contact=] fields should be modified before
443 | returning the selected contacts. It should be indistinguishable from the returned
444 | [=user contact=]s whether the user opted out from sharing some information or if the
445 | information was not present to begin with.
446 |
447 | * The UI MUST make it clear which information will be shared.
448 | * The UI MUST provide a way to select individual contacts. If |allowMultiple| is false, only one
449 | contact should be pickable.
450 | * The UI MUST provide an option to cancel/return without sharing any contacts, in which case
451 | remove the UI and return an empty [=list=].
452 | * The UI MUST provide an a way for users to indicate that they are done selecting, in which case
453 | remove the UI and return a [=list=] of the selected contacts as [=user contacts=].
454 |
455 |
456 | # Creating a `ContactAddress` from user-provided input # {#creating-contactaddress}
457 |
458 | The steps to create a `ContactAddress` from user-provided input
459 | are given by the following algorithm.
460 | The algorithm optionally takes a [=list=] |redactList|.
461 | If the |redactList| is not passed, it defaults to an [=list/empty=] [=list=].
462 |
463 | NOTE: The |redactList| optionally gives user agents
464 | the possibility to limit the amount of personal information
465 | about the recipient that the API shares with the requesting application.
466 | The resulting {{ContactAddress}} object provides enough information
467 | to perform necessary operations
468 | such as communication or service delivery,
469 | but, in most cases,
470 | not enough information to physically locate and uniquely identify the recipient.
471 | Unfortunately, even with the |redactList|,
472 | recipient anonymity cannot be assured.
473 | This is because in some countries
474 | postal codes are so fine-grained that they can uniquely identify a recipient.
475 |
476 |
477 |
478 | 1. Let |details| be the [=map=] «
479 | "addressLine" → empty [=list=],
480 | "country" → "",
481 | "phone" → "",
482 | "city" → "",
483 | "dependentLocality" → "",
484 | "organization" → "",
485 | "postalCode" → "",
486 | "recipient" → "",
487 | "region" → "",
488 | "sortingCode" → ""
489 | ».
490 | 1. If |redactList| doesn't [=list/contain=] "addressLine",
491 | set |details|["addressLine"] to the result of splitting the user-provided address line into a [=list=].
492 |
493 | NOTE: How to split an address line is locale dependent
494 | and beyond the scope of this specification.
495 |
496 | 1. If |redactList| doesn't [=list/contain=] "country",
497 | set |details|["country"] to the user-provided [=physical address/country=] as an upper case [[ISO3166-1]] alpha-2 code.
498 | 1. If |redactList| doesn't [=list/contain=] "phone",
499 | set |details|["phone"] to the user-provided [=physical address/phone number=].
500 |
501 | NOTE: To maintain users' privacy,
502 | implementers need to be mindful
503 | that a contact address's associated phone number
504 | might be different or the same from that of the end user's.
505 | As such,
506 | implementers need to take care
507 | to not provide the end user's phone number without the end user's consent.
508 |
509 | 1. If |redactList| doesn't [=list/contain=] "city",
510 | set |details|["city"] to the user-provided [=physical address/city=].
511 | 1. If |redactList| doesn't [=list/contain=] "dependentLocality",
512 | set |details|["dependentLocality"] to the user-provided [=physical address/dependent locality=].
513 | 1. If |redactList| doesn't [=list/contain=] "organization",
514 | set |details|["organization"] to the user-provided recipient [=physical address/organization=].
515 | 1. If |redactList| doesn't [=list/contain=] "postalCode",
516 | set |details|["postalCode"] to the user-provided [=physical address/postal code=].
517 | Optionally, redact part of |details|["postalCode"].
518 |
519 | NOTE: [=physical address/Postal codes=]
520 | in certain countries can be so specific
521 | as to uniquely identify an individual.
522 | This being a privacy concern,
523 | some user agents only return the part of a postal code
524 | that they deem sufficient for the application's needs.
525 | This varies across countries and regions,
526 | and so the choice to redact part,
527 | or all,
528 | of the postal code is left to the discretion of implementers
529 | in the interest of protecting users' privacy.
530 |
531 | 1. If |redactList| doesn't [=list/contain=] "recipient",
532 | set |details|["recipient"] to the user-provided [=physical address/recipient=] of the contact information.
533 | 1. If |redactList| doesn't [=list/contain=] "region",
534 | set |details|["region"] to the user-provided [=physical address/region=].
535 |
536 | NOTE: In some countries (e.g., Belgium)
537 | it is uncommon for users to include a [=physical address/region=]
538 | as part of a [=physical address=]
539 | (even if all the regions of a country are part of [[ISO3166-2]]).
540 | As such,
541 | when the user agent knows that the user is inputting the address
542 | for a particular country,
543 | it might not provide a field for the user to input a [=physical address/region=].
544 | In such cases,
545 | the user agent returns an empty string for both {{ContactAddress}}'s
546 | {{ContactAddress/region}} attribute - but the address can still serve its intended purpose
547 | (e.g., be valid for communication or service delivery).
548 |
549 | 1. If |redactList| doesn't [=list/contain=] "sortingCode",
550 | set |details|["sortingCode"] to the user-provided [=physical address/sorting code=].
551 | 1. Return a newly created {{ContactAddress}} whose attribute's value's match those in |details|.
552 |
553 |
554 |
555 |
556 | # Acknowledgments # {#acknowledgments}
557 |
558 | There has been multiple earlier attempts to standardize a Contacts API for the web and this
559 | API strives to learn from this rich history. Earlier attempts include Mozilla's
560 | [Contacts API](https://wiki.mozilla.org/WebAPI/ContactsAPI),
561 | [Contacts API](https://lists.w3.org/Archives/Public/public-device-apis/2009Apr/att-0001/contacts.html)
562 | W3C Member submission and standardization efforts by W3C Working Groups:
563 | [Contacts API](https://www.w3.org/TR/2010/WD-contacts-api-20100121/),
564 | [Pick Contacts Intent](https://www.w3.org/TR/2014/NOTE-contacts-api-20140114/), and
565 | [Contacts Manager API](https://www.w3.org/TR/2015/NOTE-contacts-manager-api-20150602/).
566 | The Contact Picker API differs in its approach to privacy, which was the main emphasis of the API
567 | when it was designed. Unlike previous attempts which allow for perpetual access after granted
568 | permission, or include a vague privacy model, this spec enforces UI restrictions which give users
569 | full control over shared data and limit abuse. For example, a picker model is enforced where the
570 | user always acts as an intermediary of the shared contact info with full control every time
571 | contacts are requested. For more historical context, please refer to the Status of the Document
572 | sections of the earlier attempts.
573 |
--------------------------------------------------------------------------------
/security-privacy-self-assessment.md:
--------------------------------------------------------------------------------
1 | https://www.w3.org/TR/security-privacy-questionnaire/
2 |
3 | ### 3.1 Does this specification deal with personally-identifiable information?
4 |
5 | Yes: names, e-mail addresses and phone numbers of people in a device's contact database.
6 |
7 | ### 3.2 Does this specification deal with high-value data?
8 |
9 | Yes: see 3.1.
10 |
11 | ### 3.3 Does this specification introduce new state for an origin that persists across browsing sessions?
12 |
13 | No.
14 |
15 | ### 3.4 Does this specification expose persistent, cross-origin state to the web?
16 |
17 | Contact information is specific to the user, so depending on whether they choose to share the same information with multiple origins it may be.
18 |
19 | ### 3.5 Does this specification expose any other data to an origin that it doesn’t currently have access to?
20 |
21 | Yes: contact information in the form of names, e-mail addresses and phone numbers. Implementations MUST make it clear to the user what information is being shared, and the user MUST be in control of selecting which information is being shared.
22 |
23 | ### 3.6 Does this specification enable new script execution/loading mechanisms?
24 |
25 | No.
26 |
27 | ### 3.7 Does this specification allow an origin access to a user’s location?
28 |
29 | No.
30 |
31 | ### 3.8 Does this specification allow an origin access to sensors on a user’s device?
32 |
33 | No.
34 |
35 | ### 3.9 Does this specification allow an origin access to aspects of a user’s local computing environment?
36 |
37 | Yes: their contact information.
38 |
39 | ### 3.10 Does this specification allow an origin access to other devices?
40 |
41 | No.
42 |
43 | ### 3.11 Does this specification allow an origin some measure of control over a user agent’s native UI?
44 |
45 | No.
46 |
47 | ### 3.12 Does this specification expose temporary identifiers to the web?
48 |
49 | No.
50 |
51 | ### 3.13 Does this specification distinguish between behavior in first-party and third-party contexts?
52 |
53 | No.
54 |
55 | ### 3.14 How should this specification work in the context of a user agent’s "incognito" mode?
56 |
57 | No change in behavior.
58 |
59 | ### 3.15 Does this specification persist data to a user’s local device?
60 |
61 | No.
62 |
63 | ### 3.16 Does this specification have a "Security Considerations" and "Privacy Considerations" section?
64 |
65 | _Not yet!_
66 |
67 | ### 3.17 Does this specification allow downgrading default security characteristics?
68 |
69 | No.
70 |
--------------------------------------------------------------------------------
/w3c.json:
--------------------------------------------------------------------------------
1 | {
2 | "group": [ 43696, 114929 ]
3 | , "contacts": ["himorin"]
4 | , "repo-type": "rec-track"
5 | }
6 |
--------------------------------------------------------------------------------