bug 1332681 - part 4/4 - convert authentication.getAssertion to credentials.get r?jcj,qdot draft
authorDavid Keeler <dkeeler@mozilla.com>
Tue, 23 May 2017 14:55:10 -0700
changeset 590357 ce53b9d5851576ad00dbf752e9207b68bd0151a4
parent 590356 7524671e6792ce63d9d290bd42c0a1851d4771b8
child 632204 e3404776dd8fb1b46ac0debc914012940c090c45
push id62720
push userbmo:dkeeler@mozilla.com
push dateWed, 07 Jun 2017 18:21:33 +0000
reviewersjcj, qdot
bugs1332681
milestone55.0a1
bug 1332681 - part 4/4 - convert authentication.getAssertion to credentials.get r?jcj,qdot MozReview-Commit-ID: 13EqlQVQApx
dom/base/Navigator.cpp
dom/base/Navigator.h
dom/credentialmanagement/CredentialsContainer.cpp
dom/credentialmanagement/CredentialsContainer.h
dom/webauthn/WebAuthentication.cpp
dom/webauthn/WebAuthentication.h
dom/webauthn/WebAuthnManager.cpp
dom/webauthn/WebAuthnManager.h
dom/webauthn/moz.build
dom/webauthn/tests/test_webauthn_get_assertion.html
dom/webauthn/tests/test_webauthn_loopback.html
dom/webauthn/tests/test_webauthn_make_credential.html
dom/webauthn/tests/test_webauthn_no_token.html
dom/webauthn/tests/test_webauthn_sameorigin.html
dom/webidl/CredentialManagement.webidl
dom/webidl/Navigator.webidl
dom/webidl/WebAuthentication.webidl
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -44,17 +44,16 @@
 #include "mozilla/dom/Presentation.h"
 #include "mozilla/dom/ServiceWorkerContainer.h"
 #include "mozilla/dom/StorageManager.h"
 #include "mozilla/dom/TCPSocket.h"
 #include "mozilla/dom/URLSearchParams.h"
 #include "mozilla/dom/VRDisplay.h"
 #include "mozilla/dom/VRDisplayEvent.h"
 #include "mozilla/dom/VRServiceTest.h"
-#include "mozilla/dom/WebAuthentication.h"
 #include "mozilla/dom/workers/RuntimeService.h"
 #include "mozilla/Hal.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/SSE.h"
 #include "mozilla/StaticPtr.h"
 #include "Connection.h"
 #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
 #include "nsGlobalWindow.h"
@@ -201,17 +200,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPermissions)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGeolocation)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotification)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBatteryManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBatteryPromise)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPowerManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConnection)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStorageManager)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAuthentication)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCredentials)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaDevices)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTimeManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorkerContainer)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeySystemAccessManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresentation)
@@ -1998,25 +1996,16 @@ Navigator::GetPresentation(ErrorResult& 
       return nullptr;
     }
     mPresentation = Presentation::Create(mWindow);
   }
 
   return mPresentation;
 }
 
-WebAuthentication*
-Navigator::Authentication()
-{
-  if (!mAuthentication) {
-    mAuthentication = new WebAuthentication(GetWindow());
-  }
-  return mAuthentication;
-}
-
 CredentialsContainer*
 Navigator::Credentials()
 {
   if (!mCredentials) {
     mCredentials = new CredentialsContainer(GetWindow());
   }
   return mCredentials;
 }
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -37,17 +37,16 @@ class systemMessageCallback;
 class MediaDevices;
 struct MediaStreamConstraints;
 class WakeLock;
 class ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams;
 class ServiceWorkerContainer;
 class DOMRequest;
 struct FlyWebPublishOptions;
 struct FlyWebFilter;
-class WebAuthentication;
 class CredentialsContainer;
 } // namespace dom
 } // namespace mozilla
 
 //*****************************************************************************
 // Navigator: Script "navigator" object
 //*****************************************************************************
 
@@ -221,17 +220,16 @@ public:
                               MozGetUserMediaDevicesSuccessCallback& aOnSuccess,
                               NavigatorUserMediaErrorCallback& aOnError,
                               uint64_t aInnerWindowID,
                               const nsAString& aCallID,
                               ErrorResult& aRv);
 
   already_AddRefed<ServiceWorkerContainer> ServiceWorker();
 
-  mozilla::dom::WebAuthentication* Authentication();
   mozilla::dom::CredentialsContainer* Credentials();
 
   void GetLanguages(nsTArray<nsString>& aLanguages);
 
   bool MozE10sEnabled();
 
   StorageManager* Storage();
 
@@ -291,17 +289,16 @@ private:
   RefPtr<nsPluginArray> mPlugins;
   RefPtr<Permissions> mPermissions;
   RefPtr<Geolocation> mGeolocation;
   RefPtr<DesktopNotificationCenter> mNotification;
   RefPtr<battery::BatteryManager> mBatteryManager;
   RefPtr<Promise> mBatteryPromise;
   RefPtr<PowerManager> mPowerManager;
   RefPtr<network::Connection> mConnection;
