WICG / digital-credentials

Digital Credentials, like driver's licenses
https://wicg.github.io/digital-credentials/
Other
82 stars 9 forks source link

How much is really in common between different `IdentityCredential` types? #14

Open RByers opened 1 year ago

RByers commented 1 year ago

One piece of feedback we've heard is that it seems premature to unify different credential types into IdentityCredential if all they're really sharing is a nonce and response and if the IdentityCredentialRequestOptions is simply a list of unrelated queries.

On the one hand, the main reason we want to unify is so that we can enable credential-picker UIs which list different types of identity credentials all together. This doesn't actually require any unification beyond a common API call (or some other way to group multiple calls into a single UI transaction).

On the other hand, there are actually things we expect to be in common, so perhaps it's worth trying to sketch that out in more detail. In particular, is it useful to think of different layers in the identity-verifying application stack and ask if abstraction can help simplify the design of some components (including the browser itself). Eg. if we add abstraction across selective disclosure, filtering and/or UI presentment of disparate credentials, does that provide sufficient concrete benefit to justify the abstraction?

samuelgoto commented 1 year ago

That’s a great question, and one that deserves some active exploration.

I think it is very important to acknowledge that is still an open question. There are many ways to go about this that we noted in this Alternatives Considered section. Most notably, introducing new Credential Manager Credentials, like an MDocCredential and a VerifiableCredential, or a WalletCredential to unify the two but keep them separate from IdentityCredential.

But, just to give you a sense of the intuition, here some of the common user journeys that I think are compelling:

I think some of the common technical characteristics of providers of identity credentials share are:

There are many architectural components that I think is similar between federation and wallets:

So, for example, in the Student Affiliation user flow, here is how a website would be able to gather either an idtoken from a federated provided or a local credential in a wallet on the device:

// An example of requesting a federated identity or an mdoc 
const student = await navigator.credentails.get({
  identity: {
    // The selector field tells the providers that is being asked.
    // It also tells the platform how to filter the registered credentials
    // and construct the identity selector.
    selector: {
      fields: ["name", "email", "photo"],
    }
    providers: [{
        responseType: "saml",
        configURL: "https://pudue.edu/fedcm.json",
        cliendID: "nature.com",
        request: {
          // These are opaque fields that are passed to the 
          // holder after selection.
          nonce: "1234",
        }
      }, {
        responseType: "mdoc",
        request: {
          // These are opaque fields that are passed to the 
          // holder after selection.
          readerPublicKey: "ftl+VEHPB17r2 ... Nioc9QZ7X/6w...",
          desiredStorageDuration: {
            days: 7,
          },
        }
    }]
  }
});

Another use case that comes up often is to be able to exchange age brackets. Here is how this could be accomplished on a website that accepts age assertions from high-assurance governments as well as low-assurance social networks.

// An example of requesting a federated identity or an mdoc 

const idtoken = await navigator.credentails.get({
  identity: {
    selector: {
      fields: ["age_over_18"],
    },
    request: {
      nonce: "1234",
    }
    providers: [{
      responseType: "idtoken",
      configURL: "https://facebook.com/fedcm.json",
      cliendID: "tiktok-2343",
    }, {
      responseType: "idtoken",
      configURL: "https://google.com/fedcm.json",
      cliendID: "tiktok-10902",
    }, {
      responseType: "mdoc",
      request: {
        readerPublicKey: "ftl+VEHPB17r2 ... Nioc9QZ7X/6w...",
        desiredStorageDuration: {
          days: 7,
        },
      }
    }]
  }
});

There are many occasions, clearly, that credentials are going to unique available in certain formats, so it is key to make sure that it is possible to ask them in isolation. For example:

// An example of a government-issued drivers license requested in isolation.
const mdl = await navigator.credentails.get({
  identity: {
    providers: [{
      // I'd like to get back an mDoc
      responseType: "mdoc",
      selector: {
        documentType: "org.iso.18013.5.1.mDL",
        namespace: "org.iso.18013.5.1",
        fields: [
          "document_number",
          "portrait",
          "driving_privileges",
          ".aamva.organ_donor",
          ".aamva.hazmat_endorsement_expiration_date",
        ],
      },
      request: {
        readerPublicKey: "ftl+VEHPB17r2 ... Nioc9QZ7X/6w...",
        nonce: "1234",
        desiredStorageDuration: {
          days: 7,
        },
      }
    }]
  }
});

Alternatively, there are also many situations when you’d only want to get things from federated providers, so here is what that could look like:

