forked from w3c/webappsec-credential-management
-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.src.html
2195 lines (1705 loc) · 111 KB
/
index.src.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<h1>Credential Management Level 1</h1>
<pre class="metadata">
Status: ED
ED: https://w3c.github.io/webappsec-credential-management/
TR: http://www.w3.org/TR/credential-management-1/
Previous Version: http://www.w3.org/TR/2016/WD-credential-management-1-20160425/
Shortname: credential-management
Level: 1
Editor: Mike West 56384, Google Inc., [email protected]
Group: webappsec
Abstract:
This specification describes an imperative API enabling a website to request a
user's credentials from a user agent, and to help the user agent correctly
store user credentials for future use.
Indent: 2
Version History: https://github.com/w3c/webappsec-credential-management/commits/master/index.src.html
Boilerplate: omit conformance, omit feedback-header
!Participate: <a href="https://github.com/w3c/webappsec-credential-management/issues/new">File an issue</a> (<a href="https://github.com/w3c/webappsec-credential-management/issues">open issues</a>)
Markup Shorthands: css off, markdown on
</pre>
<pre class="anchors">
spec: ECMA262; urlPrefix: https://tc39.github.io/ecma262/
type: dfn
text: JavaScript realm; url: sec-code-realms
text: internal method; url: sec-ordinary-object-internal-methods-and-internal-slots
spec: HTML; urlPrefix: https://html.spec.whatwg.org/multipage/
urlPrefix: forms.html
type: element-attr
text: autocomplete; for: input; url: #attr-fe-autocomplete
text: name; for: input; url: #attr-fe-name
text: enctype; for: form; url: #concept-fs-enctype
type: attr-value
for: autocomplete
text: current-password; url: attr-fe-autocomplete-current-password
text: new-password; url: attr-fe-autocomplete-new-password
text: nickname; url: attr-fe-autocomplete-nickname
text: name; url: attr-fe-autocomplete-name
text: photo; url: attr-fe-autocomplete-photo
text: username; url: attr-fe-autocomplete-username
spec: XHR; urlPrefix: https://xhr.spec.whatwg.org/
type: dfn
text: entry; url: concept-formdata-entry
text: entries; for: FormData; url: concept-formdata-entry
text: name; for: entry; url: concept-formdata-entry-name
text: value; for: entry; url: concept-formdata-entry-value
text: type; for: entry; url: concept-formdata-entry-type
type: interface
text: FormData; url: interface-formdata
spec: PSL; urlPrefix: https://publicsuffix.org/list/
type: dfn
text: registerable domain; url: #
text: public suffix; url: #
spec: FETCH; urlPrefix: https://fetch.spec.whatwg.org/
type: dfn
text: http-network-or-cache fetch; url: http-network-or-cache-fetch
</pre>
<pre class="link-defaults">
spec:html; type:dfn; for:/; text:origin
spec:html; type:dfn; for:environment settings object; text:global object
spec:fetch; type:dfn; for:/; text:request
spec:fetch; type:dictionary; for:/; text:RequestInit
spec:infra; type:dfn; for:/; text:set
spec:infra; type:dfn; for:struct; text:item
spec:webidl; type:idl; for:/; text:Function
spec:webidl; type:interface; text:Promise
spec:promises-guide-1; type:dfn; text:resolve
<!-- These need to be exported -->
spec:html; type:dfn; text:submittable element
spec:html; type:dfn; text:form owner
spec:html; type:dfn; text:autofill detail tokens
spec:url; type:dfn; text:urlencoded byte serializer
</pre>
<pre class='ignored-specs'>
spec:css-syntax-3;
</pre>
<pre class="biblio">
{
"WEB-LOGIN": {
"authors": [ "Jason Denizac", "Robin Berjon", "Anne van Kesteren" ],
"href": "https://github.com/jden/web-login",
"title": "web-login"
}
}
</pre>
<!--
████ ██ ██ ████████ ████████ ███████
██ ███ ██ ██ ██ ██ ██ ██
██ ████ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ ████████ ██ ██
██ ██ ████ ██ ██ ██ ██ ██
██ ██ ███ ██ ██ ██ ██ ██
████ ██ ██ ██ ██ ██ ███████
-->
<section>
# Introduction # {#introduction}
<em>This section is not normative.</em>
Signing into websites is more difficult than it should be. The user agent is in a unique position
to improve the experience in a number of ways, and most modern user agents have recognized this by
providing some measure of credential management natively in the browser. Users can save usernames
and passwords for websites, and those [=credentials=] are autofilled into sign-in forms with
varying degrees of success.
The <{input/autocomplete}> attribute offers a declarative mechanism by which websites can work
with user agents to improve the latter's ability to detect and fill sign-in forms by marking
specific fields as "username" or "password", and user agents implement a wide variety of detection
heuristics to work with websites which haven't taken the time to provide this detail in markup.
While this combination of heuristic and declarative detection works relatively well, the status
quo leaves some large gaps where detection is problematic. Sites with uncommon sign-in
mechanisms (submitting credentials via {{XMLHttpRequest}} [[XMLHTTPREQUEST]], for instance) are
difficult to reliably detect, as is the increasingly common case in which users wish to
authenticate themselves using a federated identity provider. Allowing websites to more directly
interact with the user agent's credential manager would allow the credential manager to be more
accurate on the one hand, and to assist users with federated sign-in on the other.
These use cases are explored in more detail in [[#use-cases]] and in
<a href="https://w3c.github.io/webappsec/usecases/credentialmanagement/">Credential Management:
Use Cases and Requirements</a>; this specification attempts to address many of the requirements
that document outlines by defining a Credential Manager API which a website can use to request
[=credentials=] for a user, and to ask the user agent to persist credentials when a user signs in
successfully.
Note: The API defined here is intentionally small and simple: it does not intend to provide
authentication in and of itself, but is limited to providing an interface to the existing
credential managers implemented by existing user agents. That functionality is valuable
<em>right now</em>, without significant effort on the part of either vendors or authors. There's
certainly quite a bit more which could be done, of course. See [[#teh-futur]] for some thoughts
we've punted for now, but which could be explored in future iterations of this API.
## Use Cases ## {#use-cases}
Modern user agents generally offer users the capability to save passwords when signing into a
website, and likewise offer the capability to fill those passwords into sign-in forms fully- or
semi-automatically when users return to a website. From the perspective of a website, this
behavior is completely invisible: the website doesn't know that passwords have been stored, and
it isn't notified that passwords have been filled. This is both good and bad. On the one hand, a
user agent's password manager works regardless of whether or not a site cooperates, which is
excellent for users. On the other, the password managers' behaviors are a fragile and proprietary
hodgepodge of heuristics meant to detect and fill sign-in forms, password change forms, etc.
A few problems with the status quo stand out as being particularly noteworthy:
* User agents have an incredibly difficult time helping users with
federated identity providers. While detecting a username/password
form submission is fairly straightforward, detecting sign-in via a
third-party is quite difficult to reliably do well. It would be nice
if a website could help the user agent understand the intent behind the
redirects associated with a typical federated sign-in action.
* Likewise, user agents struggle to detect more esoteric sign-in
mechanisms than simple username/password forms. Authors increasingly
asynchronously sign users in via {{XMLHttpRequest}} or similar
mechanisms in order to improve the experience and take more control over
the presentation. This is good for users, but tough for user agents to
integrate into their password managers. It would be nice if a website
could help the user agent make sense of the sign-in mechanism they
choose to use.
* Finally, changing passwords is less well-supported than it could be if
the website explicitly informed the user agent that credentials had
changed.
</section>
<!--
██████ ███████ ████████ ████████ ███ ████████ ████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ████████ ██████ ██ ██ ████████ ██
██ ██ ██ ██ ██ ██ █████████ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██████ ███████ ██ ██ ████████ ██ ██ ██ ████
-->
<section>
# Core API # {#core}
From a developer's perspective, a <dfn export id="concept-credential">credential</dfn> is an
object which allows a developer to make an authentication decision for a particular action. This
section defines a generic and extensible {{Credential}} interface which serves as a base class
for [=credentials=] defined in this and other documents, along with a set of APIs
hanging off of {{CredentialsContainer|navigator.credentials.*}} which enable developers to
obtain them.
Various types of [=credentials=] are represented to JavaScript as an interface which
[=interface/inherits=], either directly or indirectly, from the {{Credential}} interface. This
document defines two such interfaces, {{PasswordCredential}} and {{FederatedCredential}}.
A [=credential=] is <dfn export for="credential">effective</dfn> for a particular [=origin=] if it
is accepted as authentication on that origin. Even if a credential is effective at a particular
point in time, the UA can't assume that the same credential will be effective at any future time,
for a couple reasons:
1. A password credential may stop being effective if the account holder changes their password.
2. A credential made from a token received over SMS is likely to only be effective for a single
use.
Single-use [=credentials=] are generated by a <dfn export>credential source</dfn>, which could be
a private key, access to a federated account, the ability to receive SMS messages at a particular
phone number, or something else. Credential sources are not exposed to Javascript or explicitly
represented in this specification. To unify the model, we consider a password to be a credential
source on its own, which is simply copied to create password credentials.
Even though the UA can't assume that an effective credential will still be effective if used a
second time, or that a credential source that has generated an effective credential will be able
to generate a second effective credential in the future, the second is more likely than the first.
By recording (with {{CredentialsContainer/store()}}) which credentials have been effective in the
past, the UA has a better chance of [offering](#user-mediated-selection) effective credential
sources to the user in the future.
## Infrastructure ## {#core-infrastructure}
User agents MUST internally provide a <dfn export id="concept-credential-store">credential
store</dfn>, which is a vendor-specific, opaque storage mechanism to record which [=credentials=]
have been [=effective=]. It offers the following capabilities for [=credential=] access and
persistence:
1. <dfn for="credential store" abstract-op export>Store a credential</dfn> for later retrieval.
This accepts a [=credential=], and inserts it into the [=credential store=].
2. <dfn for="credential store" abstract-op export>Retrieve a list of credentials</dfn>. This
accepts an arbitrary filter, and returns a set of [=credentials=] that match the filter.
3. <dfn for="credential store" abstract-op export>Modify a credential</dfn>. This accepts a
[=credential=], and overwrites the state of an existing [=credential=] in the
[=credential store=].
Additionally, the [=credential store=] should maintain a <dfn for="origin">`prevent silent
access` flag</dfn> for [=origins=] (which is set to `true` unless otherwise specified).
An origin <dfn for="origin">requires user mediation</dfn> if its flag is set to `true`.
Note: The importance of user mediation is discussed in more detail in [[#user-mediation]].
Note: The [=credential store=] is an internal implementation detail of a user agent's
implementation of the API specified in this document, and is not exposed to the web directly.
More capabilities may be specified by other documents in support of specific [=credential=]
types.
This document depends on the Infra Standard for a number of foundational concepts used in its
algorithms and prose [[!INFRA]].
An [=environment settings object=] (|settings|) is <dfn noexport>same-origin with its
ancestors</dfn> if the following algorithm returns `true`:
1. If |settings| has no [=environment settings object/responsible browsing context=],
return `false`.
2. Let |origin| be |settings|' [=environment settings object/origin=].
3. Let |current| be |settings|' [=environment settings object/responsible browsing context=].
4. While |current| has a [=parent browsing context=]:
1. Set |current| to |current|'s [=parent browsing context=].
2. If |current|'s [=active document=]'s [=origin=] is not [=same origin=] with |origin|,
return `false`.
5. Return `true`.
## The `Credential` Interface ## {#the-credential-interface}
<pre class="idl">
[Exposed=Window, SecureContext]
interface Credential {
readonly attribute USVString id;
readonly attribute DOMString type;
};
</pre>
<div dfn-for="Credential">
: <dfn attribute>id</dfn>
:: The credential's identifier. The requirements for the identifier are distinct for each type
of [=credential=]. It might represent a username for username/password tuples, for example.
: <dfn attribute>type</dfn>
:: This attribute's getter returns the value of the object's [=interface object=]'s
{{[[type]]}} slot, which specifies the [=credential type=] represented by this object.
: <dfn attribute>\[[type]]</dfn>
:: The {{Credential}} [=interface object=] has an internal slot named `[[type]]`, which
unsurprisingly contains a string representing the <dfn>credential type</dfn>. The slot's value
is the empty string unless otherwise specified.
Note: The {{[[type]]}} slot's value will be the same for all credentials implementing a
particular interface, which means that developers can rely on `obj.type` returning a string
that unambiguously represents the specific kind of {{Credential}} they're dealing with.
: <dfn attribute>\[[discovery]]</dfn>
:: The {{Credential}} [=interface object=] has an internal slot named `[[discovery]]`,
representing the mechanism by which the user agent can collect credentials of
a given type. Its value is either
"<dfn enum-value for="Credential/[[discovery]]">`credential store`</dfn>" or
"<dfn enum-value for="Credential/[[discovery]]">`remote`</dfn>". The former value means that
all available credential information is stored in the user agent's [=credential store=],
while the latter means that the user agent can discover credentials outside of those
explicitly represented in the [=credential store=] via interaction with some external device
or service.
</div>
ISSUE: Talk to Tobie/Dominic about the [=interface object=] bits, here and in
[[#algorithm-request]], etc. I'm not sure I've gotten the terminology right. [=interface prototype
object=], maybe?
Some {{Credential}} objects are <dfn for="Credential">origin bound</dfn>: these contain an
internal slot named <dfn for="Credential" attribute>\[[origin]]</dfn>, which stores the [=origin=]
for which the {{Credential}} may be [=effective=].
### `Credential` Internal Methods ### {#credential-internal-methods}
The {{Credential}} [=interface object=] features several [=internal methods=] facilitating
retrieval and storage of {{Credential}} objects, with default "no-op" implementations
as specified in this section, below.
Unless otherwise specified, each [=interface object=] created for interfaces which [=interface/inherit=]
from {{Credential}} MUST provide implementations for at least one of these internal methods, overriding
{{Credential}}'s default implementations, as appropriate for the [=credential=] type. E.g.,
[[#passwordcredential-interface]], [[#federatedcredential-interface]], and [[WEBAUTHN]].
<h5 id="algorithm-collect-creds" algorithm>`[[CollectFromCredentialStore]]` internal method</h5>
<dfn for="Credential" method export>\[[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors)</dfn>
is called with an [=environment settings object/origin=], a {{CredentialRequestOptions}}, and a boolean which
is true iff the caller's [=environment settings object=] is [=same-origin with its ancestors=].
The algorithm returns a set of {{Credential}} objects from the user agent's [=credential store=] that
match the options provided. If no matching {{Credential}} objects are available, the
returned set will be empty.
{{Credential}}'s default implementation of
{{Credential/[[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors)}}:
<ol class="algorithm">
1. Return an empty set.
</ol>
<h5 id="algorithm-discover-creds" algorithm>`[[DiscoverFromExternalSource]]` internal method</h5>
<dfn for="Credential" method export>\[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)</dfn>
is called [=in parallel=] with an [=environment settings object/origin=], a {{CredentialRequestOptions}} object,
and a boolean which is true iff the caller's [=environment settings object=] is
[=same-origin with its ancestors=].
It returns a {{Credential}} if one can be
returned given the options provided, `null` if no credential is available, or an error if
discovery fails (for example, incorrect options could produce a {{TypeError}}). If this
kind of {{Credential}} is only [=effective=] for a single use or a limited time, this
method is responsible for generating new [=credentials=] using a [=credential source=].
{{Credential}}'s default implementation of
{{Credential/[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)}}:
<ol class="algorithm">
1. Return `null`.
</ol>
<h5 id="algorithm-store-cred" algorithm>`[[Store]]` internal method</h5>
<dfn for="Credential" method export>\[[Store]](credential, sameOriginWithAncestors)</dfn>
is called [=in parallel=] with a {{Credential}}, and a boolean which is true iff the caller's
[=environment settings object=] is [=same-origin with its ancestors=].
The algorithm returns once {{Credential}} is persisted to the [=credential store=].
{{Credential}}'s default implementation of {{Credential/[[Store]](credential, sameOriginWithAncestors)}}:
<ol class="algorithm">
1. Return `undefined`.
</ol>
<h5 id="algorithm-create-cred" algorithm>`[[Create]]` internal method</h5>
<dfn for="Credential" method export>\[[Create]](origin, options, sameOriginWithAncestors)</dfn>
is called [=in parallel=] with an [=environment settings object/origin=], a {{CredentialCreationOptions}},
and a boolean which is true iff the caller's
[=environment settings object=] is [=same-origin with its ancestors=].
The algorithm either:
* creates a {{Credential}}, or
* does not create a credential and returns `null`, or
* returns an error if creation fails due to exceptional situations
(for example, incorrect options could produce a {{TypeError}}).
When creating a {{Credential}}, it will return an algorithm that takes a [=global object=]
and returns an [=interface object=]
inheriting from {{Credential}}. This algorithm MUST be invoked from a [=task=].
Note: This algorithm's steps are defined on a per-[=credential type=] basis.
{{Credential}}'s default implementation of {{Credential/[[Create]](origin, options, sameOriginWithAncestors)}}:
<ol class="algorithm">
1. Return `null`.
</ol>
### `CredentialUserData` Mixin ### {#credentialuserdata-mixin}
Some {{Credential}} objects contain data which aims to give users a human-readable disambiguation
mechanism in the [=credential chooser=] by providing a friendly name and icon:
<pre class="idl">
[SecureContext]
interface mixin CredentialUserData {
readonly attribute USVString name;
readonly attribute USVString iconURL;
};
</pre>
<div dfn-for="CredentialUserData">
: <dfn attribute>name</dfn>
:: A name associated with the credential, intended as a human-understandable public name for
display in a [=credential chooser=].
: <dfn attribute>iconURL</dfn>
:: A URL pointing to an image for the credential, intended for display in a
[=credential chooser=]. This URL MUST be an <a><i lang="la">a priori</i> authenticated
URL</a>.
</div>
## `navigator.credentials` ## {#framework-credential-management}
Developers retrieve {{Credential}}s and interact with the user agent's [=credential store=] via
methods exposed on the
{{CredentialsContainer}} interface, which hangs off the {{Navigator}} object as
<a attribute for="Navigator" lt="credentials">`navigator.credentials`</a>.
<pre class="idl">
partial interface Navigator {
[SecureContext, SameObject] readonly attribute CredentialsContainer credentials;
};
</pre>
The <dfn for="Navigator" attribute>`credentials`</dfn> attribute MUST return the
{{CredentialsContainer}} associated with the [=context object=].
Note: As discussed in [[#insecure-sites]], the credential management API is exposed only in
[=Secure Contexts=].
<pre class="idl">
[Exposed=Window, SecureContext]
interface CredentialsContainer {
Promise<Credential?> get(optional CredentialRequestOptions options = {});
Promise<Credential> store(Credential credential);
Promise<Credential?> create(optional CredentialCreationOptions options = {});
Promise<void> preventSilentAccess();
};
dictionary CredentialData {
required USVString id;
};
</pre>
<div dfn-for="CredentialsContainer">
: <dfn method>get(options)</dfn>
:: When {{CredentialsContainer/get()}} is called, the user agent MUST return the result of
executing <a abstract-op>Request a `Credential`</a> on
{{CredentialsContainer/get(options)/options}}.
<pre class="argumentdef" for="CredentialsContainer/get(options)">
options: The set of properties governing the scope of the request.
</pre>
: <dfn method lt="store(credential)|store()">store(credential)</dfn>
:: When {{CredentialsContainer/store()}} is called, the user agent MUST return the result of
executing <a abstract-op>Store a `Credential`</a> on
{{CredentialsContainer/store(credential)/credential}}.
<pre class="argumentdef" for="CredentialsContainer/store(credential)">
credential: The credential to be stored.
</pre>
: <dfn method lt="create(options)|create()">create(options)</dfn>
:: When {{CredentialsContainer/create()}} is called, the user agent MUST return the result of
executing <a abstract-op>Create a `Credential`</a> on
{{CredentialsContainer/create(options)/options}}.
<pre class="argumentdef" for="CredentialsContainer/create(options)">
options: The options used to create a `Credential`.
</pre>
: <dfn method>preventSilentAccess()</dfn>
:: When {{CredentialsContainer/preventSilentAccess()}} is called, the user agent MUST return
the result of executing <a abstract-op>Prevent Silent Access</a> on the <a>current settings
object</a>.
Note: The intent here is a signal from the origin that the user has signed out. That
is, after a click on a "Sign out" button, the site updates the user's session info, and
calls `navigator.credentials.preventSilentAccess()`. This sets the <a>`prevent silent
access` flag</a>, meaning that credentials will not be automagically handed back to the
page next time the user visits.
Note: This function was previously called `requireUserMediation()` which should be considered
deprecated.
</div>
<div algorithm="create credentialscontainer">
When a {{Navigator}} object (|navigator|) is created, the user agent MUST create a
new {{CredentialsContainer}} object, using |navigator|'s [=relevant Realm=], and associate it
with |navigator|.
</div>
### The `CredentialRequestOptions` Dictionary ### {#credentialrequestoptions-dictionary}
In order to retrieve a {{Credential}} via {{CredentialsContainer/get()}}, the caller specifies a
few parameters in a {{CredentialRequestOptions}} object.
Note: The {{CredentialRequestOptions}} dictionary is an extension point. If and when new types of
credentials are introduced that require options, their dictionary types will be added to the
dictionary so they can be passed into the request. See [[#implementation-extension]].
<pre class="idl">
dictionary CredentialRequestOptions {
CredentialMediationRequirement mediation = "optional";
AbortSignal signal;
};
</pre>
<div dfn-for="CredentialRequestOptions" dfn-type="dict-member">
: <dfn>mediation</dfn>
:: This property specifies the mediation requirements for a given credential request. The
meaning of each enum value is described below in {{CredentialMediationRequirement}}.
Processing details are defined in [[#algorithm-request]].
: <dfn>signal</dfn>
:: This property lets the developer abort an ongoing {{CredentialsContainer/get()}} operation.
An aborted operation may complete normally (generally if the abort was received after the
operation finished) or reject with an "{{AbortError}}" {{DOMException}}."
</div>
<div class="note">
Earlier versions of this specification defined a boolean `unmediated` member. Setting that
to `true` had the effect of setting {{CredentialRequestOptions/mediation}} to
"{{CredentialMediationRequirement/silent}}", and setting it to `false` had the effect of
setting {{CredentialRequestOptions/mediation}} to "{{CredentialMediationRequirement/optional}}".
`unmediated` should be considered deprecated; new code should instead rely on
{{CredentialRequestOptions/mediation}}.
</div>
<div algorithm="relevant credential interfaces">
The <dfn for="CredentialRequestOptions">relevant credential interface objects</dfn> for a given
{{CredentialRequestOptions}} (|options|) is a set of [=interface objects=], collected as follows:
<ol class="algorithm">
1. Let |settings| be the <a>current settings object</a>
2. Let |interface objects| be the set of [=interface objects=] on |settings|'
[=environment settings object/global object=].
3. Let |relevant interface objects| be an empty set.
4. For each |object| in |interface objects|:
1. If |object|'s [=inherited interfaces=] do not <a for="set">contain</a> {{Credential}},
<a for="iteration">continue</a>.
2. Let |key| be |object|'s {{Credential/[[type]]}} slot's value.
3. If |options|[|key|] <a for="map">exists</a>, <a for="set">append</a> |object| to
|relevant interface objects|.
</ol>
ISSUE: jyasskin@ suggests replacing the iteration through the interface objects with a registry.
I'm not sure which is less clear, honestly. I'll leave it like this for the moment, and we can
argue about whether this is too much of a `COMEFROM` interface.
</div>
<div algorithm="can collect locally">
A given {{CredentialRequestOptions}} |options| is
<dfn for="CredentialRequestOptions">matchable <i lang="la">a priori</i></dfn> if the following
steps return `true`:
<ol class="algorithm">
1. For each |interface| in |options|' [=relevant credential interface objects=]:
1. If |interface|'s {{Credential/[[discovery]]}} slot's value is not
"{{Credential/[[discovery]]/credential store}}", return `false`.
2. Return `true`.
</ol>
Note: When executing {{get(options)}}, we only return credentials without [=user mediation=] if
the provided {{CredentialRequestOptions}} is <a>matchable <i lang="la">a priori</i></a>. If any
credential types are requested that could require discovery from some external service (OAuth
tokens, security key authenticators, etc.), then [=user mediation=] will be required in order
to guide the discovery process (by choosing a federated identity provider, BTLE device, etc).
</div>
### Mediation Requirements ### {#mediation-requirements}
When making a request via {{get(options)}}, developers can set a case-by-case requirement for
[=user mediation=] by choosing the appropriate {{CredentialMediationRequirement}} enum value.
Note: The [[#user-mediation]] section gives more detail on the concept in general, and its
implications on how the user agent deals with individual requests for a given origin).
<pre class="idl">
enum CredentialMediationRequirement {
"silent",
"optional",
"required"
};
</pre>
<div dfn-for="CredentialMediationRequirement" dfn-type="enum-value">
: <dfn>silent</dfn>
:: User mediation is suppressed for the given operation. If the operation can be performed
without user involvement, wonderful. If user involvement is necessary, then the operation
will return `null` rather than involving the user.
Note: The intended usage is to support ["Keep me signed-into this site"
scenarios](#example-mediation-silent), where a developer may wish to silently obtain
credentials if a user should be automatically signed in, but to delay bothering the user
with a sign-in prompt until they actively choose to sign-in.
: <dfn>optional</dfn>
:: If credentials can be handed over for a given operation without user mediation, they will
be. If [=requires user mediation|user mediation is required=], then the user agent will
involve the user in the decision.
Note: This is the default behavior for {{get()}}, and is intended to serve a case where a
developer has reasonable confidence that a user expects to start a sign-in operation. If
a user has just clicked "sign-in" for example, then they won't be surprised or confused to
see a [=credential chooser=] if necessary.
: <dfn>required</dfn>
:: The user agent will not hand over credentials without [=user mediation=], even if the
[=prevent silent access flag=] is unset for an origin.
Note: This requirement is intended to support [reauthentication](#example-mediation-require)
or [user-switching](#example-mediation-switch) scenarios. Further, the requirement is tied
to a specific operation, and does not affect the [=prevent silent access flag=] for the
origin. To set that flag, developers should call {{preventSilentAccess()}}.
</div>
#### Examples #### {#mediation-examples}
<div class="example" id="example-mediation-silent">
MegaCorp, Inc. wishes to seamlessly sign in users when possible. They can do so by calling
{{get()}} for all non-signed in users at some convinient point while a landing page is loading,
passing in a {{CredentialRequestOptions/mediation}} member set to
"{{CredentialMediationRequirement/silent}}". This ensures that users who have opted-into
dropping the requirements for user mediation (as described in [[#user-mediation-requirement]])
are signed in, and users who haven't opted-into such behavior won't be bothered with a confusing
[=credential chooser=] popping up without context:
<pre>
window.addEventListener('load', async () => {
const credentials = await navigator.<a for="Navigator" attribute>credentials</a>.<a for="CredentialsContainer" method lt="get()">get</a>({
...,
<a for="CredentialRequestOptions" dict-member>mediation</a>: '<a for="CredentialMediationRequirement" enum-value>silent</a>'
});
if (credentials) {
// Hooray! Let's sign the user in using these credentials!
}
});
</pre>
</div>
<div class="example" id="example-mediation-optional">
When a user clicks "Sign In", MegaCorp, Inc. wishes to give them the smoothest possible
experience. If they have [[#user-mediation-requirement|opted-into]] signing in without
[=user mediation=], and the user agent can unambigiously choose a credential, great! If
not, a [=credential chooser=] will be presented.
<pre>
document.querySelector('#sign-in').addEventListener('click', async () => {
const credentials = await navigator.<a for="Navigator" attribute>credentials</a>.<a for="CredentialsContainer" method lt="get()">get</a>({
...,
<a for="CredentialRequestOptions" dict-member>mediation</a>: '<a for="CredentialMediationRequirement" enum-value>optional</a>'
});
if (credentials) {
// Hooray! Let's sign the user in using these credentials!
}
});
</pre>
Note: MegaCorp, Inc. could also have left off the {{CredentialRequestOptions/mediation}}
member entirely, as "{{CredentialMediationRequirement/optional}}" is its default.
</div>
<div class="example" id="example-mediation-require">
MegaCorp, Inc. wishes to protect a sensitive operation by requiring a user to reauthenticate
before taking action. Even if a user has [[#user-mediation-requirement|opted-into]] signing in
without [=user mediation=], MegaCorp, Inc. can ensure that the user agent requires mediation
by calling {{get()}} with a {{CredentialRequestOptions/mediation}} member set to
"{{CredentialMediationRequirement/required}}":
Note: Depending on the security model of the browser or the credential type, this may require
the user to authenticate themselves in some way, perhaps by entering a master password, scanning
a fingerprint, etc. before a credential is handed to the website.
<pre>
document.querySelector('#important-form').addEventListener('submit', async () => {
const credentials = await navigator.<a for="Navigator" attribute>credentials</a>.<a for="CredentialsContainer" method lt="get()">get</a>({
...,
<a for="CredentialRequestOptions" dict-member>mediation</a>: '<a for="CredentialMediationRequirement" enum-value>required</a>'
});
if (credentials) {
// Verify that |credentials| enables access, and cancel the submission
// if it doesn't.
} else {
e.preventDefault();
}
});
</pre>
</div>
<div class="example" id="example-mediation-switch">
MegaCorp, Inc. wishes to support signing into multiple user accounts at once. In order to ensure
that the user gets a chance to select a different credential, MegaCorp, Inc. can call {{get()}}
with a {{CredentialRequestOptions/mediation}} member set to
"{{CredentialMediationRequirement/required}}" in order to ensure that that credentials aren't
returned automatically in response to clicking on an "Add account" button:
<pre>
document.querySelector('#switch-button').addEventListener('click', e => {
var c = await navigator.<a for="Navigator" attribute>credentials</a>.<a for="CredentialsContainer" method lt="get()">get</a>({
...,
<a for="CredentialRequestOptions" dict-member>mediation</a>: '<a for="CredentialMediationRequirement" enum-value>required</a>'
});
if (c) {
// Sign the user in using |c|.
}
});
</pre>
</div>
## The `CredentialCreationOptions` Dictionary ## {#credentialcreationoptions-dictionary}
In order to create a {{Credential}} via {{CredentialsContainer/create()}}, the caller specifies a
few parameters in a {{CredentialCreationOptions}} object.
Note: The {{CredentialCreationOptions}} dictionary is an extension point. If and when new types of
credentials are introduced, they will add to the dictionary so they can be passed into the
creation method. See [[#implementation-extension]], and the extensions introduced in this document:
[[#passwordcredential-interface]] and [[#federatedcredential-interface]].
<pre class="idl">
dictionary CredentialCreationOptions {
AbortSignal signal;
};
</pre>
<div dfn-for="CredentialCreationOptions" dfn-type="dict-member">
: <dfn>signal</dfn>
:: This property lets the developer abort an ongoing {{CredentialsContainer/create()}}
operation. An aborted operation may complete normally (generally if the abort was received
after the operation finished) or reject with an "{{AbortError}}" {{DOMException}}."
</div>
## Algorithms ## {#algorithms}
<h4 id="algorithm-request" algorithm>Request a `Credential`</h4>
The <dfn abstract-op>Request a `Credential`</dfn> algorithm accepts a {{CredentialRequestOptions}}
(|options|), and returns a {{Promise}} that resolves with a {{Credential}} if one can be
unambigiously obtained, or with `null` if not.
<ol class="algorithm">
1. Let |settings| be the <a>current settings object</a>
2. Assert: |settings| is a [=secure context=].
3. If <code>|options|.{{CredentialRequestOptions/signal}}</code>'s [=AbortSignal/aborted flag=]
is set, then return [=a promise rejected with=] an "{{AbortError}}" {{DOMException}}.
4. Let |p| be [=a new promise=].
5. Let |origin| be the [=current settings object=]'s [=environment settings object/origin=].
6. Let |sameOriginWithAncestors| be `true` if |settings| is [=same-origin with its
ancestors=], and `false` otherwise.
7. Run the following steps [=in parallel=]:
1. Let |credentials| be the result of <a abstract-op lt="collect local">collecting
`Credential`s from the credential store</a>, given |origin|, |options|, and
|sameOriginWithAncestors|.
2. If |credentials| is an [=exception=], [=reject=] |p| with |credentials|.
3. If all of the following statements are true, resolve |p| with |credentials|[0] and
skip the remaining steps:
1. |credentials|' [=set/size=] is 1
2. |origin| does not [=origin/requires user mediation|require user mediation=]
3. |options| is <a>matchable <i lang="la">a priori</i></a>.
4. |options|.{{CredentialRequestOptions/mediation}} is not
"{{CredentialMediationRequirement/required}}".
ISSUE: This might be the wrong model. It would be nice to support a site that wished
to accept either username/passwords or webauthn-style credentials without forcing
a chooser for those users who use the former, and who wish to remain signed in.
4. If |options|' {{CredentialRequestOptions/mediation}} is
"{{CredentialMediationRequirement/silent}}", [=resolve=] |p| with `null`, and skip
the remaining steps.
5. Let |choice| be the result of <a abstract-op lt="ask to choose">asking the user to
choose a `Credential`</a>, given |options| and |credentials|.
6. If |choice| is `null` or a {{Credential}}, [=resolve=] |p| with |choice| and skip the
remaining steps.
7. Assert: |choice| is an [=interface object=].
8. Let |result| be the result of executing |choice|'s
{{[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)}},
given |origin|, |options|, and |sameOriginWithAncestors|.
9. If |result| is a {{Credential}} or `null`, resolve |p| with |result|.
Otherwise, [=reject=] |p| with |result|.
7. Return |p|.
</ol>
<h4 id="algorithm-collect-known" algorithm>Collect `Credential`s from the credential store</h4>
Given an [=environment settings object/origin=] (|origin|),
a {{CredentialRequestOptions}} (|options|), and a boolean which is `true` iff the calling
context is [=same-origin with its ancestors=] (|sameOriginWithAncestors|), the user agent may
<dfn abstract-op local-lt="collect local">collect `Credential`s from the credential store</dfn>,
returning a set of {{Credential}} objects stored by the user agent locally that match |options|'
filter. If no such {{Credential}} objects are known, the returned set will be empty:
<ol class="algorithm">
1. Let |possible matches| be an empty set.
2. For each |interface| in |options|' <a>relevant credential interface objects</a>:
1. Let |r| be the result of executing |interface|'s
{{Credential/[[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors)}} internal method on
|origin|, |options|, and |sameOriginWithAncestors|.
2. If |r| is an [=exception=], return |r|.
3. Assert: |r| is a list of [=interface objects=].
4. For each |c| in |r|:
1. <a for="set">Append</a> |c| to |possible matches|.
3. Return |possible matches|.
</ol>
<h4 id="algorithm-store" algorithm>Store a `Credential`</h4>
The <dfn abstract-op>Store a `Credential`</dfn> algorithm accepts a {{Credential}}
(|credential|), and returns a {{Promise}} which resolves once the object is persisted to the
[=credential store=].
<ol class="algorithm">
1. Let |settings| be the <a>current settings object</a>
2. Assert: |settings| is a [=secure context=].
3. Let |sameOriginWithAncestors| be `true` if the [=current settings object=] is [=same-origin
with its ancestors=], and `false` otherwise.
4. Let |p| be [=a new promise=].
5. Run the following steps [=in parallel=]:
1. Let |r| be the result of executing |credential|'s [=interface object=]'s
{{Credential/[[Store]](credential, sameOriginWithAncestors)}} internal method on
|credential| and |sameOriginWithAncestors|.
2. If |r| is an [=exception=], [=reject=] |p| with |r|.
Otherwise, [=resolve=] |p| with |r|.
6. Return |p|.
</ol>
<h4 id="algorithm-create" algorithm>Create a `Credential`</h4>
The <dfn abstract-op>Create a `Credential`</dfn> algorithm accepts a {{CredentialCreationOptions}}
(|options|), and returns a {{Promise}} which resolves with a {{Credential}} if one can be created
using the options provided, or `null` if no {{Credential}} can be created. In exceptional
circumstances, the {{Promise}} may reject with an appropriate exception:
<ol class="algorithm">
1. Let |settings| be the <a>current settings object</a>.
2. Assert: |settings| is a [=secure context=].
3. Let |global| be |settings|' [=environment settings object/global object=].
4. Let |sameOriginWithAncestors| be `true` if the [=current settings object=] is [=same-origin
with its ancestors=], and `false` otherwise.
5. Let |interfaces| be the [=set=] of |options|' <a>relevant credential interface objects</a>.
6. Return [=a promise rejected with=] `NotSupportedError` if any of the following statements
are true:
1. |settings| does not have a [=environment settings object/responsible document=].
2. |interfaces|' [=list/size=] is greater than 1.
Note: It may be reasonable at some point in the future to loosen this restriction, and
allow the user agent to help the user choose among one of many potential credential
types in order to support a "sign-up" use case. For the moment, though, we're punting
on that by restricting the dictionary to a single entry.
7. If <code>|options|.{{CredentialCreationOptions/signal}}</code>'s [=AbortSignal/aborted
flag=] is set, then return [=a promise rejected with=] an "{{AbortError}}" {{DOMException}}.
8. Let |p| be [=a new promise=].
9. Let |origin| be |settings|'s [=environment settings object/origin=].
10. Run the following steps [=in parallel=]:
1. Let |r| be the result of executing |interfaces|[0]'s
{{Credential/[[Create]](origin, options, sameOriginWithAncestors)}} internal method on
|origin|, |options|, and |sameOriginWithAncestors|.
2. If |r| is an [=exception=], [=reject=] |p| with |r|, and terminate these substeps.
3. If |r| is a {{Credential}} or `null`, [=resolve=] |p| with |r|, and terminate these substeps.
4. Assert: |r| is a algorithm (as defined in [[#algorithm-create-cred]]).
5. [=Queue a task=] on |global|'s [=DOM manipulation task source=] to run the following substeps:
1. [=Resolve=] |p| with the result of [=promise-calling=] |r| given |global|.
11. Return |p|.
</ol>
<h4 id="algorithm-prevent-silent-access" algorithm>Prevent Silent Access</h4>
The <dfn abstract-op>Prevent Silent Access</dfn> algorithm accepts an [=environment settings
object=] (|settings|), and returns a {{Promise}} which resolves once the `prevent silent access`
flag is persisted to the [=credential store=].
<ol class="algorithm">
1. Let |origin| be |settings|' [=environment settings object/origin=].
2. Let |p| be [=a new promise=]
3. Run the following seps [=in parallel=]:
1. Set |origin|'s <a>`prevent silent access` flag</a> in the [=credential store=].
2. [=Resolve=] |p| with `undefined`.
4. Retun |p|.
</ol>
</section>
<!--
████████ ███ ██████ ██████ ██ ██ ███████ ████████ ████████ ██████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
████████ ██ ██ ██████ ██████ ██ ██ ██ ██ ██ ████████ ██ ██ ██████
██ █████████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
██ ██ ██ ██████ ██████ ███ ███ ███████ ██ ██ ████████ ██████
-->
<section>
# Password Credentials # {#passwords}
For good or for ill, many websites rely on username/password pairs as an authentication mechanism.
The {{PasswordCredential}} interface is a [=credential=] meant to enable this use case, storing
both a username and password, as well as metadata that can help a user choose the right account
from within a [=credential chooser=].
## Examples ## {#password-examples}
### Password-based Sign-in ### {#examples-password-signin}
<div class="example">
MegaCorp, Inc. supports passwords, and can use {{get()|navigator.credentials.get()}} to obtain
username/password pairs from a user's [=credential store=]:
<pre>
navigator.<a attribute>credentials</a>
.<a idl lt="get()" for="CredentialsContainer">get</a>({ '<a for="CredentialRequestOptions" dict-member>password</a>': true })
.then(credential => {
if (!credential) {
// The user either doesn't have credentials for this site, or
// refused to share them. Insert some code here to fall back to
// a basic login form.
return;
}
if (credential.<a attribute for="Credential">type</a> == '<a const>password</a>') {
var form = new FormData();
form.append('username_field', credential.id);
form.append('password_field', credential.password);
var opt = {
method: 'POST',
body: form,
credentials: 'include' // Send cookies.
};
fetch('https://example.com/loginEndpoint', opt)
.then(function (response) {
if (/* |response| indicates a successful login */) {
// Record that the credential was effective. See note below.