-  RefPtr<WebAuthentication> mAuthentication;
   RefPtr<CredentialsContainer> mCredentials;
   RefPtr<MediaDevices> mMediaDevices;
   RefPtr<time::TimeManager> mTimeManager;
   RefPtr<ServiceWorkerContainer> mServiceWorkerContainer;
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
   RefPtr<Presentation> mPresentation;
   RefPtr<GamepadServiceTest> mGamepadServiceTest;
   nsTArray<RefPtr<Promise> > mVRGetDisplaysPromises;
--- a/dom/credentialmanagement/CredentialsContainer.cpp
+++ b/dom/credentialmanagement/CredentialsContainer.cpp
@@ -30,16 +30,24 @@ CredentialsContainer::~CredentialsContai
 
 JSObject*
 CredentialsContainer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return CredentialsContainerBinding::Wrap(aCx, this, aGivenProto);
 }
 
 already_AddRefed<Promise>
+CredentialsContainer::Get(const CredentialRequestOptions& aOptions)
+{
+  RefPtr<WebAuthnManager> mgr = WebAuthnManager::GetOrCreate();
+  MOZ_ASSERT(mgr);
+  return mgr->GetAssertion(mParent, aOptions.mPublicKey);
+}
+
+already_AddRefed<Promise>
 CredentialsContainer::Create(const CredentialCreationOptions& aOptions)
 {
   RefPtr<WebAuthnManager> mgr = WebAuthnManager::GetOrCreate();
   MOZ_ASSERT(mgr);
   return mgr->MakeCredential(mParent, aOptions.mPublicKey);
 }
 
 } // namespace dom
--- a/dom/credentialmanagement/CredentialsContainer.h
+++ b/dom/credentialmanagement/CredentialsContainer.h
@@ -26,16 +26,18 @@ public:
   {
     return mParent;
   }
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   already_AddRefed<Promise>
+  Get(const CredentialRequestOptions& aOptions);
+  already_AddRefed<Promise>
   Create(const CredentialCreationOptions& aOptions);
 
 private:
   ~CredentialsContainer();
 
   nsCOMPtr<nsPIDOMWindowInner> mParent;
 };
 