// An example of a federeated identity requested in isolation.
const idtoken = await navigator.credentails.get({
  identity: {
    providers: [{
      // I'd like to get a JWT
      responseType: "idtoken",
      // Here is what I need
      selector: {
        fields: ["name", "email" "photo"],
      },
      configURL: "https://idp.com/fedcm.json",
      cliendID: "567",
      request: {
        // These are opaque parameters that the platform passes to the IdP
        opaque: "parameter",
        foo: "bar",
        nonce: "1234",
      }
    }]
  }
});

This is just a draft, but here is an idea of what would look like from a WebIDL perspective:

// IdentityCredential WebIDL

enum ResponseType {};

typedef (
   USVString or 
   IdentityCredentialStructuredSelector
) IdentityCredentialSelector;

// https://fedidcg.github.io/FedCM/#dictdef-identitycredentialrequestoptions
dictionary IdentityCredentialRequestOptions {
  // A querying language that allows an RP to ask what it wants from the providers.
  IdentityCredentialSelector selector;
  required sequence<IdentityProviderConfig> providers;
};

dictionary IdentityProviderConfig { 
  IdentityCredentialSelector selector;
  sequence<ResponseType> responseType;

  // maybe the following should be in params
  // sequence<USVString> scope;
  // USVString nonce;
  record<USVString, USVString> request;
};

typedef (
   USVString or 
   IdentityStructuredAttribute;
) IdentitySCope;

dictionary IdentityCredentialStructuredSelector {
  optional USVString documentType;
  optional USVString namespace;
  optional sequence<IdentityScope> scope;
}

dictionary IdentityScope {
  optional USVString namespace;
  SVString name;
  SVString value;
  bool optional = false;
}

Much like in the Credential Manager spec, APIs are expected to extend the Identity Credential spec.

For example, here is what the FedCM could look like:

// ======= separate and independent spec =======
// FedCM API
// ==========================================

// This is an extension of ResponseType.
// Unfortunately, WebIDL does not support partial enums (yet).

partial enum ResponseType {
  "idtoken",
}

partial dictionary IdentityProviderConfig {
  // URL for the Identity Provider Configuration.
  required USVString configURL;
  required USVString clientId;
}

partial dictionary IdentityCredentialSelector {
  DOMString loginHint;
  DOMString hostedDomain;
}

In the same way, a mobile document request could also extend the IdentityCredential spec in the following way:

// ======= separate and independ spec =======
// mDocs API
// ==========================================

// This is an extension of ResponseType.
// Unfortunately, WebIDL does not support partial enums (yet).
partial enum ResponseType {
  "mdoc",
}

partial dictionary IdentityCredentialSelector {
  // mdocs-specific selection language
}

And, similarly, extensions for verifiable credentials could be introduced independently:

// ======= separate and independ spec =======
// VC API
// ==========================================

// This is an extension of ResponseType.
// Unfortunately, WebIDL does not support partial enums (yet).
partial enum ResponseType {
  "vc",
}
dwaite commented 1 year ago

A credential like password or public-key is a pairwise, negotiated credential as part of a relationship between two parties. It is an authentication credential, e.g. "proof I'm the same person as last time". Since the assumption is the parties already negotiated some concept of identity between the two (say, the user account on a service), there is no need to negotiate any other information. PublicKeyCredential makes these pairwise pseudonymous, while a password manager tries its best to do so as part of good hygiene. These are two-party typically - although there are certainly arguments that a password/passkey manager is another interesting party to the service being accessed.

An identity credential is typically three party, issued by an authority of some sort. It is a brokered trust model, where the recipient is making risk decisions based on the issuing authority and the asserted data. They are otherwise rather diverse in terms of capabilities. The credentials in federated identity protocols, verifiable credentials, mDOC, and even certificates issued by client CAs all could live under this definition. These identity credentials may not cross full organizational boundaries either - organizations often structure both public and private infrastructure into a hierarchy with central identity management for efficiency, maintainability and security attack surface reasons.

How identity credentials are leveraged is not necessarily a function of their capabilities - while you certainly cannot make decisions on information which is not present, what you do with the contained data is ultimately a risk decision and part of that particular business domain. They are not directly related to the technical capabilities of the underlying protocol - e.g. relying on federated identity credentials for authentication comes down to a unilateral/bilateral trust relationship or legal agreement between parties.

In terms of selection, two-party credentials take the target origin as input so that UX can filter credentials which have been negotiated between the user/user agent and that party. Three party credentials instead would be filtered by the requested issuers/issuing frameworks (e.g. example.com specifically, or any AAMVA driving license issuers) and based on the data contained within - although it will be common to need the target origin after selection.

While the data structure and contents are both domain and protocol specific, issuing authorities do understand the types of credentials they wish to issue, their contents, and their intended usage. A group of issuing authorities may also standardize around these decisions.

