1. Password Credentials
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.
1.1. Examples
1.1.1. Password-based Sign-in
navigator.credentials.get()
to obtain
username/password pairs from a user’s credential store:
navigator.credentials .get({ 'password': 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.type == 'password') { 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. navigator.credentials.store(credential); // Notify the user that sign-in succeeded! Do amazing, signed-in things! // Maybe navigate to a landing page via location.href = // '/signed-in-experience'? } else { // Insert some code here to fall back to a basic login form. } }); } });
Alternatively, the website could just copy the credential data into a form
and call submit()
on the form:
navigator.credentials .get({ 'password': true }) .then(credential => { if (!credential) { return; // as above... } if (credential.type === 'password') { document.querySelector('input[name=username_field]').value = credential.id; document.querySelector('input[name=password_field]').value = credential.password; document.getElementById('myform').submit(); } });
Note that the former method is much preferred, as it contains an explicit call
to store()
and saves the credentials. The form
based mechanism
relies on form submission, which navigates the browsing context, making it difficult to
ensure that store()
is called after successful sign-in.
Note: The credential chooser presented by the user agent could allow the user to choose
credentials that aren’t actually stored for the current origin. For instance, it might offer up
credentials from https://m.example.com
when signing into https://www.example.com
(as
described in Credential Management 1 § 6.1 Cross-domain credential access), or it might allow a user to create a new
credential on the fly. Developers can deal gracefully with this uncertainty by calling store()
every time credentials are successfully used, even right after
credentials have been retrieved from get()
: if the credentials aren’t
yet stored for the origin, the user will be given the opportunity to do so. If they are stored,
the user won’t be prompted.
1.1.2. Post-sign-in Confirmation
To ensure that users are offered to store new credentials after a successful sign-in, they can
to be passed to store()
.
fetch()
, we can check the response to determine whether the user
was signed in successfully, and notify the user agent accordingly. Given a sign-in form like the
following:
<form action="https://example.com/login" method="POST" id="theForm"> <label for="username">Username</label> <input type="text" id="username" name="username" autocomplete="username"> <label for="password">Password</label> <input type="password" id="password" name="password" autocomplete="current-password"> <input type="submit"> </form>
Then the developer can handle the form submission with something like the following handler:
document.querySelector('#theForm').addEventListener('submit', e => { if (window.PasswordCredential) { e.preventDefault(); // Construct a new PasswordCredential from the HTMLFormElement // that fired the "submit" event: this will suck up the values of the fields // labeled with "username" and "current-password" autocomplete // attributes: var c = new PasswordCredential(e.target); // Fetch the form’s action URL, passing that new credential object in // as a FormData object. If the response indicates success, tell the user agent // so it can ask the user to store the password for future use: var opt = { method: 'POST', body: new FormData(e.target), credentials: 'include' // Send cookies. }; fetch(e.target.action, opt).then(r => { if (/* |r| is a "successful" Response */) navigator.credentials.store(c); }); } });
1.1.3. Change Password
This same storage mechanism can be reused for "password change" with no modifications: if the user changes their credentials, the website can notify the user agent that they’ve successfully signed in with new credentials. The user agent can then update the credentials it stores:
store()
with the new information.
Given a password change form like the following:
<form action="https://example.com/changePassword" method="POST" id="theForm"> <input type="hidden" name="username" autocomplete="username" value="user"> <label for="password">New Password</label> <input type="password" id="password" name="password" autocomplete="new-password"> <input type="submit"> </form>
The developer can handle the form submission with something like the following:
document.querySelector('#theForm').addEventListener('submit', e => { if (window.PasswordCredential) { e.preventDefault(); // Construct a new PasswordCredential from the HTMLFormElement // that fired the "submit" event: this will suck up the values of the fields // labeled with "username" and "new-password" autocomplete // attributes: var c = new PasswordCredential(e.target); // Fetch the form’s action URL, passing that new credential object in // as a FormData object. If the response indicates success, tell the user agent // so it can ask the user to store the password for future use: var opt = { method: 'POST', body: new FormData(e.target), credentials: 'include' // Send cookies. }; fetch(e.target.action, opt).then(r => { if (/* |r| is a "successful" Response */) navigator.credentials.store(c); }); } });
1.2. The PasswordCredential
Interface
[Exposed =Window ,SecureContext ]interface :
PasswordCredential Credential {constructor (HTMLFormElement );
form constructor (PasswordCredentialData );
data readonly attribute USVString password ; };PasswordCredential includes CredentialUserData ;partial dictionary CredentialRequestOptions {boolean =
password false ; };
password
, of type USVString, readonly-
This attribute represents the password of the credential.
[[type]]
-
The
PasswordCredential
interface object has an internal slot named[[type]]
whose value is "password
". [[discovery]]
-
The
PasswordCredential
interface object has an internal slot named[[discovery]]
whose value is "credential store
". PasswordCredential(form)
-
This constructor accepts an
HTMLFormElement
(form), and runs the following steps:-
Let origin be the current settings object's origin.
-
Let r be the result of executing Create a
PasswordCredential
from anHTMLFormElement
given form and origin. -
If r is an exception, throw r.
Otherwise, return r.
-
PasswordCredential(data)
-
This constructor accepts a
PasswordCredentialData
(data), and runs the following steps:-
Let r be the result of executing Create a
PasswordCredential
fromPasswordCredentialData
on data. -
If r is an exception, throw r.
Otherwise, return r.
-
PasswordCredential
objects can be created via navigator.credentials.create()
either explicitly by passing in a PasswordCredentialData
dictionary, or based on the contents
of an HTMLFormElement
's submittable elements.
dictionary :
PasswordCredentialData CredentialData {USVString ;
name USVString ;
iconURL required USVString ;
origin required USVString ; };
password typedef (PasswordCredentialData or HTMLFormElement );
PasswordCredentialInit partial dictionary CredentialCreationOptions {PasswordCredentialInit ; };
password
PasswordCredential
objects are origin bound.
PasswordCredential
's interface object inherits Credential
's implementation of [[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)
, and defines
its own implementation of [[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors)
, [[Create]](origin, options, sameOriginWithAncestors)
, and [[Store]](credential, sameOriginWithAncestors)
.
1.3. Algorithms
1.3.1. PasswordCredential
’s [[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors)
[[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors)
is called with an origin (origin), a CredentialRequestOptions
(options),
and a boolean which is true
if and only if the calling context is same-origin with its ancestors (sameOriginWithAncestors).
The algorithm returns a set of Credential
objects from
the credential store. If no matching Credential
objects are available, the returned set
will be empty.
The algorithm will throw a NotAllowedError
if sameOriginWithAncestors is not true
.
-
If sameOriginWithAncestors is
false
, throw a "NotAllowedError
"DOMException
.Note: This restriction aims to address the concern raised in Credential Management 1 § 6.4 Origin Confusion.
-
Return the empty set if options["
password
"] is nottrue
. -
Return the result of retrieving credentials from the credential store that match the following filter:
-
The credential is a
PasswordCredential
-
The credential’s
[[origin]]
is the same origin as origin.
-
1.3.2. PasswordCredential
’s [[Create]](origin, options, sameOriginWithAncestors)
[[Create]](origin, options, sameOriginWithAncestors)
is called with an origin (origin), a CredentialCreationOptions
(options), and a boolean which is true
if and only if the calling
context is same-origin with its ancestors (sameOriginWithAncestors).
The algorithm returns a PasswordCredential
if one can be created, null
otherwise. The CredentialCreationOptions
dictionary must have a password
member which
holds either an HTMLFormElement
or a PasswordCredentialData
. If that member’s value cannot be
used to create a PasswordCredential
, this algorithm will throw a TypeError
exception.
-
Assert: options["
password
"] exists, and sameOriginWithAncestors is unused. -
If options["
password
"] is anHTMLFormElement
, return the result of executing Create aPasswordCredential
from anHTMLFormElement
given options["password
"] and origin. Rethrow any exceptions. -
If options["
password
"] is aPasswordCredentialData
, return the result of executing Create aPasswordCredential
fromPasswordCredentialData
given options["password
"]. Rethrow any exceptions.
1.3.3. PasswordCredential
’s [[Store]](credential, sameOriginWithAncestors)
[[Store]](credential, sameOriginWithAncestors)
is
called with a PasswordCredential
(credential), and a boolean which is true
if and only if the calling
context is same-origin with its ancestors (sameOriginWithAncestors). The algorithm returns undefined
once credential is persisted to the credential store.
The algorithm will return a NotAllowedError
if sameOriginWithAncestors is not true
.
-
Throw a "
NotAllowedError
"DOMException
without altering the user agent’s credential store if sameOriginWithAncestors isfalse
.Note: This restriction aims to address the concern raised in Credential Management 1 § 6.4 Origin Confusion.
-
If the user agent’s credential store contains a
PasswordCredential
(stored) whoseid
attribute is credential’sid
and whose[[origin]]
slot is the same origin as credential’s[[origin]]
, then:-
If the user grants permission to update credentials (as discussed when defining user mediation), then:
Otherwise, if the user grants permission to store credentials (as discussed when defining user mediation, then:
-
Store a
PasswordCredential
in the credential store with the following properties:id
-
credential’s
id
name
,-
credential’s
name
iconURL
-
credential’s
iconURL
[[origin]]
-
credential’s
[[origin]]
password
-
credential’s
password
-
1.3.4. Create a PasswordCredential
from an HTMLFormElement
To Create a PasswordCredential
from an HTMLFormElement
, given an HTMLFormElement
(form) and an origin (origin), run these steps.
Note: § 1.1.2 Post-sign-in Confirmation and § 1.1.3 Change Password provide examples of the intended usage.
-
Let data be a new
PasswordCredentialData
dictionary. -
Set data’s
origin
member’s value to origin’s value. -
Let formData be the result of executing the
FormData
constructor on form. -
Let elements be a list of all the submittable elements whose form owner is form, in tree order.
-
Let newPasswordObserved be
false
. -
For each field in elements, run the following steps:
-
If field does not have an
autocomplete
attribute, then skip to the next field. -
Let name be the value of field’s
name
attribute. -
If formData’s
has()
method returnsfalse
when executed on name, then skip to the next field. -
If field’s
autocomplete
attribute’s value contains one or more autofill detail tokens (tokens), then:-
For each token in tokens:
-
If token is an ASCII case-insensitive match for one of the following strings, run the associated steps:
- "
new-password
" -
Set data’s
password
member’s value to the result of executing formData’sget()
method on name, and newPasswordObserved totrue
. - "
current-password
" -
If newPasswordObserved is
false
, set data’spassword
member’s value to the result of executing formData’sget()
method on name.Note: By checking that newPasswordObserved is
false
,new-password
fields take precedence overcurrent-password
fields. - "
photo
" -
Set data’s
iconURL
member’s value to the result of executing formData’sget()
method on name. - "
name
"- "
nickname
" - "
-
Set data’s
name
member’s value to the result of executing formData’sget()
method on name. - "
username
" -
Set data’s
id
member’s value to the result of executing formData’sget()
method on name.
- "
-
-
-
-
Let c be the result of executing Create a
PasswordCredential
fromPasswordCredentialData
on data. If that threw an exception, rethrow that exception. -
Assert: c is a
PasswordCredential
. -
Return c.
1.3.5. Create a PasswordCredential
from PasswordCredentialData
To Create a PasswordCredential
from PasswordCredentialData
, given an PasswordCredentialData
(data), run these steps.
-
Let c be a new
PasswordCredential
object. -
If any of the following are the empty string, throw a
TypeError
exception: -
Set c’s properties as follows:
-
Return c.
1.3.6. CredentialRequestOptions
Matching for PasswordCredential
Given a CredentialRequestOptions
(options), the following algorithm returns "Matches
" if
the PasswordCredential
should be available as a response to a get()
request, and "Does Not Match
" otherwise.
-
If options has a
password
member whose value istrue
, then return "Matches
". -
Return "
Does Not Match
".