Skip to content

Commit e17a30a

Browse files
committed
Bug 1861484 - move isUserVerifyingPlatformAuthenticatorAvailable to parent process. r=keeler
Differential Revision: https://phabricator.services.mozilla.com/D191998
1 parent ced0683 commit e17a30a

16 files changed

+242
-69
lines changed

dom/credentialmanagement/CredentialsContainer.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,14 @@ void CredentialsContainer::EnsureWebAuthnManager() {
124124
}
125125
}
126126

127+
already_AddRefed<WebAuthnManager> CredentialsContainer::GetWebAuthnManager() {
128+
MOZ_ASSERT(NS_IsMainThread());
129+
130+
EnsureWebAuthnManager();
131+
RefPtr<WebAuthnManager> ref = mManager;
132+
return ref.forget();
133+
}
134+
127135
JSObject* CredentialsContainer::WrapObject(JSContext* aCx,
128136
JS::Handle<JSObject*> aGivenProto) {
129137
return CredentialsContainer_Binding::Wrap(aCx, this, aGivenProto);

dom/credentialmanagement/CredentialsContainer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ class CredentialsContainer final : public nsISupports, public nsWrapperCache {
2222

2323
nsPIDOMWindowInner* GetParentObject() const { return mParent; }
2424

25+
already_AddRefed<WebAuthnManager> GetWebAuthnManager();
26+
2527
virtual JSObject* WrapObject(JSContext* aCx,
2628
JS::Handle<JSObject*> aGivenProto) override;
2729

dom/webauthn/AndroidWebAuthnService.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ namespace dom {
3333

3434
NS_IMPL_ISUPPORTS(AndroidWebAuthnService, nsIWebAuthnService)
3535

36+
NS_IMETHODIMP
37+
AndroidWebAuthnService::GetIsUVPAA(bool* aAvailable) {
38+
return NS_ERROR_NOT_IMPLEMENTED;
39+
}
40+
3641
NS_IMETHODIMP
3742
AndroidWebAuthnService::MakeCredential(uint64_t aTransactionId,
3843
uint64_t browsingContextId,

dom/webauthn/PWebAuthnTransaction.ipdl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ async protocol PWebAuthnTransaction {
141141
parent:
142142
async RequestRegister(uint64_t aTransactionId, WebAuthnMakeCredentialInfo aTransactionInfo);
143143
async RequestSign(uint64_t aTransactionId, WebAuthnGetAssertionInfo aTransactionInfo);
144+
async RequestIsUVPAA() returns (bool available);
144145
[Tainted] async RequestCancel(uint64_t aTransactionId);
145146
async DestroyMe();
146147

dom/webauthn/PublicKeyCredential.cpp

Lines changed: 10 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,13 @@
99
#include "mozilla/Preferences.h"
1010
#include "mozilla/StaticPrefs_security.h"
1111
#include "mozilla/dom/AuthenticatorResponse.h"
12+
#include "mozilla/dom/CredentialsContainer.h"
1213
#include "mozilla/dom/ChromeUtils.h"
14+
#include "mozilla/dom/Navigator.h"
1315
#include "mozilla/dom/Promise.h"
1416
#include "mozilla/dom/PublicKeyCredential.h"
1517
#include "mozilla/dom/WebAuthenticationBinding.h"
18+
#include "mozilla/dom/WebAuthnManager.h"
1619
#include "nsCycleCollectionParticipant.h"
1720

1821
#ifdef XP_WIN
@@ -117,46 +120,16 @@ void PublicKeyCredential::SetAssertionResponse(
117120
already_AddRefed<Promise>
118121
PublicKeyCredential::IsUserVerifyingPlatformAuthenticatorAvailable(
119122
GlobalObject& aGlobal, ErrorResult& aError) {
120-
RefPtr<Promise> promise =
121-
Promise::Create(xpc::CurrentNativeGlobal(aGlobal.Context()), aError);
122-
if (aError.Failed()) {
123+
nsCOMPtr<nsPIDOMWindowInner> window =
124+
do_QueryInterface(aGlobal.GetAsSupports());
125+
if (!window) {
126+
aError.Throw(NS_ERROR_FAILURE);
123127
return nullptr;
124128
}
125129

126-
// https://w3c.github.io/webauthn/#isUserVerifyingPlatformAuthenticatorAvailable
127-
//
128-
// If on latest windows, call system APIs, otherwise return false, as we don't
129-
// have other UVPAAs available at this time.
130-
#ifdef XP_WIN
131-
132-
if (WinWebAuthnService::IsUserVerifyingPlatformAuthenticatorAvailable()) {
133-
promise->MaybeResolve(true);
134-
return promise.forget();
135-
}
136-
137-
promise->MaybeResolve(false);
138-
#elif defined(MOZ_WIDGET_ANDROID)
139-
if (StaticPrefs::
140-
security_webauthn_webauthn_enable_android_fido2_residentkey()) {
141-
auto result = java::WebAuthnTokenManager::
142-
WebAuthnIsUserVerifyingPlatformAuthenticatorAvailable();
143-
auto geckoResult = java::GeckoResult::LocalRef(std::move(result));
144-
MozPromise<bool, bool, false>::FromGeckoResult(geckoResult)
145-
->Then(
146-
GetMainThreadSerialEventTarget(), __func__,
147-
[promise](const MozPromise<bool, bool, false>::ResolveOrRejectValue&
148-
aValue) {
149-
if (aValue.IsResolve()) {
150-
promise->MaybeResolve(aValue.ResolveValue());
151-
}
152-
});
153-
} else {
154-
promise->MaybeResolve(false);
155-
}
156-
#else
157-
promise->MaybeResolve(false);
158-
#endif
159-
return promise.forget();
130+
RefPtr<WebAuthnManager> manager =
131+
window->Navigator()->Credentials()->GetWebAuthnManager();
132+
return manager->IsUVPAA(aGlobal, aError);
160133
}
161134

162135
/* static */

dom/webauthn/WebAuthnManager.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "mozilla/dom/PublicKeyCredential.h"
1919
#include "mozilla/dom/Promise.h"
2020
#include "mozilla/dom/PWebAuthnTransaction.h"
21+
#include "mozilla/dom/PWebAuthnTransactionChild.h"
2122
#include "mozilla/dom/WebAuthnManager.h"
2223
#include "mozilla/dom/WebAuthnTransactionChild.h"
2324
#include "mozilla/dom/WebAuthnUtil.h"
@@ -727,6 +728,32 @@ already_AddRefed<Promise> WebAuthnManager::Store(const Credential& aCredential,
727728
return promise.forget();
728729
}
729730

731+
already_AddRefed<Promise> WebAuthnManager::IsUVPAA(GlobalObject& aGlobal,
732+
ErrorResult& aError) {
733+
RefPtr<Promise> promise =
734+
Promise::Create(xpc::CurrentNativeGlobal(aGlobal.Context()), aError);
735+
if (aError.Failed()) {
736+
return nullptr;
737+
}
738+
739+
if (!MaybeCreateBackgroundActor()) {
740+
promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
741+
return promise.forget();
742+
}
743+
744+
mChild->SendRequestIsUVPAA()->Then(
745+
GetCurrentSerialEventTarget(), __func__,
746+
[promise](const PWebAuthnTransactionChild::RequestIsUVPAAPromise::
747+
ResolveOrRejectValue& aValue) {
748+
if (aValue.IsResolve()) {
749+
promise->MaybeResolve(aValue.ResolveValue());
750+
} else {
751+
promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
752+
}
753+
});
754+
return promise.forget();
755+
}
756+
730757
void WebAuthnManager::FinishMakeCredential(
731758
const uint64_t& aTransactionId,
732759
const WebAuthnMakeCredentialResult& aResult) {

dom/webauthn/WebAuthnManager.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ class WebAuthnManager final : public WebAuthnManagerBase, public AbortFollower {
9595
already_AddRefed<Promise> Store(const Credential& aCredential,
9696
ErrorResult& aError);
9797

98+
already_AddRefed<Promise> IsUVPAA(GlobalObject& aGlobal, ErrorResult& aError);
99+
98100
// WebAuthnManagerBase
99101

100102
void FinishMakeCredential(

dom/webauthn/WebAuthnService.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ WebAuthnService::GetAssertion(uint64_t aTransactionId,
4040
aArgs, aPromise);
4141
}
4242

43+
NS_IMETHODIMP
44+
WebAuthnService::GetIsUVPAA(bool* aAvailable) {
45+
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
46+
return mTestService->GetIsUVPAA(aAvailable);
47+
}
48+
return mPlatformService->GetIsUVPAA(aAvailable);
49+
}
50+
4351
NS_IMETHODIMP
4452
WebAuthnService::Reset() {
4553
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {

dom/webauthn/WebAuthnTransactionParent.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,84 @@ mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestCancel(
280280
return IPC_OK();
281281
}
282282

283+
mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestIsUVPAA(
284+
RequestIsUVPAAResolver&& aResolver) {
285+
#ifdef MOZ_WIDGET_ANDROID
286+
// Try the nsIWebAuthnService. If we're configured for tests we
287+
// will get a result. Otherwise we expect NS_ERROR_NOT_IMPLEMENTED.
288+
nsCOMPtr<nsIWebAuthnService> service(
289+
do_GetService("@mozilla.org/webauthn/service;1"));
290+
bool available;
291+
nsresult rv = service->GetIsUVPAA(&available);
292+
if (NS_SUCCEEDED(rv)) {
293+
aResolver(available);
294+
return IPC_OK();
295+
}
296+
297+
// Don't consult the platform API if resident key support is disabled.
298+
if (!StaticPrefs::
299+
security_webauthn_webauthn_enable_android_fido2_residentkey()) {
300+
aResolver(false);
301+
return IPC_OK();
302+
}
303+
304+
// The GeckoView implementation of
305+
// isUserVerifiyingPlatformAuthenticatorAvailable does not block, but we must
306+
// call it on the main thread. It returns a MozPromise which we can ->Then to
307+
// call aResolver on the IPDL background thread.
308+
//
309+
// Bug 1550788: there is an unnecessary layer of dispatching here: ipdl ->
310+
// main -> a background thread. Other platforms just do ipdl -> a background
311+
// thread.
312+
nsCOMPtr<nsISerialEventTarget> target = GetCurrentSerialEventTarget();
313+
nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
314+
__func__, [target, resolver = std::move(aResolver)]() {
315+
auto result = java::WebAuthnTokenManager::
316+
WebAuthnIsUserVerifyingPlatformAuthenticatorAvailable();
317+
auto geckoResult = java::GeckoResult::LocalRef(std::move(result));
318+
MozPromise<bool, bool, false>::FromGeckoResult(geckoResult)
319+
->Then(
320+
target, __func__,
321+
[resolver](
322+
const MozPromise<bool, bool, false>::ResolveOrRejectValue&
323+
aValue) {
324+
if (aValue.IsResolve()) {
325+
resolver(aValue.ResolveValue());
326+
} else {
327+
resolver(false);
328+
}
329+
});
330+
}));
331+
NS_DispatchToMainThread(runnable.forget());
332+
return IPC_OK();
333+
334+
#else
335+
336+
nsCOMPtr<nsISerialEventTarget> target = GetCurrentSerialEventTarget();
337+
nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
338+
__func__, [target, resolver = std::move(aResolver)]() {
339+
bool available;
340+
nsCOMPtr<nsIWebAuthnService> service(
341+
do_GetService("@mozilla.org/webauthn/service;1"));
342+
nsresult rv = service->GetIsUVPAA(&available);
343+
if (NS_FAILED(rv)) {
344+
available = false;
345+
}
346+
BoolPromise::CreateAndResolve(available, __func__)
347+
->Then(target, __func__,
348+
[resolver](const BoolPromise::ResolveOrRejectValue& value) {
349+
if (value.IsResolve()) {
350+
resolver(value.ResolveValue());
351+
} else {
352+
resolver(false);
353+
}
354+
});
355+
}));
356+
NS_DispatchBackgroundTask(runnable.forget(), NS_DISPATCH_EVENT_MAY_BLOCK);
357+
return IPC_OK();
358+
#endif
359+
}
360+
283361
mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvDestroyMe() {
284362
::mozilla::ipc::AssertIsOnBackgroundThread();
285363

dom/webauthn/WebAuthnTransactionParent.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ class WebAuthnTransactionParent final : public PWebAuthnTransactionParent {
3535
mozilla::ipc::IPCResult RecvRequestCancel(
3636
const Tainted<uint64_t>& aTransactionId);
3737

38+
mozilla::ipc::IPCResult RecvRequestIsUVPAA(
39+
RequestIsUVPAAResolver&& aResolver);
40+
3841
mozilla::ipc::IPCResult RecvDestroyMe();
3942

4043
virtual void ActorDestroy(ActorDestroyReason aWhy) override;

dom/webauthn/WinWebAuthnService.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -132,18 +132,20 @@ bool WinWebAuthnService::AreWebAuthNApisAvailable() {
132132
gWinWebauthnGetApiVersionNumber() >= kMinWinWebAuthNApiVersion;
133133
}
134134

135-
// static
136-
bool WinWebAuthnService::IsUserVerifyingPlatformAuthenticatorAvailable() {
135+
NS_IMETHODIMP
136+
WinWebAuthnService::GetIsUVPAA(bool* aAvailable) {
137137
nsresult rv = EnsureWinWebAuthnModuleLoaded();
138-
NS_ENSURE_SUCCESS(rv, false);
138+
NS_ENSURE_SUCCESS(rv, rv);
139139

140140
if (WinWebAuthnService::AreWebAuthNApisAvailable()) {
141141
BOOL isUVPAA = FALSE;
142142
StaticAutoReadLock lock(gWinWebAuthnModuleLock);
143-
return gWinWebAuthnModule && gWinWebauthnIsUVPAA(&isUVPAA) == S_OK &&
144-
isUVPAA == TRUE;
143+
*aAvailable = gWinWebAuthnModule && gWinWebauthnIsUVPAA(&isUVPAA) == S_OK &&
144+
isUVPAA == TRUE;
145+
} else {
146+
*aAvailable = false;
145147
}
146-
return false;
148+
return NS_OK;
147149
}
148150

149151
NS_IMETHODIMP

dom/webauthn/authrs_bridge/src/lib.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,17 @@ impl AuthrsService {
614614
.or(Err(NS_ERROR_FAILURE))
615615
}
616616

617+
xpcom_method!(get_is_uvpaa => GetIsUVPAA() -> bool);
618+
fn get_is_uvpaa(&self) -> Result<bool, nsresult> {
619+
if static_prefs::pref!("security.webauth.webauthn_enable_usbtoken") {
620+
Ok(false)
621+
} else if static_prefs::pref!("security.webauth.webauthn_enable_softtoken") {
622+
Ok(self.test_token_manager.has_platform_authenticator())
623+
} else {
624+
Err(NS_ERROR_NOT_AVAILABLE)
625+
}
626+
}
627+
617628
xpcom_method!(make_credential => MakeCredential(aTid: u64, aBrowsingContextId: u64, aArgs: *const nsIWebAuthnRegisterArgs, aPromise: *const nsIWebAuthnRegisterPromise));
618629
fn make_credential(
619630
&self,

dom/webauthn/authrs_bridge/src/test_token.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -870,4 +870,19 @@ impl TestTokenManager {
870870
.may_block(true)
871871
.dispatch_background_task();
872872
}
873+
874+
pub fn has_platform_authenticator(&self) -> bool {
875+
if !static_prefs::pref!("security.webauth.webauthn_enable_softtoken") {
876+
return false;
877+
}
878+
879+
for token in self.state.lock().unwrap().values_mut() {
880+
let _ = token.init();
881+
if token.transport.as_str() == "internal" {
882+
return true;
883+
}
884+
}
885+
886+
false
887+
}
873888
}

dom/webauthn/nsIWebAuthnService.idl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ interface nsICredentialParameters : nsISupports
2222
[scriptable, uuid(e236a9b4-a26f-11ed-b6cc-07a9834e19b1)]
2323
interface nsIWebAuthnService : nsISupports
2424
{
25+
// IsUserVerifyingPlatformAuthenticatorAvailable
26+
readonly attribute bool isUVPAA;
27+
2528
void makeCredential(
2629
in uint64_t aTransactionId,
2730
in uint64_t browsingContextId,

dom/webauthn/tests/test_webauthn_isplatformauthenticatoravailable.html

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,30 @@ <h1>Test for W3C Web Authentication isUserVerifyingPlatformAuthenticatorAvailabl
1818
<script class="testbody" type="text/javascript">
1919
"use strict";
2020

21-
add_task(async function test_is_platform_available() {
22-
// This test ensures that isUserVerifyingPlatformAuthenticatorAvailable()
23-
// is a callable method, but with the softtoken enabled, it's not useful to
24-
// figure out what it actually returns, so we'll just make sure it runs.
25-
await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
26-
.then(function(aResult) {
27-
ok(true, "Resolved: " + aResult);
28-
})
29-
.catch(function(aProblem) {
30-
ok(false, "Problem encountered: " + aProblem);
31-
});
21+
add_task(async function test_uvpaa_with_no_authenticator() {
22+
let uvpaa = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
23+
ok(uvpaa === false, "Platform authenticator is not available");
3224
});
3325

26+
add_task(async () => {
27+
await addVirtualAuthenticator("ctap2_1", "usb");
28+
});
29+
30+
add_task(async function test_uvpaa_with_usb_authenticator() {
31+
let uvpaa = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
32+
ok(uvpaa === false, "Platform authenticator is not available");
33+
});
34+
35+
add_task(async () => {
36+
await addVirtualAuthenticator("ctap2_1", "internal");
37+
});
38+
39+
add_task(async function test_uvpaa_with_usb_and_platform_authenticator() {
40+
let uvpaa = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
41+
ok(uvpaa === true, "Platform authenticator is available");
42+
});
43+
44+
3445
</script>
3546

3647
</body>

0 commit comments

Comments
 (0)