1. Introduction
This section is not normative.
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 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 §1.1 Use Cases and in Credential Management: Use Cases and Requirements; 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 right now, 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 §9 Future Work for some thoughts we’ve punted for now, but which could be explored in future iterations of this API.
1.1. 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 problematic:
- 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.
1.2. Examples
1.2.1. Password-based Sign-in
A website that supports only username/password pairs can request credentials, and use them in existing sign-in forms:
navigator.credentials.get({ "password": true }).then( function(credential) { if (!credential) { // The user either doesn’t have credentials for this site, or // refused to share them. Insert some code here to show a basic // login form (or, ideally, do nothing, since this API should // really be progressive enhancement on top of an existing form). return; } if (credential.type == "password") { fetch("https://example.com/loginEndpoint", { credentials: credential, method: "POST" }) .then(function (response) { // Notify the user that signin succeeded! Do amazing, signed-in things! }); } else { // See the Federated Sign-in example } });
1.2.2. Federated Sign-in
A website that supports federated identity providers as well as passwords can request credentials, and use them to kick off the sign-in flow for the user’s chosen provider:
navigator.credentials.get({ "password": true, "federated": { "providers": [ "https://federation.com" ] } }).then( function(credential) { if (!credential) return; if (credential.type == "federated") { switch (credential.provider) { case "https://www.facebook.com/": // Use Facebook’s SDK, a la // https://developers.facebook.com/docs/facebook-login/login-flow-for-web/#logindialog FB.login(function (response) { if (response.status === "authorized") { // Can now use FB.api() calls } else { // Explain to the user that we would really like them to // click "Log me in". } }); break; case "https://accounts.google.com/": // Ditto break; // ... } } else { fetch("https://example.com/loginEndpoint", { credentials: credential, method: "POST" }) .then(function (response) { ... }) .catch(function (response) { ... }); } });
Note: This API does not go out to the identity provider to grab a token, or authenticate the user in any way. It provides a hint to the website as to which identity provider the user prefers to use, and little more. See §9 Future Work for directions future versions of this API could take.
1.2.3. Post-sign-in Confirmation
To ensure that credentials are persisted correctly, it may be useful for a website to tell the credential manager that sign-in succeeded.
fetch()
on a PasswordCredential
object, submitting that data to a sign-in endpoint, then 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" autocomplete="username"> <label for="password">Password</label> <input type="text" id="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 (navigator.credentials) { 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 the fetch’s credentials. If the response indicates success, ask // the user agent to ask the user to store the credential for future use: var init = { method: "POST", credentials: c }; fetch(e.target.action, init).then(r => { if (/* |r| is a "successful" Response */) navigator.credentials.store(c); }); } });
if (navigator.credentials) { navigator.credentials.store( new FederatedCredential({ "id": "username", "provider": "https://federation.com" }) ); }
1.2.4. Change Password
This same storage mechanism can be reused for "password change" with no modifications: if the user changes her credentials, the website can notify the user agent that she’s 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" id="username" autocomplete="username" value="user"> <label for="password">New Password</label> <input type="text" id="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 (navigator.credentials) { 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 the fetch’s credentials. If the response indicates success, ask // the user agent to ask the user to store the credential for future use: var init = { method: "POST", credentials: c }; fetch(e.target.action, init).then(r => { if (/* |r| is a "successful" Response */) navigator.credentials.store(c); }); } });
1.2.5. Layering on top of a legacy system
The API is designed in such a way as to cleanly sit on top of an existing
password-based sign-in system’s backend. For instance, the username and
password parameters can be renamed by adjusting the PasswordCredential
's idName
and passwordName
attributes, and additional data can be added to a request by setting the additionalData
attribute.
u
" and a the password as a
parameter named "p
". The following code accomplishes this:
// Given a PasswordCredential |credential|: credential.idName = "u"; credential.passwordName = "p"; fetch("https://example.com/loginEndpoint", { credentials: credential, method: "POST" });
// Given a PasswordCredential |credential|: credential.additionalData = new FormData(); credential.additionalData.append("csrf", "[random token value goes here]"); fetch("https://example.com/loginEndpoint", { credentials: credential, method: "POST" });
multipart/form-data
. They need to submit the credential information as application/x-www-form-urlencoded
instead. They can accomplish this by
setting the additionalData
attribute to a URLSearchParams
object (which serializes as a urlencoded submission):
// Given a PasswordCredential |credential|: credential.additionalData = new URLSearchParams(); // Note that MegaCorp Inc. doesn’t need to append any data to the // object. SettingadditionalData
to an emptyURLSearchParams
object // is enough to change the content type of the submission. fetch("https://example.com/loginEndpoint", { credentials: credential, method: "POST" });
2. Key Concepts and Terminology
- Credentials#credentialsReferenced in:1. Introduction (2)3.1. Credential Types (2)
- Password Credentials#password-credentialsReferenced in:3.1. Credential Types
- Federated Credentials#federated-credentialsReferenced in:3.1. Credential Types
- Password Credentials#password-credentialsReferenced in:3.1. Credential Types
-
Broadly speaking, credential is an assertion about an
entity which enables a trust decision. This specification defines two
specific types of credentials which are useful for authentication:
"password" credentials, which consist of a username/password
pair, and "federated" credentials, which point out to a federated
identity provider which is trusted to correctly assert a user’s
authentication.
Note: These two types are, of course, not exhaustive. Future versions of this and other documents will likely define other types of credentials.
- Credential Store#credential-storeReferenced in:3.1. Credential Types4.1.2. Store a Credential 4.2.1. Gather SiteBoundCredentials (2)4.2.2. Request an SiteBoundCredential without user mediation 4.2.4. Store SiteBoundCredential (2) (3)4.3.1. Require user mediation for origin 5.2. Requiring User Mediation6.5. Script Injection7.2. Signing-Out7.4. Locally Stored Data
-
A credential store is an opaque storage mechanism which
offers a user agent the ability to:
-
Store, retrieve, and modify
Credential
objects. -
To mark an origin with a requires user mediation#requires-user-mediationReferenced in:4.2.2. Request an SiteBoundCredential without user mediation 4.3.1. Require user mediation for origin 5.2. Requiring User Mediation (2)7.2. Signing-Out flag.
-
To associate an origin with a protocol set#protocol-setReferenced in:7.4. Locally Stored Data.
The implementation is vendor-specific, and the interface provided is not exposed to the web.
Note: The types of credentials defined in this document are stored locally in a user agent’s credential store, but future versions of this and other documents will likely define credential types which are external to the user agent.
-
- Federated Identity Provider #federated-identity-providerReferenced in:1. Introduction1.2.2. Federated Sign-in1.2.3. Post-sign-in Confirmation2. Key Concepts and Terminology3.1.4. FederatedCredential (2)3.2.2. Identifying providers9. Future Work
- A federated identity provider is an entity which a website trusts to correctly authenticate a user, and which provides an API for that purpose. OpenID Connect is an example of such a framework, used by a number of providers.
3. Interfaces
3.1. Credential Types
This document defines a generic and extensible Credential
interface from
which all credentials will inherit, and a slightly less generic SiteBoundCredential
, which defines the specific attributes shared
by any Credential persisted in user agent’s credential store.
PasswordCredential
and FederatedCredential
both inherit from SiteBoundCredential
, and, as you might expect, are the concrete types
mapped to password credentials and federated credentials,
respectively.
3.1.1. Credential
dictionary CredentialData#dictdef-credentialdataReferenced in:3.1.1. Credential { USVString id#dom-credentialdata-idReferenced in:1.2.3. Post-sign-in Confirmation3.1.3.1. PasswordCredential(HTMLFormElement form) Constructor 3.1.3.2. PasswordCredential(PasswordCredentialData data) Constructor (2); }; interface Credential#credentialReferenced in:2. Key Concepts and Terminology3.1. Credential Types3.1.1. Credential (2) (3) (4) (5)3.2. Credential Manager (2) (3) (4) (5) (6)3.2.1. get() Parameters 3.2.1.1. CredentialRequestOptions dictionary (2)3.2.1.2. FederatedCredentialRequestOptions dictionary 4.1.1. Request a Credential (2) (3)4.1.2. Store a Credential 4.1.3. Clone credential (2)4.2.1. Gather SiteBoundCredentials 4.2.2. Request an SiteBoundCredential without user mediation (2) (3)4.2.3. Request a SiteBoundCredential with user mediation (2)5.2. Requiring User Mediation (2) (3)5.3. Credential Selection (2)6.1. Cross-origin Credential Leakage6.3. Origin Confusion7.3. Chooser Leakage (2) (3)7.4. Locally Stored Data8.2. Extension Points (2) { readonly attribute USVString id; readonly attribute DOMString type; }; Credential implements Transferable;
- id#dom-credential-idReferenced in:3.1.1. Credential3.1.3.2. PasswordCredential(PasswordCredentialData data) Constructor 3.3.2. Extract a body and Content-Type for request’s attached credential (2)4.2.4. Store SiteBoundCredential (2) (3) (4), of type USVString, readonly
- The credential’s identifier. This might be a GUID, username, or email address, for instance.
- type#dom-credential-typeReferenced in:3.1.1. Credential (2), of type DOMString, readonly
-
The credential’s type. This attribute’s getter returns the value of the
credential’s
[[type]]
slot.Note: The
[[type]]
slot’s value will be the same for all credentials implementing a particular interface. Currently,PasswordCredential
objects have a[[type]]
ofpassword
, andFederatedCredential
objects have a[[type]]
offederated
. - [[type]]#dom-credential-type-slotReferenced in:3.1.1. Credential (2) (3) (4) (5)3.1.3. PasswordCredential (2)3.1.3.2. PasswordCredential(PasswordCredentialData data) Constructor 3.1.4. FederatedCredential (2)8.2. Extension Points (2) (3)
- All
Credential
objects have an internal slot named[[type]]
, which unsurprisingly contains a string representing the type of the credential. This property is exposed to the web via thetype
attribute.
Credential
objects implement Transferable
, and MUST support the
the structured clone algorithm. Unless otherwise specified, the cloning
mechanism for all objects which implement Credential
is defined in §4.1.3 Clone credential.
dictionary SiteBoundCredentialData#dictdef-siteboundcredentialdataReferenced in:3.1.3. PasswordCredential3.1.4. FederatedCredential : CredentialData { USVString name#dom-siteboundcredentialdata-nameReferenced in:3.1.3.1. PasswordCredential(HTMLFormElement form) Constructor 3.1.3.2. PasswordCredential(PasswordCredentialData data) Constructor ; USVString iconURL#dom-siteboundcredentialdata-iconurlReferenced in:3.1.3.1. PasswordCredential(HTMLFormElement form) Constructor 3.1.3.2. PasswordCredential(PasswordCredentialData data) Constructor ; }; interface SiteBoundCredential#siteboundcredentialReferenced in:3.1. Credential Types (2)3.1.2. SiteBoundCredential (2)3.1.3. PasswordCredential3.1.4. FederatedCredential4.1.1. Request a Credential (2)4.1.2. Store a Credential (2)4.2.1. Gather SiteBoundCredentials 4.2.2. Request an SiteBoundCredential without user mediation 4.2.3. Request a SiteBoundCredential with user mediation 4.2.4. Store SiteBoundCredential : Credential { readonly attribute USVString name; readonly attribute USVString iconURL; };
3.1.2. SiteBoundCredential
- name#dom-siteboundcredential-nameReferenced in:3.1.1. Credential3.1.3.2. PasswordCredential(PasswordCredentialData data) Constructor 4.2.4. Store SiteBoundCredential (2), of type USVString, readonly
- A name associated with the credential, intended as a human-understandable public name.
- iconURL#dom-siteboundcredential-iconurlReferenced in:3.1.1. Credential3.1.3.2. PasswordCredential(PasswordCredentialData data) Constructor 4.2.4. Store SiteBoundCredential (2) (3) (4)7.3. Chooser Leakage, of type USVString, readonly
-
A URL pointing to an image for the credential. This URL MUST be an a priori authenticated URL.
Anne suggests that this might be better modeled as an
ImageBitmap
orblob:
. We also need to figure out responsiveness. Perhaps [MANIFEST]'s format? <https://github.com/w3c/webappsec/issues/247> - [[origin]]#dom-siteboundcredential-origin-slotReferenced in:3.1.2. SiteBoundCredential 4.2.1. Gather SiteBoundCredentials 4.2.4. Store SiteBoundCredential (2)4.3.2. Does credential match origin B? 6.1. Cross-origin Credential Leakage
- All
SiteBoundCredential
objects have an internal slot named[[origin]]
, which stores the origin to which the credential is bound. This property is not directly exposed to the web.
All SiteBoundCredential
objects MUST define an options matching
algorithm#options-matching-algorithmReferenced in:3.1.3.3.
Matching Algorithm 3.1.4.1.
Matching Algorithm 3.2. Credential Manager4.2.1.
Gather SiteBoundCredentials which returns Match
#matchReferenced in:3.1.3.3.
Matching Algorithm 3.1.4.1.
Matching Algorithm (2) if the object
matches a CredentialRequestOptions
object, and Does Not
Match
#does-not-matchReferenced in:3.1.4.1.
Matching Algorithm 4.2.1.
Gather SiteBoundCredentials otherwise.
3.1.3. PasswordCredential
dictionary PasswordCredentialData#dictdef-passwordcredentialdataReferenced in:3.1.3. PasswordCredential3.1.3.1. PasswordCredential(HTMLFormElement form) Constructor 3.1.3.2. PasswordCredential(PasswordCredentialData data) Constructor : SiteBoundCredentialData { USVString password#dom-passwordcredentialdata-passwordReferenced in:3.1.3.1. PasswordCredential(HTMLFormElement form) Constructor 3.1.3.2. PasswordCredential(PasswordCredentialData data) Constructor (2); }; typedef (FormData or URLSearchParams) CredentialBodyType#typedefdef-credentialbodytypeReferenced in:3.1.3. PasswordCredential (2); [Constructor(PasswordCredentialData data), Constructor(HTMLFormElement form), Exposed=Window] interface PasswordCredential#passwordcredentialReferenced in:1.2.3. Post-sign-in Confirmation (2)1.2.4. Change Password1.2.5. Layering on top of a legacy system (2) (3) (4)3.1. Credential Types3.1.1. Credential3.1.3. PasswordCredential (2) (3) (4)3.1.3.2. PasswordCredential(PasswordCredentialData data) Constructor 3.1.3.3. Matching Algorithm 3.2.1.1. CredentialRequestOptions dictionary 3.3. Integration with Fetch (2) (3) (4)3.3.1. Monkey Patches to Fetch (2) (3) (4)3.3.2. Extract a body and Content-Type for request’s attached credential 4.1.1. Request a Credential (2) (3)4.1.2. Store a Credential 4.2.4. Store SiteBoundCredential (2)6.1. Cross-origin Credential Leakage6.2. Same-origin Leakage : SiteBoundCredential { attribute USVString idName; attribute USVString passwordName; attribute CredentialBodyType? additionalData; };
-
idName#dom-passwordcredential-idnameReferenced in:1.2.5. Layering on top of a legacy system3.1.3. PasswordCredential3.1.3.1. PasswordCredential(HTMLFormElement form) Constructor 3.1.3.2. PasswordCredential(PasswordCredentialData data) Constructor 3.3.2. Extract a body and Content-Type for request’s attached credential (2) (3) (4), of type USVString
-
This attribute represents the name which will be used for the ID field when submitting the
PasswordCredential
to a remote endpoint viafetch()
. It defaults to "username
", but can be overridden by a developer to match whatever the backend service expects. -
passwordName#dom-passwordcredential-passwordnameReferenced in:1.2.5. Layering on top of a legacy system3.1.3. PasswordCredential3.1.3.1. PasswordCredential(HTMLFormElement form) Constructor 3.1.3.2. PasswordCredential(PasswordCredentialData data) Constructor 3.3.2. Extract a body and Content-Type for request’s attached credential (2) (3) (4), of type USVString
-
This attribute represents the name which will be used for the ID field when submitting the
PasswordCredential
to a remote endpoint viafetch()
. It defaults to "password
", but can be overridden by a developer to match whatever the backend service expects. -
additionalData#dom-passwordcredential-additionaldataReferenced in:1.2.5. Layering on top of a legacy system (2) (3) (4) (5)3.1.3. PasswordCredential3.1.3.1. PasswordCredential(HTMLFormElement form) Constructor (2)3.3.2. Extract a body and Content-Type for request’s attached credential , of type CredentialBodyType, nullable
-
If the developer wishes to specify additional data to insert into the request body when submitting the credential information to a remote endpoint, they can do so by assigning a
FormData
orURLSearchParams
object to this attribute. The credential information will be mixed into the object to produce the body. The value isnull
unless otherwise specified. -
All
PasswordCredential
objects have their[[type]]
slot’s value set to the string "password
#dom-passwordcredential-passwordReferenced in:1.2.1. Password-based Sign-in".
All PasswordCredential
objects have an internal slot named [[password]]#dom-passwordcredential-password-slotReferenced in:3.1.3.2.
PasswordCredential(PasswordCredentialData data) Constructor 3.3.2.
Extract a body and Content-Type for request’s attached credential (2)4.2.4.
Store SiteBoundCredential (2)6.1. Cross-origin Credential Leakage which stores the
credential’s password. This property is not directly exposed to the web, but
used to construct the request body during fetch()
.
3.1.3.1. PasswordCredential(HTMLFormElement form)
Constructor
The PasswordCredential(form)
#dom-passwordcredential-passwordcredentialReferenced in:1.2.3. Post-sign-in Confirmation1.2.4. Change Password3.1.3. PasswordCredential constructor, when invoked with an HTMLFormElement
(form), must run these
steps.
Note: §1.2.3 Post-sign-in Confirmation and §1.2.4 Change Password provide examples of the intended usage.
-
Let data be a new
PasswordCredentialData
dictionary. -
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 idName and passwordName be empty strings.
-
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 an autofill detail token that is an ASCII case-insensitive match for any of the following strings, then run the associated steps:-
-
Set data’s
password
member’s value to the result of executing formData’sget()
method on name, and passwordName to name. -
"
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, and idName to name.
-
-
-
Let c be the result of executing the
PasswordCredential(data)
constructor on data. Rethrow any exception generated. -
Set c’s
idName
attribute to idName. -
Set c’s
passwordName
attribute to passwordName. -
If form’s enctype is "
multipart/form-data
", then:-
Set c’s
additionalData
attribute to formData.
Otherwise:
-
Let params be a new
URLSearchParams
object. -
For each entry in formData’s entries:
-
Set c’s
additionalData
attribute to formData.
-
-
Return c.
3.1.3.2. PasswordCredential(PasswordCredentialData data)
Constructor
The PasswordCredential(data)
#dom-passwordcredential-passwordcredential-dataReferenced in:3.1.3. PasswordCredential3.1.3.1.
PasswordCredential(HTMLFormElement form) Constructor constructor, when invoked with a PasswordCredentialData
(data), must 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.
3.1.3.3. Matching Algorithm
PasswordCredential
objects' options matching algorithm always
returns Match
.
3.1.4. FederatedCredential
dictionary FederatedCredentialData#dictdef-federatedcredentialdataReferenced in:3.1.4. FederatedCredential : SiteBoundCredentialData { USVString provider#dom-federatedcredentialdata-providerReferenced in:1.2.3. Post-sign-in Confirmation; DOMString protocol; }; [Constructor(FederatedCredentialData data), Exposed=Window] interface FederatedCredential#federatedcredentialReferenced in:1.2.3. Post-sign-in Confirmation3.1. Credential Types3.1.1. Credential3.1.4. FederatedCredential3.1.4.1. Matching Algorithm (2)3.2.1.1. CredentialRequestOptions dictionary 3.2.2. Identifying providers4.1.1. Request a Credential (2)4.1.2. Store a Credential 4.2.4. Store SiteBoundCredential (2)9. Future Work : SiteBoundCredential { readonly attribute USVString provider; readonly attribute DOMString? protocol; };
- provider#dom-federatedcredential-providerReferenced in:1.2.2. Federated Sign-in3.1.4. FederatedCredential (2)3.1.4.1. Matching Algorithm 3.2.2. Identifying providers4.2.4. Store SiteBoundCredential (2), of type USVString, readonly
- The credential’s federated identity provider. For details regarding valid formats, see §3.2.2 Identifying providers.
- protocol#dom-federatedcredential-protocolReferenced in:3.1.4. FederatedCredential3.1.4.1. Matching Algorithm , of type DOMString, readonly, nullable
- The credential’s federated identity provider’s protocol (e.g.
"openidconnect"). If this value is
null
, then the protocol can be inferred from theprovider
. [[type]]
- All
FederatedCredential
objects have their[[type]]
slot’s value set to the string "federated
#dom-federatedcredential-federatedReferenced in:1.2.2. Federated Sign-in".
3.1.4.1. Matching Algorithm
FederatedCredential
objects' options matching algorithm is as
follows. Given a FederatedCredential
(credential) and a CredentialRequestOptions
(options):
- Let federated be the value of options’s
federated
property. -
If the
providers
property is present in federated: -
If the
protocols
property is present in federated: - Return
Does Not Match
.
3.2. Credential Manager
The credential manager hangs off of the Navigator
object, and exposes
methods to request credentials, and to notify the user agent when interesting
events occur: successful sign in and sign out.
partial interface Navigator {
readonly attribute CredentialsContainer credentials#dom-navigator-credentialsReferenced in:1.2.1. Password-based Sign-in1.2.2. Federated Sign-in1.2.3. Post-sign-in Confirmation (2) (3)1.2.4. Change Password3.2.1.2.
FederatedCredentialRequestOptions dictionary
(2);
};
interface CredentialsContainer#credentialscontainerReferenced in:3.2. Credential Manager7.2. Signing-Out8.2. Extension Points {
Promise<Credential?> get(CredentialRequestOptions options);
Promise<Credential> store(Credential credential);
Promise<void> requireUserMediation();
};
- get()#dom-credentialscontainer-getReferenced in:1.2.1. Password-based Sign-in1.2.2. Federated Sign-in3.2. Credential Manager (2) (3)3.2.1. get() Parameters 3.2.1.2. FederatedCredentialRequestOptions dictionary (2)4.1.1. Request a Credential 5.3. Credential Selection6.1. Cross-origin Credential Leakage (2) (3)6.3. Origin Confusion7.1. Timing Attacks (2)8.3. Browser Extensions
-
Request a credential from the credential manager.
The
options
argument contains an object filled with type-specific sets of parameters which will be used to select a particularCredential
to return. This process is described in eachCredential
type’s options matching algorithm.When
get()
is called, the user agent MUST execute the algorithm defined in §4.1.1 Request a Credential on types and options.Note: If and when we need to support returning more than a single credential in response to a single call, we will likely introduce a
getAll()
method which would returnPromise<sequence<Credential>>
.Arguments for the CredentialsContainer.get(options) method. Parameter Type Nullable Optional Description options#dom-credentialscontainer-get-options-optionsReferenced in:3.2. Credential Manager (2) CredentialRequestOptions ✘ ✘ The set of properties governing the scope of the request. - store()#dom-credentialscontainer-storeReferenced in:1.2.3. Post-sign-in Confirmation (2)1.2.4. Change Password (2)3.2. Credential Manager (2) (3)5.1. Storing and Updating Credentials6.3. Origin Confusion6.5. Script Injection8.3. Browser Extensions
-
Ask the credential manager to store a
Credential
for the user. Authors could call this method after a user successfully signs in, or after a successful password change operation.When
store()
is called, the user agent MUST execute the algorithm defined in §4.1.2 Store a Credential with credential as an argument.Arguments for the CredentialsContainer.store(credential) method. Parameter Type Nullable Optional Description credential#dom-credentialscontainer-store-credential-credentialReferenced in:3.2. Credential Manager Credential ✘ ✘ The credential to be stored. - requireUserMediation()#dom-credentialscontainer-requireusermediationReferenced in:3.2. Credential Manager (2)7.2. Signing-Out
-
Ask the credential manager to require user mediation before returning
credentials for the origin in which the method is called. This might be
called after a user signs out of a website, for instance, in order to
ensure that she isn’t automatically signed back in next time she visits.
When
requireUserMediation()
is called, the user agent MUST execute the algorithm defined in §4.3.1 Require user mediation for origin with the origin of the incumbent settings object in which this method is called.
3.2.1. get()
Parameters
In order to obtain the desired Credential
via 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 §8.2 Extension Points.
3.2.1.1. CredentialRequestOptions
dictionary
dictionary CredentialRequestOptions#dictdef-credentialrequestoptionsReferenced in:3.1.2.
SiteBoundCredential
3.1.4.1.
Matching Algorithm
3.2. Credential Manager3.2.1.
get() Parameters
(2)4.1.1.
Request a Credential
4.2.1.
Gather SiteBoundCredentials
4.2.2.
Request an SiteBoundCredential without user mediation
4.2.3.
Request a SiteBoundCredential with user mediation
8.2. Extension Points {
boolean password = false;
FederatedCredentialRequestOptions federated;
boolean unmediated = false;
};
- password#dom-credentialrequestoptions-passwordReferenced in:1.2.1. Password-based Sign-in1.2.2. Federated Sign-in3.2.1.1.
CredentialRequestOptions dictionary , of type boolean, defaulting to
false
- If set, the user agent will request
PasswordCredential
objects, as outlined in §4.1.1 Request a Credential. - federated#dom-credentialrequestoptions-federatedReferenced in:1.2.2. Federated Sign-in3.1.4.1. Matching Algorithm 3.2.1.1. CredentialRequestOptions dictionary 3.2.1.2. FederatedCredentialRequestOptions dictionary (2), of type FederatedCredentialRequestOptions
- If set, the user agent will request
FederatedCredential
objects, as outlined in §4.1.1 Request a Credential. - unmediated#dom-credentialrequestoptions-unmediatedReferenced in:3.2.1.1.
CredentialRequestOptions dictionary 3.2.1.2.
FederatedCredentialRequestOptions dictionary 4.1.1.
Request a Credential 8.1. Website Authors, of type boolean, defaulting to
false
- If
true
, the user agent will only attempt to provide aCredential
without user interaction. It MUST NOT present the user with any visible prompt to grant access to aCredential
: if the user has opted-into always giving a particular site access to a particular set of credentials, they will be provided. If not, the promise will resolve withundefined
. For processing details, see the algorithm defined in §4.1.1 Request a Credential.
3.2.1.2. FederatedCredentialRequestOptions
dictionary
dictionary FederatedCredentialRequestOptions#dictdef-federatedcredentialrequestoptionsReferenced in:3.2.1.1.
CredentialRequestOptions dictionary
(2)3.2.2. Identifying providers {
sequence<USVString> providers;
sequence<DOMString> protocols;
};
- providers#dom-federatedcredentialrequestoptions-providersReferenced in:1.2.2. Federated Sign-in3.1.4.1. Matching Algorithm 3.2.1.2. FederatedCredentialRequestOptions dictionary (2) (3)3.2.2. Identifying providers7.1. Timing Attacks, of type sequence<USVString>
- An array of federation identifiers. For details regarding valid formats see §3.2.2 Identifying providers.
- protocols#dom-federatedcredentialrequestoptions-protocolsReferenced in:3.1.4.1. Matching Algorithm (2)3.2.1.2. FederatedCredentialRequestOptions dictionary , of type sequence<DOMString>
- A sequence of protocol identifiers.
https://example.com/
only supports federated sign-in
via https://identity.example.com/
. It could request credentials
via the following call:
navigator.credentials.get({ "federated": { "providers": [ "https://identity.example.com/" ] } }).then(function (credential) { // ... });
If it wanted to ensure that the user agent didn’t bother the user with
questions, it could ask only for unmediated credentials (and, therefore,
to receive Credential
s if and only if the user had chosen to
automatically sign into the origin):
navigator.credentials.get( "federated": { "providers": [ "https://identity.example.com/" ] }, "unmediated: true ).then(function (credential) { // ... });
3.2.2. Identifying providers
Every site should use the same identifier when referring to a specific federated identity provider. For example, Facebook Login shouldn’t be "Facebook" and "Facebook Login" and "FB" and "FBL" and "Facebook.com" and so on. It should have a canonical identifier which everyone can make use of, as consistent identification makes it possible for user agents to be helpful.
For consistency, federations passed into the APIs defined in this document
(e.g. FederatedCredentialRequestOptions
's providers
array, or FederatedCredential
's provider
property)
MUST be identified by the ASCII serialization of the origin the
provider uses for sign in. That is, Facebook would be represented by https://www.facebook.com
and Google by https://accounts.google.com
.
The ASCII serialization of an origin does not include a trailing
U+002F SOLIDUS ("/
"), but user agents SHOULD accept them
silently: https://accounts.google.com/
is clearly intended to
be the same as https://accounts.google.com
.
3.3. Integration with Fetch
In order to actually authenticate a user, PasswordCredential
objects may
be submitted to a server for evaluation. To prevent the risk that sensitive
credential information will be exposed directly to a page’s JavaScript (see §6.1 Cross-origin Credential Leakage), we hide the credential information in
non-web-exposed slots on the PasswordCredential
object, and extract them
during fetch()
in ways that will remain opaque to anything other than the
remote (same-origin) server.
PasswordCredential
objects may be bound to a Request
by passing them
into the Request
constructor as the "credentials
" member of a RequestInit
object:
[ Given a PasswordCredential
named 'c' ]
var init = {
method: "POST",
credentials: c
};
var r = Request("https://example.com/endpoint", init);
Note: The credential information will be transmitted as the request’s body,
just as it would be for a form submission. This means that the "method
"
cannot be "GET
".
Until the request hits the network, the credential object will be hidden from
JavaScript. Once the user agent determines that the Request
is going to
hit the network (that is, once Fetch hits the
"HTTP-network-or-cache fetch" algorithm), the PasswordCredential
will be serialized into a body and content type as defined in §3.3.2 Extract a body and Content-Type for request’s attached credential, and the request will proceed out to the server-side
authentication endpoint.
3.3.1. Monkey Patches to Fetch
To accomplish the above, Fetch could be modified in the following ways:
-
A request has an associated attached credential#request-attached-credentialReferenced in:3.3.1. Monkey Patches to Fetch (2) (3) (4)3.3.2. Extract a body and Content-Type for request’s attached credential (null, or a
PasswordCredential
). Unless stated otherwise, it is null. -
A request’s credentials mode gains a new "
password
" value, which implies the same behavior as "include
", with the additional assertion that the request has a non-null attached credential which will be serialized into its body when it hits the network. -
The
RequestCredentials
enum is renamed to "RequestCredentialsMode
, and likewise updated with the new "password
" value:enum RequestCredentialsMode { "omit", "same-origin", "include", "password" };
-
The
RequestInit
dictionary is updated to accept aPasswordCredential
as itscredentials
member:typedef (PasswordCredential or RequestCredentials) CredentialInfo#typedefdef-credentialinfoReferenced in:3.3.1. Monkey Patches to Fetch; partial dictionary RequestInit { CredentialInfo credentials; };
-
Replace step 20 of the current
Request
constructor with the following:-
If credentials is non-null:
-
If credentials is a
PasswordCredential
, then set request’s credentials mode to "password
", and request’s attached credential to credentials.Otherwise, set request’s credentials mode to credentials.
-
-
-
Replace step 32 of the current
Request
constructor with the following:-
If request’s method is
GET
orHEAD
, then throw aTypeError
if any of the following are true:-
init’s
body
member is present and non-null -
inputBody is non-null
-
request’s attached credential is non-null
-
-
-
The HTTP-network-or-cache fetch algorithm is updated with the following step after the existing step 2:
-
If httpRequest’s attached credential is not null, run these substeps:
-
Assert: httpRequest’s body is null.
-
Let body and type be the result of running §3.3.2 Extract a body and Content-Type for request’s attached credential on httpRequest.
-
If type is the empty string, return a network error.
-
Let httpRequest’s body be body, Content-Type be type, and redirect mode be "
manual
".
-
-
These patches are combined into an outstanding pull request against Fetch. <https://github.com/whatwg/fetch/issues/237>
3.3.2. Extract a body and Content-Type for request’s attached credential
Given a Request
(request), this algorithm returns a Body
(body) and
a string (Content-Type):
-
Let credential be request’s attached credential.
-
Let body be a new body, and Content-Type be the empty string.
-
If credential is a
PasswordCredential
, run these substeps.-
If request’s url’s scheme is not the same as request’s client’s
origin
's scheme, or request’s url’s host’s registerable domain is not the same as request’s client’sorigin
's host’s registerable domain, then skip the remaining substeps. -
Let data be a copy of credential’s
additionalData
attribute if it is notundefined
, and a newFormData
object otherwise. -
Let list be an empty list.
-
If data is a
FormData
object, then:-
Set list to a copy of data’s entries.
-
Remove all entry objects from list whose name is either credential’s
idName
or credential’spasswordName
. -
Let entry be a new entry whose name is credential’s
idName
and whose value is credential’sid
. -
Append entry to list.
-
Let entry be a new entry whose name is credential’s
passwordName
and whose value is credential’s[[password]]
. -
Append entry to list.
-
Push the result of running the
multipart/form-data
encoding algorithm, with list as the form data set and with "utf-8
" as the explicit character encoding, to body’s stream. -
Set Content-Type to
multipart/form-data;boundary=
, followed by themultipart/form-data
boundary string generated by the multipart/form-data encoding algorithm.
Otherwise data is a
URLSearchParams
object, so:-
Set list to a copy of data’s list.
-
Remove all name-value pairs from list whose name is either credential’s
idName
or credential’spasswordName
. -
Append a new name-value pair to list whose name is credential’s
idName
and whose value is credential’sid
. -
Append a new name-value pair to list whose name is credential’s
passwordName
and whose value is credential’s[[password]]
. -
Push the result of running the
application/x-www-form-urlencoded
serializer, with list, to body’s stream. -
Set Content-Type to
application/x-www-form-urlencoded;charset=UTF-8
.
-
-
-
Return body and Content-Type.
4. Algorithms
4.1. Processing Credential
s
4.1.1. Request a Credential
Given a sequence<DOMString>
(types) and a CredentialRequestOptions
object (options), this
algorithm returns a Promise
which resolves with either a
single Credential
object if one can be obtained, or undefined
if not.
If called from an environment which is not a secure context, or
from somewhere other than a top-level browsing context, the Promise
will be rejected with a NotSupportedError
.
- Let settings be the incumbent settings object.
- Let origin be settings’ origin.
-
Return a
Promise
rejected withNotSupportedError
if any of the following statements are true:- settings does not have a responsible document
- settings’ responsible document is not the active document in the top-level browsing context
- settings is not a secure context
- Let types be an empty set.
-
For each key in options:
- Let interface be the interface whose name is key, or
null
if no interface’s name matches. - If interface is not
null
, insert interface into possible types.
- Let interface be the interface whose name is key, or
-
Let type be the lowest common ancestor interface of the
interfaces contained in types.
Note: That is, given a set containing
PasswordCredential
andFederatedCredential
, type will beSiteBoundCredential
. - Return a
Promise
rejected withTypeMismatchError
if type isCredential
. - Let promise be a newly created
Promise
object. - Return promise, and execute the remaining steps asynchronously.
-
Switch on type, and execute the associated steps:
FederatedCredential
SiteBoundCredential
PasswordCredential
-
- Let result be the result of executing §4.2.2 Request an SiteBoundCredential without user mediation, passing in origin, types and options.
- If result is not
null
, resolve promise with result, and terminate this algorithm. - If options’s
unmediated
istrue
, resolve promise with undefined, and terminate this algorithm. - Resolve promise with the result of executing §4.2.3 Request a SiteBoundCredential with user mediation, passing in origin, types, and options.
- This is an extension point.
- When new credential types are defined in the future, they’ll go here.
Note: Currently, we reject a call to get()
if the options provided specify a set of Credential
types that don’t
play well together (e.g. some future "NeedsLotsOfUserInteractionCredential"
type in the same request as an PasswordCredential
). We may wish to define
a more graceful fallback mechanism if/when new credential types are defined.
4.1.2. Store a Credential
Given a Credential
object (credential), this algorithm executes
a type-specific storage algorithm. SiteBoundCredential
objects will
be persisted to the user agent’s credential store. Future object types
could, for instance, be persisted to some other (potentially remote) storage
mechanism.
If called from an environment which is not a secure context, or
from somewhere other than a top-level browsing context, the Promise
will be rejected with a NotSupportedError
.
- Let settings be the incumbent settings object.
- Let origin be settings’ origin.
-
Return a
Promise
rejected withNotSupportedError
if any of the following statements are true:- settings does not have a responsible document
- settings’ responsible document is not the active document in the top-level browsing context
- settings is not a secure context
- Let promise be a newly created
Promise
object. - Return promise, and execute the remaining steps asynchronously.
-
Switch on type, and execute the associated steps:
FederatedCredential
SiteBoundCredential
PasswordCredential
-
- Resolve promise with the result of executing §4.2.4 Store SiteBoundCredential, passing in credential and origin.
- This is an extension point.
- When new credential types are defined in the future, they’ll go here.
4.1.3. Clone credential
Given a Credential
(input), the following algorithm
defines the way in which a structured clone will be produced. This
algorithm plugs into the internal structured cloning algorithm defined in [HTML]:
- Let output be a
Credential
object of the same type as input’sconstructor
. -
For each internal slot on input:
- Let name be the name of the slot.
- Let source value be the slot’s value.
- Let cloned value be the result of invoking the internal
structured cloning algorithm with source value as the
"
input
" argument, and memory as the "memory
" argument. - If an exception results from the previous step, abort the overall structured clone algorithm, and pass that exception through to the caller.
- Add a new slot to output having name name and value cloned value.
- Set deep clone to
own
.
4.2. Processing SiteBoundCredential
s
4.2.1. Gather SiteBoundCredential
s
Given an origin (origin), and a set of interfaces (types), and an CredentialRequestOptions
dictionary
(options), this algorithm returns a sequence of SiteBoundCredential
from the user agent’s credential store which are potential candidates:
-
Let credentials be the set of
Credential
objects in the credential store whose[[origin]]
slot is equal to the ASCII serialization of origin.Note: This is an exact match, not a registerable domain match. See §6.1 Cross-origin Credential Leakage for details as to why.
- Remove any items from credentials whose interface is not present in types.
- Remove any items from credentials whose options matching
algorithm returns
Does Not Match
when executed on options. - Return credentials.
4.2.2. Request an SiteBoundCredential
without user mediation
This algorithm accepts an origin (origin), a set of interfaces (types) and an CredentialRequestOptions
dictionary (options), and returns either a single SiteBoundCredential
object if and only if one can be
provided without user mediation, or null
if not.
-
If the user agent has disabled sharing credentials without user
mediation, or origin’s requires user mediation flag
in the user agent’s credential store is
true
, returnnull
.Note: See §5.2 Requiring User Mediation for details.
- Let credentials be the result of executing §4.2.1 Gather SiteBoundCredentials on origin, types, and options.
-
If credentials is empty, or contains more than one
Credential
object, returnnull
.Note: In the future, we may wish to allow multiple
Credential
objects to be returned; for the moment, we’re erring on the cautious side to avoid accidentally revealing explicit relationships between user accounts. - Otherwise, let credential be the single
Credential
object in credentials. - Return credential.
Note: If a user agent implements some sort of "private browsing" mode, we
recommend that this algorithm always return null
when the user
has enabled private browsing.
4.2.3. Request a SiteBoundCredential
with user mediation
This algorithm accepts an origin (origin), a set of interfaces (types) and an CredentialRequestOptions
dictionary (options), and returns either a single SiteBoundCredential
object, or null
if none can be
provided.
- Let credentials be the result of executing §4.2.1 Gather SiteBoundCredentials on origin, types, and options.
-
Ask the user which
Credential
to share.Note: This behavior is vendor-specific. Guidelines for user agent behavior are presented in §5 User Mediation.
- Let credential be the
Credential
the user chose, ornull
if the user chose not to share a credential with origin. - Return credential.
4.2.4. Store SiteBoundCredential
This algorithm accepts a SiteBoundCredential
(credential),
and an origin (origin), and hands it to the internal
credential manager for processing. It returns credential regardless
of whether the user allows or disallows the credential to be persisted to the credential store:
- Set credential’s
[[origin]]
slot to the ASCII serialization of origin. - If credential’s
iconURL
is not an a priori authenticated URL, set credential’siconURL
to the empty string. -
Switch on credential’s primary interface, and execute the
associated steps:
PasswordCredential
-
-
If the user agent’s credential manager contains a
PasswordCredential
storedCredential whoseid
attribute is credential’sid
and whose[[origin]]
slot is origin, then:-
If the user grants origin permission to update
credentials (as discussed in §5.1 Storing and Updating Credentials),
then:
- Set storedCredential’s
[[password]]
to the value of credential’s[[password]]
slot. - Set storedCredential’s
name
to the value of credential’sname
. - Set storedCredential’s
iconURL
to the value of credential’siconURL
.
- Set storedCredential’s
-
If the user grants origin permission to update
credentials (as discussed in §5.1 Storing and Updating Credentials),
then:
-
Otherwise:
- If the user grants origin permission to store credentials (as discussed in §5.1 Storing and Updating Credentials), then store credential in the credential store.
-
If the user agent’s credential manager contains a
FederatedCredential
-
-
If the user agent’s credential manager does not contain a
FederatedCredential
storedCredential whoseid
attribute is credential’sid
and whoseprovider
attribute is credential’sprovider
, then:- If the user grants origin permission to store credentials (as discussed in §5.1 Storing and Updating Credentials), store credential in the credential store.
-
If the user agent’s credential manager does not contain a
- This is an extension point.
- When new credential types are defined in the future, they’ll go here.
- Return credential.
4.3. Helpful Algorithms
4.3.1. Require user mediation for origin
- Let promise be a newly created
Promise
object. - Return promise, and execute the remaining steps asynchronously.
- Set origin’s requires user mediation flag to
true
in the user agent’s credential store. - Resolve promise with
undefined
.
4.3.2. Does credential match origin B?
- Let origin A be the value of credential’s
[[origin]]
slot. - If origin A is the same as origin B, return
Exact Match
. - If origin A’s
scheme
is origin B’sscheme
, and origin A’shost
's registerable domain is origin B’shost
's registerable domain, then returnFuzzy Match
. - Return
No Match
5. User Mediation
Exposing credential information to the web via an API has a number of potential impacts on user privacy. The user agent, therefore, MUST involve the user in a number of cases in order to ensure that she clearly understands what’s going on, and with whom her credentials are being shared.
5.1. Storing and Updating Credentials
Credential information is sensitive data, and users MUST remain in control of that information’s storage. Inadvertent credential storage could, for instance, unexpectedly link a user’s local profile on a particular device to a specific online persona. To mitigate the risk of surprise:
- Credential information MUST NOT be stored or updated without explicit
user consent. For example, the user agent could display a "Save this
password?" dialog box to the user in response to each call to
store()
. -
User consent MAY be requested every time a credential is stored or
updated, or the user agent MAY request a more persistent
grant of consent which would apply to some or all subsequent API
operations.
For example, a user agent may offer an option to "Always save passwords", or "Always save password on this site".
- User agents SHOULD notify users when credentials are stored. This might take the form of an icon in the address bar, or some similar location.
- User agents MUST allow users to manually remove stored credentials. This functionality might be implemented as a settings page, or via interaction with a notification as described above.
5.2. Requiring User Mediation
If a an origin’s requires user mediation flag is set to false
in the user agent’s credential store, then Credential
objects from that origin MAY be provided to pages from that
origin without user interaction. The user will be signed-in to that origin
persistently, which, on the one hand, is desirable from the perspective of
usability and convenience, but which might nevertheless surprise the user.
If the user agent syncs the state of a Credential
between devices, an
origin could explicitly tie the devices together in a way which might surprise
their owner.
To mitigate the risk of surprise:
- User agents MUST allow users to require user mediation for
Credential
objects. This functionality might be implemented as a global toggle that requires user mediation for all origins, or via more granular settings for specific origins (or specific credentials on specific origins). - User agents MUST NOT set an origin’s requires user mediation slot’s
value to
false
without user consent. For example, the credential chooser described in §5.3 Credential Selection could have a checkbox which the user could toggle to mark the selected credential as available without mediation for the origin, or the user agent could have an onboarding process for its credential manager which asked a user for a default setting. - User agents MUST notify users when credentials are provided to an origin. This could take the form of an icon in the address bar, or some similar location.
- If a user clears her browsing data for an origin (cookies, localStorage, and so on), the user agent MUST require user mediation for that origin by executing the algorithm defined in §4.3.1 Require user mediation for origin.
5.3. Credential Selection
When responding to a call to get()
on an origin
without credentials that are available without user mediation, user agents
MUST ask the user for permission to share credential information. This SHOULD
take the form of a credential chooser#credential-chooserReferenced in:5.2. Requiring User Mediation which
presents the user with a list of credentials that are available for use on a
site, allowing her to select one which should be provided to the website, or
to reject the request entirely.
The chooser interface SHOULD be implemented in such a way as to be distinguishable from UI which a website could produce. For example, the chooser might overlap the user agent’s UI in some unspoofable way.
The chooser interface SHOULD include an indication of the origin which is requesting credentials.
The chooser interface SHOULD include all Credential
objects associated
with the origin that requested credentials.
The following image is an exceptionally non-normative mock:
User agents MAY internally associate information with each Credential
object beyond the attributes specified in this document in order to enhance
the utility of such a chooser. For example, favicons could help disambiguate
identity providers, etc. Any additional information stored MUST not be
exposed directly to the web.
6. Security Considerations
6.1. Cross-origin Credential Leakage
Credentials are sensitive information, and user agents need to exercise
caution in determining when they can be safely shared with a website. The
safest option is to restrict credential sharing to the exact origin on
which they were saved. That is likely too restrictive for the web, however:
consider sites which divide functionality into subdomains: example.com
vs admin.example.com
.
As a compromise between annoying users, and securing their credentials, user agents:
- MUST NOT share credentials between origins whose scheme components are
not the same. That is: credentials saved on
https://example.com/
will never be available tohttp://example.com/
via a user agent’s autofill mechanism - MAY use the Public Suffix List [PSL] to determine the effective scope
of a credential by comparing the registerable domains of the
credential’s
[[origin]]
with the origin in whichget()
is called. That is: credentials saved onhttps://admin.example.com/
andhttps://example.com/
MAY be offered to users whenget()
is called fromhttps://www.example.com/
. - MUST NOT offer credentials to an origin in response to
get()
without user mediation if the credential’s origin is not an exact match for the calling origin. That is,Credential
objects forhttps://example.com
would not be returned directly tohttps://www.example.com
, but could be offered to the user via the chooser.
PasswordCredential
s further mitigate the risk of data leakage by never
exposing the [[password]]
slot directly to a page’s JavaScript, but only
allowing its submission to a same-origin server-side endpoint via fetch()
.
Additionally, authors SHOULD mitigate the risk of leakage by setting a reasonable Content Security Policy [CSP2] which restricts the origins to which data can be sent. In particular, authors should ensure that the following directives are set, explicitly or implicitly, in their pages' policies:
-
connect-src
further restricts the origins to whichfetch()
may submit data (which mitigates the risk of redirect-based attacks). -
child-src
restricts the nested browsing contexts which may be embedded in a page, making it more difficult to inject a maliciouspostMessage()
target. [WEBMESSAGING]
6.2. Same-origin Leakage
Cross-site scripting attacks could make it possible to exploit PasswordCredential
's integration with fetch()
in order to leak
credential information via a vulnerable same-origin endpoint. To mitigate
this kind of risk, authors SHOULD set a reasonable Content Security Policy
which restricts the kinds of content which can execute in their sites'
context. [CSP2] Moreover, authors should ensure that both script-src
and object-src
directives
are set in any policy they specify in order to ensure that only trusted
script is executed on a page with access to a user’s credentials.
6.3. Origin Confusion
If framed pages have access to the APIs defined here, it might be possible to confuse a user into granting access to credentials for an origin other than the top-level browsing context, which is the only security origin which users can reasonably be expected to understand.
Therefore, Nested browsing contexts and other environments like
Workers [WORKERS] cannot receive or store Credential
objects; the user
agent MUST reject promises generated by calls to get()
and store()
with a SecurityError
when
called from a context which is not a top-level browsing context.
See the algorithms defined in §4.1.1 Request a Credential and §4.1.2 Store a Credential for details.
6.4. Insecure Sites
User agents MUST NOT expose the APIs defined here to environments which are not secure contexts. User agents MAY implement autofill mechanisms which store user credentials and fill sign-in forms on non-a priori authenticated URLs, but those sites cannot be trusted to interact directly with the credential manager in any meaningful way, and those sites MUST NOT have access to credentials saved in secure contexts (as discussed in §6.1 Cross-origin Credential Leakage.
6.5. Script Injection
If a malicious party is able to inject script into an origin, they could
(among many other things you wouldn’t like) overwrite the behavior of store()
to steal a user’s credentials as they’re written into the credential store.
Authors SHOULD mitigate the risk of such attacks by properly escaping input and output, and add layers of defense in depth by setting a reasonably strong Content Security Policy [CSP2] which restricts the origins from which script can be injected, and by using Subresource Integrity checks [SRI] to ensure that only trusted JavaScript is executed.
7. Privacy Considerations
7.1. Timing Attacks
If the user has no credentials for an origin, a call to get()
will resolve very quickly indeed. A malicious
website could distinguish between a user with no credentials and a user with
credentials who chooses not to share them.
This could allow a malicious website to determine if a user has credentials
saved for particular federated identity providers by repeatedly calling get()
with a single item in the providers
array. The risk is mitigated
by the fact that the user would, sooner or later, be prompted to provide
credentials to the site which would certainly raise her suspicions as to its
behavior.
User agents SHOULD also rate-limit credential requests. It’s almost certainly abusive for a page to request credentials more than a few times in a short period.
7.2. Signing-Out
If a user has chosent to automatically sign-in to websites, as discussed
in §5.2 Requiring User Mediation, then the user agent will provide
credentials to an origin whenever it asks for them. The website can instruct
the user agent to suppress this behavior by calling CredentialsContainer
's requireUserMediation()
method, which will turn off
automatic sign-in for a given origin.
The user agent relies on the website to do the right thing; an inattentive (or malicious) website could simply neglect to call this method, causing the user agent to continue providing credentials against the user’s apparent intention.
The user MUST have some control over this behavior. As noted in §5.2 Requiring User Mediation, clearing cookies for an origin will also
reset that origin’s requires user mediation flag in the credential
store to true
. Additionally, we recommend that the user agent provide
some UI affordance for disabling automatic sign-in for a particular origin.
This could be tied to the notification that credentials have been provided to
an origin, for example.
7.3. Chooser Leakage
If a user agent displays images supplied by a website or federation (for
example, if a Credential
's iconURL
is displayed),
requests for these images MUST NOT be directly tied to instantiating the
chooser in order to avoid leaking chooser usage. One option would be to fetch
the images in the background when saving or updating a Credential
, and to
cache them for the lifetime of the Credential
.
These images MUST be fetched with the credentials
mode set to
"omit
", the skip-service-worker flag
set, the client
set to null
, the initiator
set
to the empty string, and the destination
set to subresource
.
Moreover, if the user agent allows the user to change either the name or icon associated with the credential, the alterations to the data SHOULD NOT be exposed to the website (consider a user who names two credentials for an origin "My fake account" and "My real account", for instance).
7.4. Locally Stored Data
This API offers an origin the ability to store data persistently along with a user’s profile. Since most user agents treat credential data differently than "browsing data" (cookies, etc.) this might have the side effect of surprising a user who might believe that all traces of an origin have been wiped out when they clear their cookies.
User agents SHOULD provide UI that makes it clear to a user that credential data is stored for an origin, and SHOULD make it easy for users to remove such data when they’re no longer interested in keeping it around.
Moreover, the credential store’s association between origins and protocol sets SHOULD be cleared along with "browsing data" if
no Credential
has been stored for the origin.
8. Implementation Considerations
This section is non-normative.
8.1. Website Authors
Add some thoughts here about when and how the API
should be used, especially with regard to unmediated
. <https://github.com/w3c/webappsec/issues/290>
8.2. Extension Points
As noted in §9 Future Work, there is known interest in extending the API defined here to serve use cases beyond those this document addresses. To that end, the API is fairly generic, with several explicit extension points.
-
Define a new
ExampleCredential
that inherits fromCredential
, and define the value of its[[type]]
slot:interface ExampleCredential : Credential { // Definition goes here. }; ... All
ExampleCredential
objects have their [[type]] slot’s value set to the string "example". -
Define the options that the new credential type requires, and add them
to the
CredentialRequestOptions
dictionary with a property name that matches the[[type]]
slot’s value.dictionary ExampleCredentialRequestOptions { // Definition goes here. }; partial dictionary CredentialRequestOptions { ExampleCredentialRequestOptions? example; };
- Add
ExampleCredential
to the switch in step 8 of §4.1.1 Request a Credential, and define the request behavior necessary to grab and return the new type. - Add
ExampleCredential
to the switch in step 6 of §4.1.2 Store a Credential, and define the persistance behavior necessary to store the new type.
You might also need new primitives. For instance, you might want to return
many Credential
objects rather than just one. That might be accomplished
in a generic fashion by adding a getAll()
method to CredentialsContainer
, and defining a CredentialSet
object that
contained a sequence<Credential>
, and could be easily
extended to meet some use case.
For any such extension, we recommend getting in touch with public-webappsec@ for consultation and review.
8.3. Browser Extensions
Ideally, user agents that implement an extension system of some sort will allow third-parties to hook into these API endpoints in order to improve the behavior of third party credential management software in the same way that user agents can improve their own via this imperative approach.
This could range from a complex new API that the user agent mediates, or
simply by allowing extensions to overwrite the get()
and store()
endpoints for their own purposes.
9. Future Work
This section is non-normative.
The API defined here does the bare minimum to expose user agent’s credential managers to the web, and allows the web to help those credential managers understand when federated identity providers are in use. The next logical step will be along the lines sketched in documents like [WEB-LOGIN] (and, to some extent, Mozilla’s BrowserID [BROWSERID]).
The user agent is in the unique position of being able to effectively mediate the relationship between users, identity providers, and websites. If the user agent can remove some of the risk and confusion associated with the typical authentication flows, users will be in a significantly better position than today.
A natural way to expose this information might be to extend the FederatedCredential
interface with properties like authentication tokens,
and possibly to add some form of manifest format with properties that declare
the authentication type which the provider supports.
The API described here is designed to be extensible enough to support use
cases that require user interaction, perhaps with websites other than the one
which requested credentials. We hope that the Promise-based system we’ve
settled on is extensible enough to support these kinds of asynchronous flows
which could require some level of interaction between multiple browsing
contexts (e.g. mediated activity on idp.com
might resolve a
Promise handed back to rp.com
) in the future without redesigning
the API from the ground up.
Baby steps.