RE: API consumer question: How do we recover Credential?

Your thinking isn’t flawed at all AFAICT. These are great questions.

  1.  How do we want people to serialize/deserialize Credential objects?
You’ve hit on something that we discussed a long time ago and then just forgot about over time. The short answer is that the existing Web IDL is technically wrong.
The right way to do this is that Interface should be reserved for honest-to-goodness objects which are created by the UA and returned from various things. Input parameters should be defined as Dictionary. See http://www.w3.org/TR/api-design/#when-to-use

So to do this really truly right we would create a dictionary type with the same fields as the Credential interface, and then use JSON.parse just as you have in your sample code below. We had discussed this a long time ago but we left the IDL in the present form because it’s simpler to read and it avoids us having to explain to everyone why we have an interface and a dictionary type with the same fields. Obviously we should fix this before CR. Maybe I can roll this into the IDL update I’m doing right now as part of #84.

  1.  For getAssertion(), whitelist is optional. But without it, how would U2F-style authenticators get their wrapped private key to do work?
They wouldn’t. This means that U2F authenticators cannot possibly do a first-factor scenario on a new device. But we knew that ☺

The question to you would be – do you think there is enough information in the current API so that an RP can know when it has only U2F authenticators registered, so it can do a reasonable UI?

From: J.C. Jones [mailto:jc@mozilla.com]
Sent: Wednesday, July 13, 2016 4:20 PM
To: W3C WebAuthn WG <public-webauthn@w3.org>
Subject: API consumer question: How do we recover Credential?

I'm working my head through using WebAuthn for a U2F-like use case and hit a snag: What are the right ways to serialize and deserialize the Credentials which come from makeCredential(), so we have them handy for getAssertion()?
The Credential interface in WebIDL consists of two read-only attributes; I don't believe it's possible to deserialize something to match that interface using JSON.parse(), but I could be wrong.
Let me walk through a sketch of this, just for everyone's reference. Note: the JS is imperfect, and I didn't do all the necessary incantations for IndexedDB -- it's just a sketch. :)
(It's online / colorized here: https://jsfiddle.net/ad3ssofo/5/ )
First, we need to make a new credential:


/* Called when a new authenticator is being added to the account */
function addAuthenticator(account, params, challenge) {
  let wauthn = navigator.authentication;
  wauthn.makeCredential(account, params, challenge)
    .then(function(scopedCred) {
      /* Send the Scoped Cred info to the RP */
      transmitCredToRP(scopedCred);

      /* Cache in IndexedDB */
      let dbReq = window.indexedDB.open("appDB", 1);

      dbReq.onsuccess = function() {
          let db = DBOpenRequest.result;
          let objectStore = db.createObjectStore("credStore");
          objectStore.put(scopedCred.credential, "credential");
      }
    });
}
...which calls the stub:

/* Send the scoped credential data to the RP */
function transmitCredToRP(scopedCred) {
  let serialized = JSON.stringify(scopedCred);
  /* send(serialized); */
}
All's well, so far, I guess. There's no reason a Scoped Credential can't be serialized to JSON. When we go to generate the assertion though:

/* Called when an authenticator is being asked to prove possession */
function authenticate(serializedCredential) {
  let wauthn = navigator.authentication;

  let whitelist = [];
  /* Decode a Credential from a serialized form from the RP */
  whitelist.push(deserializeCredential(serializedCredential));

  /* Maybe could also come via IndexedDB? */
  let dbReq = window.indexedDB.open("appDB", 1);

  dbReq.onsuccess = function() {
      let db = DBOpenRequest.result;
      let objectStore = db.createObjectStore("credStore");
      whitelist.push(objectStore.get("credential"))
  }

  /* Whitelist is optional, but if it's not provided, how would
     a U2F-type authenticator obtain its' private key? */
  wauthn.getAssertion(challenge, whitelist)
    .then(function(assertion) {
      /* Transmit the assertion object to the RP */
      transmitAssertionToRP(assertion)
    });
}
...which uses the stubs:

/* Send the assertion data to the RP */
function transmitAssertionToRP(assertion) {
  let serialized = JSON.stringify(assertion);
  /* send(serialized); */
}

/* Deserialize a Credential object from the RP for re-use */
function deserializeCredential(serializedCredential) {
  /* DOES THIS WORK? Or do we need a constructor? */
  let credentialObj = JSON.parse(serializedCredential);
  return credentialObj;
}
So the problems I'm hitting here are:

  1.  How do we want people to serialize/deserialize Credential objects?
  2.  For getAssertion(), whitelist is optional. But without it, how would U2F-style authenticators get their wrapped private key to do work?
Where's my thinking flawed?
Thanks,
J.C.

Received on Wednesday, 13 July 2016 23:48:12 UTC