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
XMLHttpRequestor 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://www.facebook.com", "https://accounts.google.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" 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 (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" 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 (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. SettingadditionalDatato an emptyURLSearchParamsobject // 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
- Password Credentials
- Federated Credentials
- Password Credentials
-
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
-
A
credential
store
is
an
opaque
storage
mechanism
which
offers
a
user
agent
the
ability
to:
-
Store, retrieve, and modify
Credentialobjects. -
To mark an origin with a requires user mediation flag.
-
To associate an origin with a protocol set .
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
- 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 required USVString ; }; [id SecureContext ]interface {Credential readonly attribute USVString id ;readonly attribute DOMString type ; };Credential implements Transferable ;
-
id, of type USVString , readonly - The credential’s identifier. This might be a GUID, username, or email address, for instance.
-
type, 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,PasswordCredentialobjects have a[[type]]ofpassword, andFederatedCredentialobjects have a[[type]]offederated. -
[[type]] -
All
Credentialobjects 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 thetypeattribute.
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 CredentialData {USVString ;name USVString ; }; [iconURL SecureContext ]interface :SiteBoundCredential Credential {readonly attribute USVString name ;readonly attribute USVString iconURL ; };
3.1.2.
SiteBoundCredential
-
name, of type USVString , readonly - A name associated with the credential, intended as a human-understandable public name.
-
iconURL, 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
ImageBitmaporblob:. We also need to figure out responsiveness. Perhaps [MANIFEST] 's format? <https://github.com/w3c/webappsec/issues/247> -
[[origin]] -
All
SiteBoundCredentialobjects 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
which
returns
Match
if
the
object
matches
a
CredentialRequestOptions
object,
and
Does
Not
Match
otherwise.
3.1.3.
PasswordCredential
dictionary :PasswordCredentialData SiteBoundCredentialData {required USVString ; };password typedef (FormData or URLSearchParams ); [CredentialBodyType Constructor (PasswordCredentialData data ),Constructor (HTMLFormElement form ),Exposed =Window ,SecureContext ]interface :PasswordCredential SiteBoundCredential {attribute USVString idName ;attribute USVString passwordName ;attribute CredentialBodyType ?additionalData ; };
-
idName, of type USVString -
This attribute represents the name which will be used for the ID field when submitting the
PasswordCredentialto a remote endpoint viafetch(). It defaults to "username", but can be overridden by a developer to match whatever the backend service expects. -
passwordName, of type USVString -
This attribute represents the name which will be used for the ID field when submitting the
PasswordCredentialto a remote endpoint viafetch(). It defaults to "password", but can be overridden by a developer to match whatever the backend service expects. -
additionalData, 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
FormDataorURLSearchParamsobject to this attribute. The credential information will be mixed into the object to produce the body. The value isnullunless otherwise specified. -
[[type]] -
In only one current engine.
Firefox None Safari None Chrome 60+
Opera 47+ Edge 79+
Edge (Legacy) None IE None
Firefox for Android None iOS Safari None Chrome for Android 60+ Android WebView 60+ Samsung Internet 8.0+ Opera Mobile 44+All
PasswordCredentialobjects have their[[type]]slot’s value set to the string "password".
All
PasswordCredential
objects
have
an
internal
slot
named
[[password]]
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)
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
PasswordCredentialDatadictionary. -
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 idName and passwordName be empty strings.
-
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:
-
"
current-password"- "
new-password" - "
-
Set data ’s
passwordmember’s value to the result of executing formData ’sget()method on name , and passwordName to name . -
"
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 , and idName to name .
-
"
-
-
-
-
Let c be the result of executing the
PasswordCredential(data)constructor on data . Rethrow any exception generated. -
Set c ’s
idNameattribute to idName . -
Set c ’s
passwordNameattribute to passwordName . -
If form ’s enctype is "
multipart/form-data", then:-
Set c ’s
additionalDataattribute to formData .
Otherwise:
-
Let params be a new
URLSearchParamsobject. -
For each entry in formData ’s entries :
-
Set c ’s
additionalDataattribute to params .
-
-
Return c .
3.1.3.2.
PasswordCredential(PasswordCredentialData
data)
Constructor
The
PasswordCredential(data)
constructor,
when
invoked
with
a
PasswordCredentialData
(
data
),
must
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 .
3.1.3.3. Matching Algorithm
PasswordCredential
objects'
options
matching
algorithm
always
returns
Match
.
3.1.4.
FederatedCredential
dictionary :FederatedCredentialData SiteBoundCredentialData {required USVString ;provider DOMString ; }; [protocol Constructor (FederatedCredentialData data ),Exposed =Window ,SecureContext ]interface :FederatedCredential SiteBoundCredential {readonly attribute USVString provider ;readonly attribute DOMString ?protocol ; };
-
provider, of type USVString , readonly - The credential’s federated identity provider . For details regarding valid formats, see § 3.2.2 Identifying providers .
-
In only one current engine.
Firefox None Safari None Chrome 51+
Opera 38+ Edge 79+
Edge (Legacy) None IE None
Firefox for Android None iOS Safari None Chrome for Android 51+ Android WebView 51+ Samsung Internet 5.0+ Opera Mobile 41+protocol, 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
FederatedCredentialobjects have their[[type]]slot’s value set to the string "federated".
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
federatedproperty. -
If
the
providersproperty is present in federated : -
If
the
protocolsproperty 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 { [SecureContext ]readonly attribute CredentialsContainer ; };credentials
[SecureContext ]interface {CredentialsContainer Promise <Credential ?>get (optional CredentialRequestOptions options );Promise <Credential >(store Credential credential );Promise <void >requireUserMediation (); };
-
In all current engines.
Firefox 61+ Safari 13+ Chrome 60+
Opera 47+ Edge 79+
Edge (Legacy) 18 IE None
Firefox for Android 61+ iOS Safari 13+ Chrome for Android 60+ Android WebView 60+ Samsung Internet 8.0+ Opera Mobile 44+In all current engines.
Firefox 61+ Safari 13+ Chrome 51+
Opera 38+ Edge 79+
Edge (Legacy) 18 IE None
Firefox for Android 61+ iOS Safari 13+ Chrome for Android 51+ Android WebView 51+ Samsung Internet 5.0+ Opera Mobile 41+get() -
Request
a
credential
from
the
credential
manager.
The
optionsargument contains an object filled with type-specific sets of parameters which will be used to select a particularCredentialto return. This process is described in eachCredentialtype’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 optionsCredentialRequestOptions ✘ ✔ The set of properties governing the scope of the request. -
store() -
Ask
the
credential
manager
to
store
a
Credentialfor 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 credentialCredential ✘ ✘ The credential to be stored. -
requireUserMediation() -
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 boolean password =false ;FederatedCredentialRequestOptions federated ;boolean unmediated =false ; };
-
password, of type boolean , defaulting tofalse -
If
set,
the
user
agent
will
request
PasswordCredentialobjects, as outlined in § 4.1.1 Request a Credential . -
federated, of type FederatedCredentialRequestOptions -
If
set,
the
user
agent
will
request
FederatedCredentialobjects, as outlined in § 4.1.1 Request a Credential . -
unmediated, of type boolean , defaulting tofalse -
If
true, the user agent will only attempt to provide aCredentialwithout 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 sequence <USVString >providers ;sequence <DOMString >protocols ; };
-
providers, of type sequence< USVString > - An array of federation identifiers. For details regarding valid formats see § 3.2.2 Identifying providers .
-
protocols, 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 = new 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 (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
RequestCredentialsenum is renamed to "RequestCredentialsMode, and likewise updated with the new "password" value:enum {RequestCredentialsMode ,"omit" ,"same-origin" ,"include" };"password" -
The
RequestInitdictionary is updated to accept aPasswordCredentialas itscredentialsmember:typedef (PasswordCredential or RequestCredentials );CredentialInfo partial dictionary RequestInit {CredentialInfo ; };credentials -
Replace step 20 of the current
Requestconstructor 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
Requestconstructor with the following:-
If request ’s method is
GETorHEAD, then throw aTypeErrorif any of the following are true:-
init ’s
bodymember 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
additionalDataattribute if it is notundefined, and a newFormDataobject otherwise. -
Let list be an empty list.
-
If data is a
FormDataobject, then:-
Set list to a copy of data ’s entries .
-
Remove all entry objects from list whose name is either credential ’s
idNameor credential ’spasswordName. -
Let entry be a new entry whose name is credential ’s
idNameand whose value is credential ’sid. -
Append entry to list .
-
Let entry be a new entry whose name is credential ’s
passwordNameand whose value is credential ’s[[password]]. -
Append entry to list .
-
Push the result of running the
multipart/form-dataencoding 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-databoundary string generated by the multipart/form-data encoding algorithm.
Otherwise data is a
URLSearchParamsobject, so:-
Set list to a copy of data ’s list .
-
Remove all name-value pairs from list whose name is either credential ’s
idNameor credential ’spasswordName. -
Append a new name-value pair to list whose name is credential ’s
idNameand whose value is credential ’sid. -
Append a new name-value pair to list whose name is credential ’s
passwordNameand whose value is credential ’s[[password]]. -
Push the result of running the
application/x-www-form-urlencodedserializer , 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
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
Promiserejected withNotSupportedErrorif 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
such
that
[[type]]across all objects implementing that interface is key , ornullif no interface’s name matches. -
If
interface
is
not
null, insert interface into types .
-
Let
interface
be
the
interface
such
that
-
Let
type
be
the
lowest
common
ancestor
interface
of
the
interfaces
contained
in
types
.
Note: That is, given a set containing
PasswordCredentialandFederatedCredential, type will beSiteBoundCredential. -
Return
a
Promiserejected withTypeMismatchErrorif type isCredential. -
Let
promise
be
a
newly
created
Promiseobject. - Return promise , and execute the remaining steps asynchronously.
-
Switch
on
type
,
and
execute
the
associated
steps:
-
FederatedCredentialSiteBoundCredentialPasswordCredential -
- 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
unmediatedistrue, resolve promise withundefined, 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
Promiserejected withNotSupportedErrorif 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
Promiseobject. - Return promise , and execute the remaining steps asynchronously.
-
Switch
on
type
,
and
execute
the
associated
steps:
-
FederatedCredentialSiteBoundCredentialPasswordCredential -
- 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
Credentialobject 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
Credentialobjects 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 Matchwhen 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
Credentialobject, returnnull.Note: In the future, we may wish to allow multiple
Credentialobjects 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
Credentialobject 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
Credentialto share.Note: This behavior is vendor-specific. Guidelines for user agent behavior are presented in § 5 User Mediation .
-
Let
credential
be
the
Credentialthe user chose, ornullif 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
iconURLis not an a priori authenticated URL , set credential ’siconURLto the empty string. -
Switch
on
credential
’s
primary
interface,
and
execute
the
associated
steps:
-
PasswordCredential -
-
If
the
user
agent’s
credential
manager
contains
a
PasswordCredentialstoredCredential whoseidattribute is credential ’sidand 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
nameto the value of credential ’sname. -
Set
storedCredential
’s
iconURLto 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
id,[[type]],name,iconURL,[[origin]]and[[password]]of credential in the credential store .
-
If
the
user
grants
origin
permission
to
store
credentials
(as
discussed
in
§ 5.1
Storing
and
Updating
Credentials
),
then
store
-
If
the
user
agent’s
credential
manager
contains
a
-
FederatedCredential -
-
If
the
user
agent’s
credential
manager
does
not
contain
a
FederatedCredentialstoredCredential whoseidattribute is credential ’sidand whoseproviderattribute is credential ’sproviderand whose[[origin]]slot is origin , then:-
If
the
user
grants
origin
permission
to
store
credentials
(as
discussed
in
§ 5.1
Storing
and
Updating
Credentials
),
store
id,[[type]],name,iconURL,[[origin]],providerandprotocolof credential in the credential store .
-
If
the
user
grants
origin
permission
to
store
credentials
(as
discussed
in
§ 5.1
Storing
and
Updating
Credentials
),
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
Promiseobject. - Return promise , and execute the remaining steps asynchronously.
-
Set
origin
’s
requires
user
mediation
flag
to
truein 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
schemeis 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
Credentialobjects. 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
falsewithout 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
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,Credentialobjects forwould not be returned directly tohttps://example.comhttps://example.com/, but could be offered to the user via the chooser.https://www.example.comhttps://www.example.com/ -
MAY
store
credentials
without
user
mediation
when
store()is called if the credential was retrieved via a user mediatedget()from a PSL-matched domain and new domain is still PSL-matching the original domain for which the credentials were stored.
https://www.example.com/
on
their
computer.
This
credential
is
synced
via
the
browser’s
syncing
technologies
to
the
user’s
phone.
The
user
visits
https://m.example.com/
.
The
browser
may
provide
the
stored
credential
to
https://m.example.com/
in
a
user
mediated
way
due
to
the
Public
Suffix
List
[PSL]
.
After
that
the
website
can
call
store()
and
the
browser
may
silently
store
a
second
credential
or
a
reference
that
allows
https://m.example.com/
to
retrieve
the
credential
without
user
mediation
in
the
future.
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 [CSP] 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-srcfurther restricts the origins to whichfetch()may submit data (which mitigates the risk of redirect-based attacks). -
child-srcrestricts 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.
[CSP]
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 [CSP] 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
chosen
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
ExampleCredentialthat inherits fromCredential, and define the value of its[[type]]slot:interface ExampleCredential : Credential { // Definition goes here. }; ... AllExampleCredentialobjects 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
CredentialRequestOptionsdictionary with a property name that matches the[[type]]slot’s value.dictionary ExampleCredentialRequestOptions { // Definition goes here. }; partial dictionary CredentialRequestOptions { ExampleCredentialRequestOptions? example; }; -
Add
ExampleCredentialto 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
ExampleCredentialto 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.