diff --git a/specs/credentialmanagement/index.src.html b/specs/credentialmanagement/index.src.html index b3bfb2be..56a8e34c 100644 --- a/specs/credentialmanagement/index.src.html +++ b/specs/credentialmanagement/index.src.html @@ -238,7 +238,7 @@

Password-based Sign-in

         navigator.credentials.get({
-          "types": [ "password" ]
+          "filters": { "password" : {} }
         }).then(
             function(credential) {
               if (!credential) {
@@ -248,7 +248,7 @@ 

Password-based Sign-in

// really be progressive enhancement on top of an existing form). return; } - if (credential.type == "PasswordCredential") { + if (credential.type == "password") { credential.send("https://example.com/loginEndpoint") .then(function (response) { // Notify the user that signin succeeded! Do amazing, signed-in things! @@ -269,16 +269,17 @@

Federated Sign-in

         navigator.credentials.get({
-          "types": [ "federated" ],
-          "options": {
-              "providers": [ "https://federation.com" ]
+          "filters": {
+              "federated": {
+                  "providers" : [ "https://federation.com" , "https://accounts.google.com/" ]
+              }
           }
         }).then(
             function(credential) {
               if (!credential)
                 return;
 
-              if (credential.type == "FederatedCredential") {
+              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
@@ -306,9 +307,10 @@ 

Federated Sign-in

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 - [[#future-work]] for directions future versions of this API could take. + or authenticate the user in any way against a federated identity provider. + It provides a hint to the website as to which identity provider the user + prefers to use, and little more. See [[#future-work]] for directions future + versions of this API could take.

Post-sign-in Confirmation

@@ -329,6 +331,7 @@

Post-sign-in Confirmation

credential.send("https://example.com/sign-in/") .then(function (response) { if (response.status == 200) + // Inspect response to determine if this was a successful login then... navigator.credentials.store(credential); });
@@ -432,7 +435,8 @@

Key Concepts and Terminology

authentication. Note: These two types are, of course, not exhaustive. Future versions of - this and other documents will likely define other types of credentials. + this and other documents will likely define other types of credentials and the + API allows developers to define their own custom credential types in script.
@@ -459,8 +463,15 @@

Key Concepts and Terminology

A federated identity provider is an entity which a website trusts to correctly authenticate a user, and which provides an - API for that purpose. OAuth2 [[RFC6749]] is an example of such an API, - used by a number of providers. + API for that purpose. OAuth2 [[RFC6749]] is an example of a framework that + defines such an API, used by a number of providers. +
+
+ Many federated identity providers expose their services using standardised + protocols such as OAuth2 but others may use proprietary or customised protocols. + It is possible therefor for a relying party to support any provider that follows + a particular standardised protocol or protocols, to support an explicit list of + providers or a combination of explicit providers and standard protocols.
@@ -501,16 +512,17 @@

Credential

interface Credential { readonly attribute DOMString id; readonly attribute DOMString type; + boolean isMatch (any filter); }; Credential implements Transferable;
-
id
+
id
The credential's identifier. This might be a GUID, username, or email address, for instance.
-
type
+
type
The credential's type. This attribute's getter returns the value of the credential's {{[[type]]}} slot. @@ -521,13 +533,25 @@

Credential

password, and {{FederatedCredential}} objects have a {{[[type]]}} of federated.
-
\[[type]]
+
\[[type]]
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 the {{Credential/type}} attribute.
+
isMatch()
+
+ {{Credential}} objects provide filter matching logic within the + isMatch() function. This hook allows different {{Credential}} + types to define how they will match the values from the {{CredentialRequestFilters}} + passed into the {{CredentialContainer/get()}} function. The base class implementation returns + true. +
+
+ Custom {{Credential}} interfaces defined in script can override this method as a way of influencing + the matching algorithm for custom classes. +
{{Credential}} objects implement {{Transferable}}, and MUST support the @@ -567,10 +591,10 @@

Credential

- All {{LocallyStoredCredential}} objects MUST define an options matching - algorithm which returns Match if the object matches an - {{CredentialRequest/options}} dictionary, and Does Not Match - otherwise. + All {{LocallyStoredCredential}} objects MUST define a filter matching + algorithm in the body of the {{Credential/isMatch()}} function. + The algorithm must return true if there is a Match otherwise false + if the {{Credential}} Does Not Match.

PasswordCredential

@@ -616,8 +640,8 @@ 
Matching Algorithm
- {{PasswordCredential}} objects' options matching algorithm always - returns Match. + {{PasswordCredential}} objects' filter matching algorithm always + returns true (Match).

FederatedCredential

@@ -626,10 +650,16 @@ 

FederatedCredenti DOMString? protocol; }; + dictionary FederatedCredentialFilter { + sequence<USVString> providers; + sequence<DOMString> protocols; + }; + [Constructor(FederatedCredentialData data), Exposed=Window] interface FederatedCredential { readonly attribute USVString provider; readonly attribute DOMString? protocol; + boolean isMatch ( FederatedCredentialFilter filter ); }; FederatedCredential implements LocallyStoredCredential;

@@ -645,6 +675,11 @@

FederatedCredenti "openidconnect"). If this value is null, then the protocol can be inferred from the {{FederatedCredential/provider}}. +
isMatch()
+
+ All {{FederatedCredential}} objects expect a {{FederatedCredentialFilter}} as the argument + passed to their {{Credential/isMatch()}} function. +
{{[[type]]}}
All {{FederatedCredential}} objects have their {{[[type]]}} @@ -656,23 +691,23 @@
Matching Algorithm
- {{FederatedCredential}} objects' options matching algorithm is as + {{FederatedCredential}} objects' filter matching algorithm is as follows. Given a {{FederatedCredential}} (credential) and a - {{CredentialRequest/options}} (options): + {{FederatedCredentialFilter}} (filter):
  1. - If options has a - {{FederatedCredentialRequestOptions/providers}} property: + If filter has a + {{FederatedCredentialFilter/providers}} property:
    1. - For each provider in options' + For each provider in filter's providers list:
      1. - Return Matches if credential's + Return true if credential's {{FederatedCredential/provider}} is a case-sensitive match for provider.
      2. @@ -681,17 +716,17 @@
    2. - If options has a - {{FederatedCredentialRequestOptions/protocols}} property: + If filter has a + {{FederatedCredentialFilter/protocols}} property:
      1. - For each protocol in options' - {{FederatedCredentialRequestOptions/protocols}} list: + For each protocol in filter's + {{FederatedCredentialFilter/protocols}} list:
        1. - Return Matches if credential's + Return true if credential's {{FederatedCredential/protocol}} is a case-insensitive match for protocol.
        2. @@ -700,7 +735,7 @@
      2. - Return Does Not Match. + Return false (Does Not Match).
      @@ -769,39 +804,50 @@

      {{CredentialContainer/get()}}, the caller specifies a few parameters in a {{CredentialRequest}} dictionary. - Note: The {{CredentialRequestOptions}} typedef is an extension point. If and - when new types of credentials are introduced that require options, their - dictionary types will be added to the typedef so they can be passed into the - request. -
      -    dictionary FederatedCredentialRequestOptions {
      -      sequence<USVString> providers;
      -      sequence<DOMString> protocols;
      +    interface CredentialFilterMap {
      +      readonly attribute unsigned long size;
      +
      +      getter any get(DOMString credentialType);
      +      setter creator void set(DOMString credentialType, any value);
      +      deleter void remove(DOMString type);
           };
       
      -    typedef FederatedCredentialRequestOptions CredentialRequestOptions;
      +    typedef CredentialFilterMap CredentialRequestFilters;
       
           dictionary CredentialRequest {
      -      CredentialRequestOptions options;
      +      CredentialRequestFilters filters;
             boolean suppressUI = false;
      -      sequence<DOMString> types;
           };
      +
         
      -
      -
      providers
      -
      - An array of federation identifiers. For details regarding valid formats - see [[#identifying-federations]]. -
      -
      types
      +
      filters
      - A sequence of strings specifying the types of {{Credential}} objects which - the caller wishes to obtain. An empty sequence signifies that the caller - will accept any credential type. For processing details, see the algorithm - defined in [[#request-credential]]. + A map containing a set of parameters which will be used to select + a particular {{Credential}} to return. +
      +
      + Each key in the map should correspond to the value of the + {{Credential/type}} property of any {{Credential}} type that + should be returned. +
      +
      + For each possible credential that can be returned: +
        +
      1. If the {{Credential/type}} is not a defined key then the {{Credential}} + is immediatley excluded.
      2. +
      3. If the {{Credential/type}} is a defined key then let V be the value in + the map stored under that key. Call {{Credential/isMatch()}} passing V + as the only argument. If the the function returns false then exclude + the {{Credential}}.
      4. +
      + Built-in {{Credential}} types define an filter matching algorithm for that credential type. +
      +
      + Custom {{Credential}} types can be defined in ECMAScript that override the default {{Credential/isMatch()}} + function and thereby provide custom matching logic.
      suppressUI
      @@ -812,12 +858,6 @@

      undefined. For processing details, see the algorithm defined in [[#request-credential]].

      -
      options
      -
      - An dictionary containing a set of parameters which will be used to select - a particular {{Credential}} to return. This process is described in each - {{Credential}} type's options matching algorithm. -
      @@ -827,9 +867,10 @@

             navigator.credentials.get({
      -        "types": [ "federated" ],
      -        "options": {
      -            "providers": [ "https://identity.example.com/" ]
      +        "filters": {
      +            "federated" : {
      +                "providers": [ "https://identity.example.com/" ]
      +            }
               }
             }).then(function (credential) {
               // ...
      @@ -843,9 +884,10 @@ 

             navigator.credentials.get({
      -        "types": [ "federated" ],
      -        "options": {
      -            "providers": [ "https://identity.example.com/" ]
      +        "filters": {
      +            "federated" : {
      +                "providers": [ "https://identity.example.com/" ]
      +            }
               },
               "suppressUI": true
             }).then(function (credential) {
      @@ -865,8 +907,8 @@ 

      Identifying providers

      agents to be helpful. For consistency, federations passed into the APIs defined in this document - (e.g. {{FederatedCredentialRequestOptions}}'s - {{FederatedCredentialRequestOptions/providers}} array, or + (e.g. {{FederatedCredentialFilter}}'s + {{FederatedCredentialFilter/providers}} array, or {{FederatedCredential}}'s {{FederatedCredential/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 @@ -932,18 +974,6 @@

  2. -
  3. - Let type be lowest common ancestor interface of the types - referenced in request's {{CredentialRequest/types}} property. - - ISSUE(w3c/webappsec#289): This is terribly hand-wavey. The intent is - clear, but I need to do the work to walk through the list of DOMStrings - and convert them to interfaces and etc. Busywork for later. -
  4. -
  5. - Return a Promise rejected with TypeMismatchError - if type is {{Credential}}. -
  6. Let promise be a newly created Promise object.
  7. @@ -960,9 +990,8 @@

  8. Let result be the result of executing [[#request-locallystoredcredential-without-mediation]], - passing in origin, request's - {{CredentialRequest/types}} property, and - request's {{CredentialRequest/options}} property. + passing in origin and + request's {{CredentialRequest/filters}} property.
  9. If result is not null, resolve @@ -985,9 +1014,8 @@

  10. Resolve promise with the result of executing [[#request-locallystoredcredential-with-mediation]], - passing in origin, request' - {{CredentialRequest/types}} property, and - request' {{CredentialRequest/options}} property. + passing in origin and + request' {{CredentialRequest/filters}} property.
@@ -1004,8 +1032,8 @@

{{LocallyStoredCredential}} isn't the only type? For example, how could an author pull some "RemoteCredential" that required interaction with a third-party, but fall back to a {{PasswordCredential}}? For the moment, we're - resolving this by rejecting the promise if the types listed in - {{CredentialRequest/types}} aren't all instances of the same type, but that + resolving this by rejecting the promise if the types listed as keys in + {{CredentialRequestFilters}} map aren't all instances of the same type, but that seems like something we'd want to fix.

@@ -1083,9 +1111,8 @@

Gather LocallyStoredCredentials

- Given an origin (origin), and a sequence of {{DOMString}} - (types), and an {{CredentialRequest/options}} dictionary - (options), this algorithm returns a sequence of + Given an origin (origin) and {{CredentialRequest/filters}} map + (filters), this algorithm returns a sequence of {{LocallyStoredCredential}} from the user agent's credential store which are potential candidates: @@ -1103,13 +1130,26 @@

based on vendor-specific heuristics (as described in [[#synthesis]]).
  • - Remove any items from credentials whose {{Credential/type}} - property is not a case-sensitive match for a value in types. -
  • -
  • - Remove any items from credentials whose options matching - algorithm returns Does Not Match when executed on - options. + For each {{Credential}} in credentials, let type be + the value of the {{Credential/type}} property of the {{Credential}}. +
      +
    1. Let filter be the value returned by calling {{CredentialFilterMap/get()}} + and passing type as the argument credentialType. +
        +
      1. + If filter is undefined remove the {{Credential}} from + credentials and process the next {{Credential}}. +
      2. +
      3. + Call {{Credential/isMatch()}} passing filter as the only parameter. +
      4. +
      5. + If the function returns false remove the {{Credential}} from + credentials and process the next {{Credential}}. +
      6. +
      +
    2. +
  • Return credentials. @@ -1120,9 +1160,8 @@

    Request a LocallyStoredCredential without user mediation

    - This algorithm accepts an origin (origin), a sequence of - type names (types) and an {{CredentialRequest/options}} dictionary - (options), and returns either a single {{LocallyStoredCredential}} + This algorithm accepts an origin (origin) and {{CredentialRequest/filters}} + map (filters), and returns either a single {{LocallyStoredCredential}} object if and only if one can be provided without user mediation, or null if not. @@ -1137,8 +1176,8 @@

  • Let credentials be the result of executing - [[#gather-locallystoredcredentials]] on origin, - types, and options. + [[#gather-locallystoredcredentials]] on origin + and filters.
  • If credentials is empty, or contains more than one @@ -1165,16 +1204,15 @@

    Request a LocallyStoredCredential with user mediation

    - This algorithm accepts an origin (origin), a sequence of - type names (types) and an {{CredentialRequest/options}} dictionary - (options), and returns either a single {{LocallyStoredCredential}} + This algorithm accepts an origin (origin) and {{CredentialRequest/filters}} + map (filters), and returns either a single {{LocallyStoredCredential}} object, or null if none can be provided.
    1. Let credentials be the result of executing - [[#gather-locallystoredcredentials]] on origin, - types, and options. + [[#gather-locallystoredcredentials]] on origin + and filters.
    2. This requirement only applies if we would show UI to the user. If she's elected to allow credentials to be provided without user @@ -1491,7 +1529,7 @@

      Requiring User Mediation

      {{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 convinience, but which might nevertheless surprise the user. + 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 @@ -1578,8 +1616,8 @@

      Synthesizing Credentials

    3. Switch on the interface whose {{Credential/type}} property matches one of - the items in the {{CredentialRequest}} {{CredentialRequest/types}} - sequence, and execute the associated steps: + the keys in the {{CredentialRequest}}'s {{CredentialRequest/filters}} + map, and execute the associated steps:
      {{FederatedCredential}}
      @@ -1590,7 +1628,7 @@

      Synthesizing Credentials

      1. - Let providers be the value of options' + Let providers be the value of filter's providers property.
      2. @@ -1599,8 +1637,8 @@

        Synthesizing Credentials

        1. - For each provider in options' - {{FederatedCredentialRequestOptions/providers}} property: + For each provider in filters' + {{FederatedCredentialFilter/providers}} property:
          1. @@ -1649,7 +1687,7 @@

            Synthesizing Credentials

            being a bit counterproductive, as the site needs to display a list of dozens of providers, hoping that users will see an icon they remember. - A call to {{get()}} will end up with an empty list of {{Credential}} objects + A call to {{CredentialContainer/get()}} will end up with an empty list of {{Credential}} objects for new users, as they won't have any https://example.com/ credentials. They might, however, have a {{PasswordCredential}} stored for the exciting social media site https://federation.com/, which @@ -1662,9 +1700,9 @@

            Synthesizing Credentials

            it to the user as a sign-up options in a chooser. In this way, https://example.com/ can support dozens of - providers as arguments to {{get()}} (via - {{FederatedCredentialRequestOptions}}'s - {{FederatedCredentialRequestOptions/providers}} property), and the user + providers as arguments to {{CredentialContainer/get()}} (via + {{FederatedCredentialFilter}}'s + {{FederatedCredentialFilter/providers}} property), and the user agent can cull that list down to the federations with which the user regularly interacts (or which have been previously used as {{FederatedCredential}}s, or any of a number of other heuristics). @@ -1801,16 +1839,16 @@

            Privacy Considerations

            Timing Attacks

            - If the user has no credentials for an origin, a call to {{get()}} will + If the user has no credentials for an origin, a call to {{CredentialContainer/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 - {{FederatedCredentialRequestOptions/providers}} array. The risk is mitigated - by the fact that a user-mediated {{get()}} is tied to a user gesture, and the + {{CredentialContainer/get()}} with a single item in the + {{FederatedCredentialFilter/providers}} array. The risk is mitigated + by the fact that a user-mediated {{CredentialContainer/get()}} is tied to a user gesture, and the user would, sooner or later, be prompted to provide credentials to the site, which would certainly raise her suspicions as to its behavior. @@ -1875,19 +1913,23 @@

            Extension Points

                       interface ExampleCredential : Credential {
                         // Definition goes here.
            +
                       };
                     
          2. - Define the options that the new credential type requires, and add them - to the {{CredentialRequestOptions}} typedef: + Define a strongly typed filter for the new credential type, and override the isMatch() + function:
            -          dictionary ExampleCredentialRequestOptions {
            +          dictionary ExampleCredentialFilter {
                         // Definition goes here.
                       };
             
            -          typedef (FederatedCredentialRequestOptions or ExampleCredentialRequestOptions) CredentialRequestOptions;
            +          partial interface ExampleCredential {
            +            boolean isMatch( ExampleCredentialFilter filter);
            +          };
            +