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
PasswordCredentialinterface object has an internal slot named[[type]]whose value is "password". [[discovery]]-
The
PasswordCredentialinterface 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
PasswordCredentialfrom anHTMLFormElementgiven 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
PasswordCredentialfromPasswordCredentialDataon 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 aPasswordCredentialfrom anHTMLFormElementgiven options["password"] and origin. Rethrow any exceptions. -
If options["
password"] is aPasswordCredentialData, return the result of executing Create aPasswordCredentialfromPasswordCredentialDatagiven 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"DOMExceptionwithout 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) whoseidattribute is credential’sidand 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
PasswordCredentialin 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
PasswordCredentialDatadictionary. -
Set data’s
originmember’s value to origin’s value. -
Let formData be the result of executing the
FormDataconstructor 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
autocompleteattribute, then skip to the next field. -
Let name be the value of field’s
nameattribute. -
If formData’s
has()method returnsfalsewhen executed on name, then skip to the next field. -
If field’s
autocompleteattribute’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
passwordmember’s value to the result of executing formData’sget()method on name, and newPasswordObserved totrue. - "
current-password" -
If newPasswordObserved is
false, set data’spasswordmember’s value to the result of executing formData’sget()method on name.Note: By checking that newPasswordObserved is
false,new-passwordfields take precedence overcurrent-passwordfields. - "
photo" -
Set data’s
iconURLmember’s value to the result of executing formData’sget()method on name. - "
name"- "
nickname" - "
-
Set data’s
namemember’s value to the result of executing formData’sget()method on name. - "
username" -
Set data’s
idmember’s value to the result of executing formData’sget()method on name.
- "
-
-
-
-
Let c be the result of executing Create a
PasswordCredentialfromPasswordCredentialDataon 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
PasswordCredentialobject. -
If any of the following are the empty string, throw a
TypeErrorexception: -
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
passwordmember whose value istrue, then return "Matches". -
Return "
Does Not Match".