In terms of filtering identity credentials within a selector, it is likely better to filter by acceptable issuer and filter by acceptable type(s) of credential (e.g. mdoc with international driving license and AAMVA extension data) rather than on attributes (age over 25). The assumption is that the selector does not know the credential data, the issuer, or the business cases/domain, so the selector is only capable of "hiding" options. The underlying credential provider would be what is capable of informing the user why they are invalid.

While it is more clicks, it is likely a better experience to let the credential provider explain why their driving license is not acceptable to e.g. rent a vehicle, rather than for their perfectly valid driving license to not show up as an option at all.

Outside of filtering by issuer, type, and capabilities (namespace and doctype in mdoc-land), further instructions on requirements and requested disclosure/release as well as business/regulatory controlled instructions like intention to retain is all part of defining the credential itself and how to request it from the issuers. Further information on the request could potentially be passed through to the credential provider without processing.

OR13 commented 1 year ago

VerifiableCredential is an RDF class.... what are the other things?

samuelgoto commented 1 year ago

VerifiableCredential isn't a web-exposed name though, is it?

OR13 commented 1 year ago

@samuelgoto its a normative property of the w3c vc data model:

https://w3c.github.io/vc-data-model/#dfn-type

In the case of IdentityCredential as "a more specific verifiable credential type"... is "IdentityCredential" also an RDF Class (yes)... is it a web-exposed name?

I don't know.

it also shows up here:

https://datatracker.ietf.org/doc/html/draft-ietf-oauth-sd-jwt-vc-00#section-4.2.2.1

Of course you can model a credential with and without RDF... but if you are modeling credentials as RDF, and you see this:

"type": ["VerifiableCredential", "IdentityCredential"]

You know the JSON-LD is attempting to be both a https://w3c.github.io/vc-data-model/#dfn-conforming-document

and an "IdentityCredential"... represented as a "conforming document"... hence it being a more specific type of credential, and following RDF naming conventions for compact JSON-LD documents.... see also:

https://www.w3.org/TR/json-ld11/#specifying-the-type

Basically, I understand what type and @type mean, in the context of RDF / JSON-LD.

I don't understand how these type systems connect to this work item, and its possible that they don't connect at all, and these are just 2 totally different title / pascal cased arbitrary strings.

dlongley commented 1 year ago

In the case of IdentityCredential as "a more specific verifiable credential type"... is "IdentityCredential" also an RDF Class (yes)... is it a web-exposed name?

I suspect this is backwards from how it would be modeled. An IdentityCredential (or whatever ends up being a web-exposed name via WebIDL), would be some kind of container with properties that could potentially hold things such as Verifiable Credentials.

The way this is modeled in CHAPI today is that there is a WebCredential (instead of an IdentityCredential) that has dataType and data properties, with one option for dataType being the string VerifiablePresentation and data then holding a Verifiable Presentation (which, in turn, can have N-many Verifiable Credentials). Given the actual interactions between a relying party and the user agent, it made more sense to have the web-exposed "credential" interface provide access to a Verifiable Presentation in this way than to VCs directly. This is true for both get() and store() operations.

I will continue to note that this gets a bit confusing / awkward with the reuse of the term "credential" in a variety of different ways in this space, but the point is that the number of types of Verifiable Credentials is unbounded, so it makes little sense to expose VCs or their subtypes to the web (in WebIDL). Some kind of wrapper around them makes more sense to create an interface for use with existing (or new) Web APIs to simply transport them to/from digital wallets.

OR13 commented 1 year ago

In that case IdentityCredential would be some abstract class, not an RDF class... and the web APIs would not need to know anything about the interface, beyond that it was CBOR (mDoc), JSON (data integrity proof), or SD-JWT moving around, there would be no "browser awareness" of RDF at all.

On the other side of the pipe, the wallet might error when it realized it got a credential that was malformed, or attempted to redefine an existing term, but that would only happen in the credential payload was actually processed as RDF, and that would be up the verifier / RP, as it is today... for example, some RPs might reject certain timestamps, or certain ciphersuites, like RSA or EdDSA.

The IdentityCredential interface, only needs to cover the APIs that are exposed to developers, which in turn depend on the requirements needed to satisfy digital credential formats.

At a minimum:

The existing types this issue comments on are:

  1. application/jwt ... id_token (compact encoded JWTs)
  2. application/cose (or some mdoc media types, I don't pay to read mdoc...)... mdoc (COSE Sign1 CBOR)
  3. application/vc+ld+json JSON-LD secured with data integrity
  4. application/vc+ld+json+sd-jwt JSON-LD secured with sd-jwt
  5. application/vc+sd-jwt JSON secured with sd-jwt

Seems like there is good alignment on the key binding side (need to identify the confirmation key, maybe we could agree to a shared way to do that).

I'd say aim for a good JSON or CBOR interface for IdentityCredential, don't make it an awkward mix like WebAuthN did, if you can avoid it.