deleted file mode 100644
--- a/dom/webauthn/WebAuthentication.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "mozilla/dom/WebAuthentication.h"
-#include "mozilla/dom/WebAuthnManager.h"
-
-namespace mozilla {
-namespace dom {
-
-// Only needed for refcounted objects.
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebAuthentication, mParent)
-NS_IMPL_CYCLE_COLLECTING_ADDREF(WebAuthentication)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(WebAuthentication)
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebAuthentication)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-WebAuthentication::WebAuthentication(nsPIDOMWindowInner* aParent) :
-  mParent(aParent)
-{
-  MOZ_ASSERT(aParent);
-}
-
-WebAuthentication::~WebAuthentication()
-{}
-
-JSObject*
-WebAuthentication::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
-{
-  return WebAuthenticationBinding::Wrap(aCx, this, aGivenProto);
-}
-
-already_AddRefed<Promise>
-WebAuthentication::GetAssertion(const ArrayBufferViewOrArrayBuffer& aChallenge,
-                                const AssertionOptions& aOptions)
-{
-  RefPtr<WebAuthnManager> mgr = WebAuthnManager::GetOrCreate();
-  MOZ_ASSERT(mgr);
-  return mgr->GetAssertion(mParent, aChallenge, aOptions);
-}
-
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/dom/webauthn/WebAuthentication.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_dom_WebAuthentication_h
-#define mozilla_dom_WebAuthentication_h
-
-#include "mozilla/dom/WebAuthenticationBinding.h"
-
-namespace mozilla {
-namespace dom {
-
-struct Account;
-class ArrayBufferViewOrArrayBuffer;
-struct AssertionOptions;
-struct ScopedCredentialOptions;
-struct ScopedCredentialParameters;
-
-} // namespace dom
-} // namespace mozilla
-
-namespace mozilla {
-namespace dom {
-
-class WebAuthentication final : public nsISupports
-                              , public nsWrapperCache
-{
-public:
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebAuthentication)
-
-  explicit WebAuthentication(nsPIDOMWindowInner* aParent);
-
-  nsPIDOMWindowInner*
-  GetParentObject() const
-  {
-    return mParent;
-  }
-
-  virtual JSObject*
-  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
-
-  already_AddRefed<Promise>
-  GetAssertion(const ArrayBufferViewOrArrayBuffer& assertionChallenge,
-               const AssertionOptions& options);
-private:
-  ~WebAuthentication();
-
-  already_AddRefed<Promise> CreatePromise();
-  nsresult GetOrigin(/*out*/ nsAString& aOrigin);
-
-  nsCOMPtr<nsPIDOMWindowInner> mParent;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_WebAuthentication_h
--- a/dom/webauthn/WebAuthnManager.cpp
+++ b/dom/webauthn/WebAuthnManager.cpp
@@ -451,18 +451,17 @@ void
 WebAuthnManager::StartSign() {
   if (mChild) {
     mChild->SendRequestSign(mInfo.ref());
   }
 }
 
 already_AddRefed<Promise>
 WebAuthnManager::GetAssertion(nsPIDOMWindowInner* aParent,
-                              const ArrayBufferViewOrArrayBuffer& aChallenge,
-                              const AssertionOptions& aOptions)
+                              const PublicKeyCredentialRequestOptions& aOptions)
 {
   MOZ_ASSERT(aParent);
 
   MaybeClearTransaction();
 
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aParent);
 
   ErrorResult rv;
@@ -477,21 +476,21 @@ WebAuthnManager::GetAssertion(nsPIDOMWin
     promise->MaybeReject(rv);
     return promise.forget();
   }
 
   // If timeoutSeconds was specified, check if its value lies within a
   // reasonable range as defined by the platform and if not, correct it to the
   // closest value lying within that range.
 
-  double adjustedTimeout = 30.0;
-  if (aOptions.mTimeoutSeconds.WasPassed()) {
-    adjustedTimeout = aOptions.mTimeoutSeconds.Value();
-    adjustedTimeout = std::max(15.0, adjustedTimeout);
-    adjustedTimeout = std::min(120.0, adjustedTimeout);
+  uint32_t adjustedTimeout = 30000;
+  if (aOptions.mTimeout.WasPassed()) {
+    adjustedTimeout = aOptions.mTimeout.Value();
+    adjustedTimeout = std::max(15000u, adjustedTimeout);
+    adjustedTimeout = std::min(120000u, adjustedTimeout);
   }
 
   nsCString rpId;
   if (!aOptions.mRpId.WasPassed()) {
     // If rpId is not specified, then set rpId to callerOrigin, and rpIdHash to
     // the SHA-256 hash of rpId.
     rpId.Assign(NS_ConvertUTF16toUTF8(origin));
   } else {
@@ -529,17 +528,17 @@ WebAuthnManager::GetAssertion(nsPIDOMWin
     return promise.forget();
   }
 
   // Use assertionChallenge, callerOrigin and rpId, along with the token binding
   // key associated with callerOrigin (if any), to create a ClientData structure
   // representing this request. Choose a hash algorithm for hashAlg and compute
   // the clientDataJSON and clientDataHash.
   CryptoBuffer challenge;
-  if (!challenge.Assign(aChallenge)) {
+  if (!challenge.Assign(aOptions.mChallenge)) {
     promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
     return promise.forget();
   }
 
   nsAutoCString clientDataJSON;
   srv = AssembleClientData(origin, challenge, clientDataJSON);
   if (NS_WARN_IF(NS_FAILED(srv))) {
     promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
@@ -555,23 +554,23 @@ WebAuthnManager::GetAssertion(nsPIDOMWin
   srv = HashCString(hashService, clientDataJSON, clientDataHash);
   if (NS_WARN_IF(NS_FAILED(srv))) {
     promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
     return promise.forget();
   }
 
   // Note: we only support U2F-style authentication for now, so we effectively
   // require an AllowList.
-  if (!aOptions.mAllowList.WasPassed()) {
+  if (aOptions.mAllowList.Length() < 1) {
     promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
     return promise.forget();
   }
 
   nsTArray<WebAuthnScopedCredentialDescriptor> allowList;
-  for (const auto& s: aOptions.mAllowList.Value()) {
+  for (const auto& s: aOptions.mAllowList) {
     WebAuthnScopedCredentialDescriptor c;
     CryptoBuffer cb;
     cb.Assign(s.mId);
     c.id() = cb;
     allowList.AppendElement(c);
   }
 
   // TODO: Add extension list building
@@ -579,17 +578,17 @@ WebAuthnManager::GetAssertion(nsPIDOMWin
   // client platform, to produce the extension data that needs to be sent to the
   // authenticator. If an error is encountered while processing an extension,
   // skip that extension and do not produce any extension data for it. Call the
   // result of this processing clientExtensions.
   nsTArray<WebAuthnExtension> extensions;
 
   WebAuthnTransactionInfo info(rpIdHash,
                                clientDataHash,
-                               10000,
+                               adjustedTimeout,
                                allowList,
                                extensions);
   RefPtr<MozPromise<nsresult, nsresult, false>> p = GetOrCreateBackgroundActor();
   p->Then(AbstractThread::MainThread(), __func__,
           []() {
             WebAuthnManager* mgr = WebAuthnManager::Get();
             if (!mgr) {
               return;
--- a/dom/webauthn/WebAuthnManager.h
+++ b/dom/webauthn/WebAuthnManager.h
@@ -73,18 +73,17 @@ public:
   Cancel(const nsresult& aError);
 
   already_AddRefed<Promise>
   MakeCredential(nsPIDOMWindowInner* aParent,
                  const MakeCredentialOptions& aOptions);
 
   already_AddRefed<Promise>
   GetAssertion(nsPIDOMWindowInner* aParent,
-               const ArrayBufferViewOrArrayBuffer& aAssertionChallenge,
-               const AssertionOptions& aOptions);
+               const PublicKeyCredentialRequestOptions& aOptions);
 
   void StartRegister();
   void StartSign();
 
   // nsIIPCbackgroundChildCreateCallback methods
   void ActorCreated(PBackgroundChild* aActor) override;
   void ActorFailed() override;
   void ActorDestroyed();
--- a/dom/webauthn/moz.build
+++ b/dom/webauthn/moz.build
@@ -15,33 +15,31 @@ EXPORTS.mozilla.dom += [
     'AuthenticatorAssertionResponse.h',
     'AuthenticatorAttestationResponse.h',
     'AuthenticatorResponse.h',
     'NSSU2FTokenRemote.h',
     'PublicKeyCredential.h',
     'U2FSoftTokenManager.h',
     'U2FTokenManager.h',
     'U2FTokenTransport.h',
-    'WebAuthentication.h',
     'WebAuthnManager.h',
     'WebAuthnRequest.h',
     'WebAuthnTransactionChild.h',
     'WebAuthnTransactionParent.h',
     'WebAuthnUtil.h'
 ]
 
 UNIFIED_SOURCES += [
     'AuthenticatorAssertionResponse.cpp',
     'AuthenticatorAttestationResponse.cpp',
     'AuthenticatorResponse.cpp',
     'NSSU2FTokenRemote.cpp',
     'PublicKeyCredential.cpp',
     'U2FSoftTokenManager.cpp',
     'U2FTokenManager.cpp',
-    'WebAuthentication.cpp',
     'WebAuthnManager.cpp',
     'WebAuthnTransactionChild.cpp',
     'WebAuthnTransactionParent.cpp',
     'WebAuthnUtil.cpp'
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
--- a/dom/webauthn/tests/test_webauthn_get_assertion.html
+++ b/dom/webauthn/tests/test_webauthn_get_assertion.html
@@ -11,100 +11,119 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 
   <h1>Tests for GetAssertion for W3C Web Authentication</h1>
   <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1309284">Mozilla Bug 1309284</a>
 
   <script class="testbody" type="text/javascript">
-   "use strict";
-
-   // Execute the full-scope test
-   SimpleTest.waitForExplicitFinish();
+    "use strict";
 
-   function arrivingHereIsBad(aResult) {
-     ok(false, "Bad result! Received a: " + aResult);
-     return Promise.resolve();
-   }
+    // Execute the full-scope test
+    SimpleTest.waitForExplicitFinish();
 
-   function expectNotAllowedError(aResult) {
-     ok(aResult.toString().startsWith("NotAllowedError"), "Expecting a NotAllowedError");
-     return Promise.resolve();
-   }
+    function arrivingHereIsBad(aResult) {
+      ok(false, "Bad result! Received a: " + aResult);
+      return Promise.resolve();
+    }
 
-   function expectTypeError(aResult) {
-     ok(aResult.toString().startsWith("TypeError"), "Expecting a TypeError");
-     return Promise.resolve();
-   }
+    function expectNotAllowedError(aResult) {
+      ok(aResult.toString().startsWith("NotAllowedError"), "Expecting a NotAllowedError");
+      return Promise.resolve();
+    }
 
-   SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn", true],
-                                      ["security.webauth.webauthn_enable_softtoken", true],
-                                      ["security.webauth.webauthn_enable_usbtoken", false]]},
-                             runTests);
+    function expectTypeError(aResult) {
+      ok(aResult.toString().startsWith("TypeError"), "Expecting a TypeError");
+      return Promise.resolve();
+    }
+
+    SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn", true],
+                                       ["security.webauth.webauthn_enable_softtoken", true],
+                                       ["security.webauth.webauthn_enable_usbtoken", false]]},
+                              runTests);
 
-   function runTests() {
-     isnot(navigator.authentication, undefined, "WebAuthn API endpoint must exist");
-     isnot(navigator.credentials, undefined, "Credential Management API endpoint must exist");
-     is(navigator.authentication.makeCredential, undefined, "WebAuthn makeCredential API endpoint does not exist any longer");
-     isnot(navigator.authentication.getAssertion, undefined, "WebAuthn getAssertion API endpoint must exist");
-     isnot(navigator.credentials.create, undefined, "CredentialManagement create API endpoint must exist");
+    function runTests() {
+      is(navigator.authentication, undefined, "navigator.authentication does not exist any longer");
+      isnot(navigator.credentials, undefined, "Credential Management API endpoint must exist");
+      isnot(navigator.credentials.create, undefined, "CredentialManagement create API endpoint must exist");
+      isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist");
 
-     let authn = navigator.authentication;
+      let credm = navigator.credentials;
 
-     let gAssertionChallenge = new Uint8Array(16);
-     window.crypto.getRandomValues(gAssertionChallenge);
+      let gAssertionChallenge = new Uint8Array(16);
+      window.crypto.getRandomValues(gAssertionChallenge);
 
-     let invalidCred = { type: "Magic", id: base64ToBytes("AAA=") };
-     let unknownCred = { type: "ScopedCred", id: base64ToBytes("AAA=") };
+      let invalidCred = {type: "Magic", id: base64ToBytes("AAA=")};
+      let unknownCred = {type: "public-key", id: base64ToBytes("AAA=")};
 
-     var testFuncs = [
-       function () {
-         // Test basic good call, but without giving a credential so expect failures
-         // this is OK by the standard, but not supported by U2F-backed authenticators
-         // like the soft token in use here.
-         return authn.getAssertion(gAssertionChallenge)
-                     .then(arrivingHereIsBad)
-                     .catch(expectNotAllowedError);
-       },
-       function () {
-         // Test with an unexpected option
-         return authn.getAssertion(gAssertionChallenge, { unknownValue: "hi" })
-                     .then(arrivingHereIsBad)
-                     .catch(expectNotAllowedError);
-       },
-       function () {
-         // Test with an invalid credential
-         return authn.getAssertion(gAssertionChallenge, { allowList: [invalidCred] })
-                     .then(arrivingHereIsBad)
-                     .catch(expectTypeError);
-       },
-       function () {
-         // Test with an unknown credential
-         return authn.getAssertion(gAssertionChallenge, { allowList: [unknownCred] })
-                     .then(arrivingHereIsBad)
-                     .catch(expectNotAllowedError);
-       },
-       function () {
-         // Test with an unexpected option and an invalid credential
-         return authn.getAssertion(gAssertionChallenge, { unknownValue: "hi" })
-                     .then(arrivingHereIsBad)
-                     .catch(expectNotAllowedError);
-       }
-     ];
+      var testFuncs = [
+        function () {
+          // Test basic good call, but without giving a credential so expect failures
+          // this is OK by the standard, but not supported by U2F-backed authenticators
+          // like the soft token in use here.
+          let publicKeyCredentialRequestOptions = {
+            challenge: gAssertionChallenge
+          };
+          return credm.get({publicKey: publicKeyCredentialRequestOptions})
+                      .then(arrivingHereIsBad)
+                      .catch(expectNotAllowedError);
+        },
+        function () {
+          // Test with an unexpected option
+          let publicKeyCredentialRequestOptions = {
+            challenge: gAssertionChallenge,
+            unknownValue: "hi"
+          };
+          return credm.get({publicKey: publicKeyCredentialRequestOptions})
+                      .then(arrivingHereIsBad)
+                      .catch(expectNotAllowedError);
+        },
+        function () {
+          // Test with an invalid credential
+          let publicKeyCredentialRequestOptions = {
+            challenge: gAssertionChallenge,
+            allowList: [invalidCred]
+          };
+          return credm.get({publicKey: publicKeyCredentialRequestOptions})
+                      .then(arrivingHereIsBad)
+                      .catch(expectTypeError);
+        },
+        function () {
+          // Test with an unknown credential
+          let publicKeyCredentialRequestOptions = {
+            challenge: gAssertionChallenge,
+            allowList: [unknownCred]
+          };
+          return credm.get({publicKey: publicKeyCredentialRequestOptions})
+                      .then(arrivingHereIsBad)
+                      .catch(expectNotAllowedError);
+        },
+        function () {
+          // Test with an unexpected option and an invalid credential
+          let publicKeyCredentialRequestOptions = {
+            challenge: gAssertionChallenge,
+            unknownValue: "hi",
+            allowList: [invalidCred]
+          };
+          return credm.get({publicKey: publicKeyCredentialRequestOptions})
+                      .then(arrivingHereIsBad)
+                      .catch(expectTypeError);
+        }
+      ];
 
-     var i = 0;
-     var runNextTest = () => {
-       if (i == testFuncs.length) {
-         SimpleTest.finish();
-         return;
-       }
-       testFuncs[i]().then(() => { runNextTest(); });
-       i = i + 1;
-     };
-     runNextTest();
+      var i = 0;
+      var runNextTest = () => {
+        if (i == testFuncs.length) {
+          SimpleTest.finish();
+          return;
+        }
+        testFuncs[i]().then(() => { runNextTest(); });
+        i = i + 1;
+      };
+      runNextTest();
 
-   }
+    }
 
   </script>
 
 </body>
 </html>
--- a/dom/webauthn/tests/test_webauthn_loopback.html
+++ b/dom/webauthn/tests/test_webauthn_loopback.html
@@ -20,23 +20,21 @@
 
 // Execute the full-scope test
 SimpleTest.waitForExplicitFinish();
 
 SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn", true],
                                    ["security.webauth.webauthn_enable_softtoken", true],
                                    ["security.webauth.webauthn_enable_usbtoken", false]]},
 function() {
-  isnot(navigator.authentication, undefined, "WebAuthn API endpoint must exist");
+  is(navigator.authentication, undefined, "navigator.authentication does not exist any longer");
   isnot(navigator.credentials, undefined, "Credential Management API endpoint must exist");
-  is(navigator.authentication.makeCredential, undefined, "WebAuthn makeCredential API endpoint does not exist any longer");
-  isnot(navigator.authentication.getAssertion, undefined, "WebAuthn getAssertion API endpoint must exist");
   isnot(navigator.credentials.create, undefined, "CredentialManagement create API endpoint must exist");
+  isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist");
 
-  let authn = navigator.authentication;
   let credm = navigator.credentials;
 
   let gCredentialChallenge = new Uint8Array(16);
   window.crypto.getRandomValues(gCredentialChallenge);
   let gAssertionChallenge = new Uint8Array(16);
   window.crypto.getRandomValues(gAssertionChallenge);
 
   testMakeCredential();
@@ -145,24 +143,28 @@ function() {
     .catch(function(aReason) {
       ok(aReason.toString().startsWith("NotAllowedError"), "Expect NotAllowedError, got " + aReason);
       testAssertion(aCredInfo);
     });
   }
 
   function testAssertion(aCredInfo) {
     let newCredential = {
-      type: "ScopedCred",
+      type: "public-key",
       id: Uint8Array.from(aCredInfo.rawId),
       transports: ["usb"],
     }
 
-    let assertOptions = {rpId: document.origin, timeoutSeconds: 5,
-                         allowList: [newCredential]};
-    authn.getAssertion(gAssertionChallenge, assertOptions)
+    let publicKeyCredentialRequestOptions = {
+      challenge: gAssertionChallenge,
+      timeout: 5000, // the minimum timeout is actually 15 seconds
+      rpId: document.origin,
+      allowList: [newCredential]
+    };
+    credm.get({publicKey: publicKeyCredentialRequestOptions})
     .then(function(aAssertion) {
       /* Pass along the pubKey. */
       return checkAssertionAndSigValid(aCredInfo.u2fReg.publicKey, aAssertion);
     })
     .then(function(aSigVerifyResult) {
       ok(aSigVerifyResult, "Signing signature verified");
       SimpleTest.finish();
     })
--- a/dom/webauthn/tests/test_webauthn_make_credential.html
+++ b/dom/webauthn/tests/test_webauthn_make_credential.html
@@ -45,23 +45,21 @@
       ok(aResult.toString().startsWith("NotSupportedError"), "Expecting a NotSupportedError");
       return Promise.resolve();
     }
 
     SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn", true],
                                        ["security.webauth.webauthn_enable_softtoken", true],
                                        ["security.webauth.webauthn_enable_usbtoken", false]]}, runTests);
     function runTests() {
-      isnot(navigator.authentication, undefined, "WebAuthn API endpoint must exist");
+      is(navigator.authentication, undefined, "navigator.authentication does not exist any longer");
       isnot(navigator.credentials, undefined, "Credential Management API endpoint must exist");
-      is(navigator.authentication.makeCredential, undefined, "WebAuthn makeCredential API endpoint does not exist any longer");
-      isnot(navigator.authentication.getAssertion, undefined, "WebAuthn getAssertion API endpoint must exist");
       isnot(navigator.credentials.create, undefined, "CredentialManagement create API endpoint must exist");
+      isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist");
 
-      let authn = navigator.authentication;
       let credm = navigator.credentials;
 
       let gCredentialChallenge = new Uint8Array(16);
       window.crypto.getRandomValues(gCredentialChallenge);
 
       let rp = {id: "none", name: "none", icon: "none"};
       let user = {id: "none", name: "none", icon: "none", displayName: "none"};
       let param = {type: "public-key", algorithm: "p-256"};
--- a/dom/webauthn/tests/test_webauthn_no_token.html
+++ b/dom/webauthn/tests/test_webauthn_no_token.html
@@ -21,23 +21,21 @@
 // Execute the full-scope test
 SimpleTest.waitForExplicitFinish();
 
 // Turn off all tokens. This should result in "not allowed" failures
 SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn", true],
                                    ["security.webauth.webauthn_enable_softtoken", false],
                                    ["security.webauth.webauthn_enable_usbtoken", false]]},
 function() {
-  isnot(navigator.authentication, undefined, "WebAuthn API endpoint must exist");
+  is(navigator.authentication, undefined, "navigator.authentication does not exist any longer");
   isnot(navigator.credentials, undefined, "Credential Management API endpoint must exist");
-  is(navigator.authentication.makeCredential, undefined, "WebAuthn makeCredential API endpoint does not exist any longer");
-  isnot(navigator.authentication.getAssertion, undefined, "WebAuthn getAssertion API endpoint must exist");
   isnot(navigator.credentials.create, undefined, "CredentialManagement create API endpoint must exist");
+  isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist");
 
-  let authn = navigator.authentication;
   let credm = navigator.credentials;
 
   let credentialChallenge = new Uint8Array(16);
   window.crypto.getRandomValues(credentialChallenge);
   let assertionChallenge = new Uint8Array(16);
   window.crypto.getRandomValues(assertionChallenge);
   let credentialId = new Uint8Array(128);
   window.crypto.getRandomValues(credentialId);
@@ -59,23 +57,27 @@ function() {
     .catch(function(aReason) {
       ok(aReason.toString().startsWith("NotAllowedError"), aReason);
       testAssertion();
     });
   }
 
   function testAssertion() {
     let newCredential = {
-      type: "ScopedCred",
+      type: "public-key",
       id: credentialId,
       transports: ["usb"],
     }
-    let assertOptions = {rpId: document.origin, timeoutSeconds: 5,
-                         allowList: [newCredential]};
-    authn.getAssertion(assertionChallenge, assertOptions)
+    let publicKeyCredentialRequestOptions = {
+      challenge: assertionChallenge,
+      timeout: 5000, // the minimum timeout is actually 15 seconds
+      rpId: document.origin,
+      allowList: [newCredential]
+    };
+    credm.get({publicKey: publicKeyCredentialRequestOptions})
     .then(function(aResult) {
       ok(false, "Should have failed.");
       SimpleTest.finish();
     })
     .catch(function(aReason) {
       ok(aReason.toString().startsWith("NotAllowedError"), aReason);
       SimpleTest.finish();
     })
--- a/dom/webauthn/tests/test_webauthn_sameorigin.html
+++ b/dom/webauthn/tests/test_webauthn_sameorigin.html
@@ -37,31 +37,29 @@
     function expectSecurityError(aResult) {
       // TODO: Change to `ok` when Bug 1329764 lands
       todo(aResult.toString().startsWith("SecurityError"), "Expecting a SecurityError");
       return Promise.resolve();
     }
 
     function keepThisPublicKeyCredential(aPublicKeyCredential) {
       gTrackedCredential = {
-        type: "ScopedCred",
+        type: "public-key",
         id: Uint8Array.from(aPublicKeyCredential.rawId),
         transports: [ "usb" ],
       }
       return Promise.resolve(aPublicKeyCredential);
     }
 
     function runTests() {
-      isnot(navigator.authentication, undefined, "WebAuthn API endpoint must exist");
+      is(navigator.authentication, undefined, "navigator.authentication does not exist any longer");
       isnot(navigator.credentials, undefined, "Credential Management API endpoint must exist");
-      is(navigator.authentication.makeCredential, undefined, "WebAuthn makeCredential API endpoint does not exist any longer");
-      isnot(navigator.authentication.getAssertion, undefined, "WebAuthn getAssertion API endpoint must exist");
       isnot(navigator.credentials.create, undefined, "CredentialManagement create API endpoint must exist");
+      isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist");
 
-      let authn = navigator.authentication;
       let credm = navigator.credentials;
 
       let chall = new Uint8Array(16);
       window.crypto.getRandomValues(chall);
 
       let user = {id: "none", name: "none", icon: "none", displayName: "none"};
       let param = {type: "public-key", algorithm: "p-256"};
 
@@ -134,59 +132,87 @@
             rp: rp, user: user, challenge: chall, parameters: [param]
           };
           return credm.create({publicKey: makeCredentialOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectSecurityError);
         },
         function () {
           // Test basic good call
-          return authn.getAssertion(chall, {allowList: [gTrackedCredential],
-                                            rpId: document.origin})
+          let publicKeyCredentialRequestOptions = {
+            challenge: chall,
+            rpId: document.origin,
+            allowList: [gTrackedCredential]
+          };
+          return credm.get({publicKey: publicKeyCredentialRequestOptions})
                       .then(arrivingHereIsGood)
                       .catch(arrivingHereIsBad);
         },
         function () {
           // Test rpId being unset
-          return authn.getAssertion(chall, {allowList: [gTrackedCredential]})
+          let publicKeyCredentialRequestOptions = {
+            challenge: chall,
+            allowList: [gTrackedCredential]
+          };
+          return credm.get({publicKey: publicKeyCredentialRequestOptions})
                       .then(arrivingHereIsGood)
                       .catch(arrivingHereIsBad);
         },
         function () {
           // Test this origin with optional fields
-          return authn.getAssertion(chall, {allowList: [gTrackedCredential],
-                                            rpId: "user:pass@" + document.origin + ":8888"})
+          let publicKeyCredentialRequestOptions = {
+            challenge: chall,
+            rpId: "user:pass@" + document.origin + ":8888",
+            allowList: [gTrackedCredential]
+          };
+          return credm.get({publicKey: publicKeyCredentialRequestOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectSecurityError);
         },
         function () {
           // Test blank rpId
-          return authn.getAssertion(chall, {allowList: [gTrackedCredential],
-                                            rpId: ""})
+          let publicKeyCredentialRequestOptions = {
+            challenge: chall,
+            rpId: "",
+            allowList: [gTrackedCredential]
+          };
+          return credm.get({publicKey: publicKeyCredentialRequestOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectSecurityError);
         },
         function () {
           // Test subdomain of this origin
-          return authn.getAssertion(chall, {allowList: [gTrackedCredential],
-                                            rpId: "subdomain." + document.origin})
+          let publicKeyCredentialRequestOptions = {
+            challenge: chall,
+            rpId: "subdomain." + document.origin,
+            allowList: [gTrackedCredential]
+          };
+          return credm.get({publicKey: publicKeyCredentialRequestOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectSecurityError);
         },
         function () {
           // Test another origin
-          return authn.getAssertion(chall, {allowList: [gTrackedCredential],
-                                            rpId: "example.com"})
+          let publicKeyCredentialRequestOptions = {
+            challenge: chall,
+            rpId: "example.com",
+            allowList: [gTrackedCredential]
+          };
+          return credm.get({publicKey: publicKeyCredentialRequestOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectSecurityError);
         },
         function () {
           // Test a different domain within the same TLD
-          return authn.getAssertion(chall, {allowList: [gTrackedCredential],
-                                            rpId: "alt.test"})
+          let publicKeyCredentialRequestOptions = {
+            challenge: chall,
+            rpId: "alt.test",
+            allowList: [gTrackedCredential]
+          };
+          return credm.get({publicKey: publicKeyCredentialRequestOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectSecurityError)
         },
         function () {
           SimpleTest.finish();
         }
       ];
       var i = 0;
--- a/dom/webidl/CredentialManagement.webidl
+++ b/dom/webidl/CredentialManagement.webidl
@@ -10,14 +10,19 @@
 [Exposed=Window, SecureContext, Pref="security.webauth.webauthn"]
 interface Credential {
   readonly attribute USVString id;
   readonly attribute DOMString type;
 };
 
 [Exposed=Window, SecureContext, Pref="security.webauth.webauthn"]
 interface CredentialsContainer {
+  Promise<Credential?> get(optional CredentialRequestOptions options);
   Promise<Credential?> create(optional CredentialCreationOptions options);
 };
 
+dictionary CredentialRequestOptions {
+  PublicKeyCredentialRequestOptions publicKey;
+};
+
 dictionary CredentialCreationOptions {
   MakeCredentialOptions publicKey;
 };
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -366,15 +366,10 @@ partial interface Navigator {
 
 [NoInterfaceObject, Exposed=(Window,Worker)]
 interface NavigatorConcurrentHardware {
   readonly attribute unsigned long long hardwareConcurrency;
 };
 
 partial interface Navigator {
   [Pref="security.webauth.webauthn", SameObject]
-  readonly attribute WebAuthentication authentication;
-};
-
-partial interface Navigator {
-  [Pref="security.webauth.webauthn", SameObject]
   readonly attribute CredentialsContainer credentials;
 };
--- a/dom/webidl/WebAuthentication.webidl
+++ b/dom/webidl/WebAuthentication.webidl
@@ -61,16 +61,25 @@ dictionary AuthenticatorSelectionCriteri
     boolean       requireResidentKey = false;
 };
 
 enum Attachment {
     "platform",
     "cross-platform"
 };
 
+dictionary PublicKeyCredentialRequestOptions {
+    required BufferSource                challenge;
+    unsigned long                        timeout;
+    USVString                            rpId;
+    sequence<PublicKeyCredentialDescriptor> allowList = [];
+    // Extensions are not supported yet.
+    // AuthenticationExtensions             extensions;
+};
+
 dictionary CollectedClientData {
     required DOMString           challenge;
     required DOMString           origin;
     required DOMString           hashAlg;
     DOMString                    tokenBinding;
     // Extensions are not supported yet.
     // AuthenticationExtensions     clientExtensions;
     // AuthenticationExtensions     authenticatorExtensions;
@@ -89,44 +98,14 @@ dictionary PublicKeyCredentialDescriptor
 typedef (boolean or DOMString) WebAuthnAlgorithmID; // Fix when upstream there's a definition of how to serialize AlgorithmIdentifier
 
 [SecureContext, Pref="security.webauth.webauthn"]
 interface AuthenticatorAssertionResponse : AuthenticatorResponse {
     readonly attribute ArrayBuffer      authenticatorData;
     readonly attribute ArrayBuffer      signature;
 };
 
-dictionary AssertionOptions {
-    unsigned long                        timeoutSeconds;
-    USVString                            rpId;
-    sequence<ScopedCredentialDescriptor> allowList;
-    WebAuthnExtensions                   extensions;
-};
-
-dictionary WebAuthnExtensions {
-};
-
-enum ScopedCredentialType {
-    "ScopedCred"
-};
-
-dictionary ScopedCredentialDescriptor {
-    required ScopedCredentialType type;
-    required BufferSource         id;
-    sequence <WebAuthnTransport>  transports;
-};
-
 // Renamed from "Transport" to avoid a collision with U2F
 enum WebAuthnTransport {
     "usb",
     "nfc",
     "ble"
 };
-
-/***** The Main API *****/
-
-[SecureContext, Pref="security.webauth.webauthn"]
-interface WebAuthentication {
-    Promise<PublicKeyCredential> getAssertion (
-        BufferSource               assertionChallenge,
-        optional AssertionOptions  options
-    );
-};