зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1861484 - move isUserVerifyingPlatformAuthenticatorAvailable to parent process. r=keeler
Differential Revision: https://phabricator.services.mozilla.com/D191998
This commit is contained in:
Родитель
ced0683cec
Коммит
e17a30add4
|
@ -124,6 +124,14 @@ void CredentialsContainer::EnsureWebAuthnManager() {
|
|||
}
|
||||
}
|
||||
|
||||
already_AddRefed<WebAuthnManager> CredentialsContainer::GetWebAuthnManager() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
EnsureWebAuthnManager();
|
||||
RefPtr<WebAuthnManager> ref = mManager;
|
||||
return ref.forget();
|
||||
}
|
||||
|
||||
JSObject* CredentialsContainer::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) {
|
||||
return CredentialsContainer_Binding::Wrap(aCx, this, aGivenProto);
|
||||
|
|
|
@ -22,6 +22,8 @@ class CredentialsContainer final : public nsISupports, public nsWrapperCache {
|
|||
|
||||
nsPIDOMWindowInner* GetParentObject() const { return mParent; }
|
||||
|
||||
already_AddRefed<WebAuthnManager> GetWebAuthnManager();
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
|
|
|
@ -33,6 +33,11 @@ namespace dom {
|
|||
|
||||
NS_IMPL_ISUPPORTS(AndroidWebAuthnService, nsIWebAuthnService)
|
||||
|
||||
NS_IMETHODIMP
|
||||
AndroidWebAuthnService::GetIsUVPAA(bool* aAvailable) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AndroidWebAuthnService::MakeCredential(uint64_t aTransactionId,
|
||||
uint64_t browsingContextId,
|
||||
|
|
|
@ -141,6 +141,7 @@ async protocol PWebAuthnTransaction {
|
|||
parent:
|
||||
async RequestRegister(uint64_t aTransactionId, WebAuthnMakeCredentialInfo aTransactionInfo);
|
||||
async RequestSign(uint64_t aTransactionId, WebAuthnGetAssertionInfo aTransactionInfo);
|
||||
async RequestIsUVPAA() returns (bool available);
|
||||
[Tainted] async RequestCancel(uint64_t aTransactionId);
|
||||
async DestroyMe();
|
||||
|
||||
|
|
|
@ -9,10 +9,13 @@
|
|||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/StaticPrefs_security.h"
|
||||
#include "mozilla/dom/AuthenticatorResponse.h"
|
||||
#include "mozilla/dom/CredentialsContainer.h"
|
||||
#include "mozilla/dom/ChromeUtils.h"
|
||||
#include "mozilla/dom/Navigator.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/PublicKeyCredential.h"
|
||||
#include "mozilla/dom/WebAuthenticationBinding.h"
|
||||
#include "mozilla/dom/WebAuthnManager.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
|
@ -117,46 +120,16 @@ void PublicKeyCredential::SetAssertionResponse(
|
|||
already_AddRefed<Promise>
|
||||
PublicKeyCredential::IsUserVerifyingPlatformAuthenticatorAvailable(
|
||||
GlobalObject& aGlobal, ErrorResult& aError) {
|
||||
RefPtr<Promise> promise =
|
||||
Promise::Create(xpc::CurrentNativeGlobal(aGlobal.Context()), aError);
|
||||
if (aError.Failed()) {
|
||||
nsCOMPtr<nsPIDOMWindowInner> window =
|
||||
do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!window) {
|
||||
aError.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webauthn/#isUserVerifyingPlatformAuthenticatorAvailable
|
||||
//
|
||||
// If on latest windows, call system APIs, otherwise return false, as we don't
|
||||
// have other UVPAAs available at this time.
|
||||
#ifdef XP_WIN
|
||||
|
||||
if (WinWebAuthnService::IsUserVerifyingPlatformAuthenticatorAvailable()) {
|
||||
promise->MaybeResolve(true);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
promise->MaybeResolve(false);
|
||||
#elif defined(MOZ_WIDGET_ANDROID)
|
||||
if (StaticPrefs::
|
||||
security_webauthn_webauthn_enable_android_fido2_residentkey()) {
|
||||
auto result = java::WebAuthnTokenManager::
|
||||
WebAuthnIsUserVerifyingPlatformAuthenticatorAvailable();
|
||||
auto geckoResult = java::GeckoResult::LocalRef(std::move(result));
|
||||
MozPromise<bool, bool, false>::FromGeckoResult(geckoResult)
|
||||
->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
[promise](const MozPromise<bool, bool, false>::ResolveOrRejectValue&
|
||||
aValue) {
|
||||
if (aValue.IsResolve()) {
|
||||
promise->MaybeResolve(aValue.ResolveValue());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
promise->MaybeResolve(false);
|
||||
}
|
||||
#else
|
||||
promise->MaybeResolve(false);
|
||||
#endif
|
||||
return promise.forget();
|
||||
RefPtr<WebAuthnManager> manager =
|
||||
window->Navigator()->Credentials()->GetWebAuthnManager();
|
||||
return manager->IsUVPAA(aGlobal, aError);
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "mozilla/dom/PublicKeyCredential.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/PWebAuthnTransaction.h"
|
||||
#include "mozilla/dom/PWebAuthnTransactionChild.h"
|
||||
#include "mozilla/dom/WebAuthnManager.h"
|
||||
#include "mozilla/dom/WebAuthnTransactionChild.h"
|
||||
#include "mozilla/dom/WebAuthnUtil.h"
|
||||
|
@ -727,6 +728,32 @@ already_AddRefed<Promise> WebAuthnManager::Store(const Credential& aCredential,
|
|||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> WebAuthnManager::IsUVPAA(GlobalObject& aGlobal,
|
||||
ErrorResult& aError) {
|
||||
RefPtr<Promise> promise =
|
||||
Promise::Create(xpc::CurrentNativeGlobal(aGlobal.Context()), aError);
|
||||
if (aError.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!MaybeCreateBackgroundActor()) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
mChild->SendRequestIsUVPAA()->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
[promise](const PWebAuthnTransactionChild::RequestIsUVPAAPromise::
|
||||
ResolveOrRejectValue& aValue) {
|
||||
if (aValue.IsResolve()) {
|
||||
promise->MaybeResolve(aValue.ResolveValue());
|
||||
} else {
|
||||
promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
|
||||
}
|
||||
});
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
void WebAuthnManager::FinishMakeCredential(
|
||||
const uint64_t& aTransactionId,
|
||||
const WebAuthnMakeCredentialResult& aResult) {
|
||||
|
|
|
@ -95,6 +95,8 @@ class WebAuthnManager final : public WebAuthnManagerBase, public AbortFollower {
|
|||
already_AddRefed<Promise> Store(const Credential& aCredential,
|
||||
ErrorResult& aError);
|
||||
|
||||
already_AddRefed<Promise> IsUVPAA(GlobalObject& aGlobal, ErrorResult& aError);
|
||||
|
||||
// WebAuthnManagerBase
|
||||
|
||||
void FinishMakeCredential(
|
||||
|
|
|
@ -40,6 +40,14 @@ WebAuthnService::GetAssertion(uint64_t aTransactionId,
|
|||
aArgs, aPromise);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebAuthnService::GetIsUVPAA(bool* aAvailable) {
|
||||
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
|
||||
return mTestService->GetIsUVPAA(aAvailable);
|
||||
}
|
||||
return mPlatformService->GetIsUVPAA(aAvailable);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebAuthnService::Reset() {
|
||||
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
|
||||
|
|
|
@ -280,6 +280,84 @@ mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestCancel(
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestIsUVPAA(
|
||||
RequestIsUVPAAResolver&& aResolver) {
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
// Try the nsIWebAuthnService. If we're configured for tests we
|
||||
// will get a result. Otherwise we expect NS_ERROR_NOT_IMPLEMENTED.
|
||||
nsCOMPtr<nsIWebAuthnService> service(
|
||||
do_GetService("@mozilla.org/webauthn/service;1"));
|
||||
bool available;
|
||||
nsresult rv = service->GetIsUVPAA(&available);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
aResolver(available);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Don't consult the platform API if resident key support is disabled.
|
||||
if (!StaticPrefs::
|
||||
security_webauthn_webauthn_enable_android_fido2_residentkey()) {
|
||||
aResolver(false);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// The GeckoView implementation of
|
||||
// isUserVerifiyingPlatformAuthenticatorAvailable does not block, but we must
|
||||
// call it on the main thread. It returns a MozPromise which we can ->Then to
|
||||
// call aResolver on the IPDL background thread.
|
||||
//
|
||||
// Bug 1550788: there is an unnecessary layer of dispatching here: ipdl ->
|
||||
// main -> a background thread. Other platforms just do ipdl -> a background
|
||||
// thread.
|
||||
nsCOMPtr<nsISerialEventTarget> target = GetCurrentSerialEventTarget();
|
||||
nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
|
||||
__func__, [target, resolver = std::move(aResolver)]() {
|
||||
auto result = java::WebAuthnTokenManager::
|
||||
WebAuthnIsUserVerifyingPlatformAuthenticatorAvailable();
|
||||
auto geckoResult = java::GeckoResult::LocalRef(std::move(result));
|
||||
MozPromise<bool, bool, false>::FromGeckoResult(geckoResult)
|
||||
->Then(
|
||||
target, __func__,
|
||||
[resolver](
|
||||
const MozPromise<bool, bool, false>::ResolveOrRejectValue&
|
||||
aValue) {
|
||||
if (aValue.IsResolve()) {
|
||||
resolver(aValue.ResolveValue());
|
||||
} else {
|
||||
resolver(false);
|
||||
}
|
||||
});
|
||||
}));
|
||||
NS_DispatchToMainThread(runnable.forget());
|
||||
return IPC_OK();
|
||||
|
||||
#else
|
||||
|
||||
nsCOMPtr<nsISerialEventTarget> target = GetCurrentSerialEventTarget();
|
||||
nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
|
||||
__func__, [target, resolver = std::move(aResolver)]() {
|
||||
bool available;
|
||||
nsCOMPtr<nsIWebAuthnService> service(
|
||||
do_GetService("@mozilla.org/webauthn/service;1"));
|
||||
nsresult rv = service->GetIsUVPAA(&available);
|
||||
if (NS_FAILED(rv)) {
|
||||
available = false;
|
||||
}
|
||||
BoolPromise::CreateAndResolve(available, __func__)
|
||||
->Then(target, __func__,
|
||||
[resolver](const BoolPromise::ResolveOrRejectValue& value) {
|
||||
if (value.IsResolve()) {
|
||||
resolver(value.ResolveValue());
|
||||
} else {
|
||||
resolver(false);
|
||||
}
|
||||
});
|
||||
}));
|
||||
NS_DispatchBackgroundTask(runnable.forget(), NS_DISPATCH_EVENT_MAY_BLOCK);
|
||||
return IPC_OK();
|
||||
#endif
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvDestroyMe() {
|
||||
::mozilla::ipc::AssertIsOnBackgroundThread();
|
||||
|
||||
|
|
|
@ -35,6 +35,9 @@ class WebAuthnTransactionParent final : public PWebAuthnTransactionParent {
|
|||
mozilla::ipc::IPCResult RecvRequestCancel(
|
||||
const Tainted<uint64_t>& aTransactionId);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRequestIsUVPAA(
|
||||
RequestIsUVPAAResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvDestroyMe();
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
|
|
@ -132,18 +132,20 @@ bool WinWebAuthnService::AreWebAuthNApisAvailable() {
|
|||
gWinWebauthnGetApiVersionNumber() >= kMinWinWebAuthNApiVersion;
|
||||
}
|
||||
|
||||
// static
|
||||
bool WinWebAuthnService::IsUserVerifyingPlatformAuthenticatorAvailable() {
|
||||
NS_IMETHODIMP
|
||||
WinWebAuthnService::GetIsUVPAA(bool* aAvailable) {
|
||||
nsresult rv = EnsureWinWebAuthnModuleLoaded();
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (WinWebAuthnService::AreWebAuthNApisAvailable()) {
|
||||
BOOL isUVPAA = FALSE;
|
||||
StaticAutoReadLock lock(gWinWebAuthnModuleLock);
|
||||
return gWinWebAuthnModule && gWinWebauthnIsUVPAA(&isUVPAA) == S_OK &&
|
||||
isUVPAA == TRUE;
|
||||
*aAvailable = gWinWebAuthnModule && gWinWebauthnIsUVPAA(&isUVPAA) == S_OK &&
|
||||
isUVPAA == TRUE;
|
||||
} else {
|
||||
*aAvailable = false;
|
||||
}
|
||||
return false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -614,6 +614,17 @@ impl AuthrsService {
|
|||
.or(Err(NS_ERROR_FAILURE))
|
||||
}
|
||||
|
||||
xpcom_method!(get_is_uvpaa => GetIsUVPAA() -> bool);
|
||||
fn get_is_uvpaa(&self) -> Result<bool, nsresult> {
|
||||
if static_prefs::pref!("security.webauth.webauthn_enable_usbtoken") {
|
||||
Ok(false)
|
||||
} else if static_prefs::pref!("security.webauth.webauthn_enable_softtoken") {
|
||||
Ok(self.test_token_manager.has_platform_authenticator())
|
||||
} else {
|
||||
Err(NS_ERROR_NOT_AVAILABLE)
|
||||
}
|
||||
}
|
||||
|
||||
xpcom_method!(make_credential => MakeCredential(aTid: u64, aBrowsingContextId: u64, aArgs: *const nsIWebAuthnRegisterArgs, aPromise: *const nsIWebAuthnRegisterPromise));
|
||||
fn make_credential(
|
||||
&self,
|
||||
|
|
|
@ -870,4 +870,19 @@ impl TestTokenManager {
|
|||
.may_block(true)
|
||||
.dispatch_background_task();
|
||||
}
|
||||
|
||||
pub fn has_platform_authenticator(&self) -> bool {
|
||||
if !static_prefs::pref!("security.webauth.webauthn_enable_softtoken") {
|
||||
return false;
|
||||
}
|
||||
|
||||
for token in self.state.lock().unwrap().values_mut() {
|
||||
let _ = token.init();
|
||||
if token.transport.as_str() == "internal" {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,9 @@ interface nsICredentialParameters : nsISupports
|
|||
[scriptable, uuid(e236a9b4-a26f-11ed-b6cc-07a9834e19b1)]
|
||||
interface nsIWebAuthnService : nsISupports
|
||||
{
|
||||
// IsUserVerifyingPlatformAuthenticatorAvailable
|
||||
readonly attribute bool isUVPAA;
|
||||
|
||||
void makeCredential(
|
||||
in uint64_t aTransactionId,
|
||||
in uint64_t browsingContextId,
|
||||
|
|
|
@ -18,19 +18,30 @@
|
|||
<script class="testbody" type="text/javascript">
|
||||
"use strict";
|
||||
|
||||
add_task(async function test_is_platform_available() {
|
||||
// This test ensures that isUserVerifyingPlatformAuthenticatorAvailable()
|
||||
// is a callable method, but with the softtoken enabled, it's not useful to
|
||||
// figure out what it actually returns, so we'll just make sure it runs.
|
||||
await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
|
||||
.then(function(aResult) {
|
||||
ok(true, "Resolved: " + aResult);
|
||||
})
|
||||
.catch(function(aProblem) {
|
||||
ok(false, "Problem encountered: " + aProblem);
|
||||
});
|
||||
add_task(async function test_uvpaa_with_no_authenticator() {
|
||||
let uvpaa = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
|
||||
ok(uvpaa === false, "Platform authenticator is not available");
|
||||
});
|
||||
|
||||
add_task(async () => {
|
||||
await addVirtualAuthenticator("ctap2_1", "usb");
|
||||
});
|
||||
|
||||
add_task(async function test_uvpaa_with_usb_authenticator() {
|
||||
let uvpaa = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
|
||||
ok(uvpaa === false, "Platform authenticator is not available");
|
||||
});
|
||||
|
||||
add_task(async () => {
|
||||
await addVirtualAuthenticator("ctap2_1", "internal");
|
||||
});
|
||||
|
||||
add_task(async function test_uvpaa_with_usb_and_platform_authenticator() {
|
||||
let uvpaa = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
|
||||
ok(uvpaa === true, "Platform authenticator is available");
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -20,21 +20,45 @@ var { AppConstants } = SpecialPowers.ChromeUtils.importESModule(
|
|||
"resource://gre/modules/AppConstants.sys.mjs"
|
||||
);
|
||||
|
||||
async function addVirtualAuthenticator() {
|
||||
let id = await SpecialPowers.spawnChrome([], () => {
|
||||
let webauthnService = Cc["@mozilla.org/webauthn/service;1"].getService(
|
||||
Ci.nsIWebAuthnService
|
||||
);
|
||||
let id = webauthnService.addVirtualAuthenticator(
|
||||
"ctap2_1",
|
||||
"internal",
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
);
|
||||
return id;
|
||||
});
|
||||
async function addVirtualAuthenticator(
|
||||
protocol = "ctap2_1",
|
||||
transport = "internal",
|
||||
hasResidentKey = true,
|
||||
hasUserVerification = true,
|
||||
isUserConsenting = true,
|
||||
isUserVerified = true
|
||||
) {
|
||||
let id = await SpecialPowers.spawnChrome(
|
||||
[
|
||||
protocol,
|
||||
transport,
|
||||
hasResidentKey,
|
||||
hasUserVerification,
|
||||
isUserConsenting,
|
||||
isUserVerified,
|
||||
],
|
||||
(
|
||||
protocol,
|
||||
transport,
|
||||
hasResidentKey,
|
||||
hasUserVerification,
|
||||
isUserConsenting,
|
||||
isUserVerified
|
||||
) => {
|
||||
let webauthnService = Cc["@mozilla.org/webauthn/service;1"].getService(
|
||||
Ci.nsIWebAuthnService
|
||||
);
|
||||
let id = webauthnService.addVirtualAuthenticator(
|
||||
protocol,
|
||||
transport,
|
||||
hasResidentKey,
|
||||
hasUserVerification,
|
||||
isUserConsenting,
|
||||
isUserVerified
|
||||
);
|
||||
return id;
|
||||
}
|
||||
);
|
||||
|
||||
SimpleTest.registerCleanupFunction(async () => {
|
||||
await SpecialPowers.spawnChrome([id], id => {
|
||||
|
|
Загрузка…
Ссылка в новой задаче