Bug 1753919 - AndroidWebAuthnTokenManager uses GeckoResult directly instead of callback. r=geckoview-reviewers,agi

Since WebAuthnTokenManager uses GeckoReuslt, we can remove callback of WebAuthnTokenManager.

Differential Revision: https://phabricator.services.mozilla.com/D137973
This commit is contained in:
Makoto Kato 2022-02-09 06:59:52 +00:00
Родитель 988ddd76dc
Коммит 926307a1d7
6 изменённых файлов: 139 добавлений и 154 удалений

Просмотреть файл

@ -10,9 +10,38 @@
#include "AndroidWebAuthnTokenManager.h"
#include "JavaBuiltins.h"
#include "JavaExceptions.h"
#include "mozilla/java/WebAuthnTokenManagerWrappers.h"
#include "mozilla/jni/Conversions.h"
namespace mozilla {
namespace jni {
template <>
dom::AndroidWebAuthnResult Java2Native(mozilla::jni::Object::Param aData,
JNIEnv* aEnv) {
// TODO:
// AndroidWebAuthnResult stores successful both result and failure result.
// We should split it into success and failure (Bug 1754157)
if (aData.IsInstanceOf<jni::Throwable>()) {
java::sdk::Throwable::LocalRef throwable(aData);
return dom::AndroidWebAuthnResult(throwable->GetMessage()->ToString());
}
if (aData
.IsInstanceOf<java::WebAuthnTokenManager::MakeCredentialResponse>()) {
java::WebAuthnTokenManager::MakeCredentialResponse::LocalRef response(
aData);
return dom::AndroidWebAuthnResult(response);
}
MOZ_ASSERT(
aData.IsInstanceOf<java::WebAuthnTokenManager::GetAssertionResponse>());
java::WebAuthnTokenManager::GetAssertionResponse::LocalRef response(aData);
return dom::AndroidWebAuthnResult(response);
}
} // namespace jni
namespace dom {
static nsIThread* gAndroidPBackgroundThread;
@ -65,7 +94,7 @@ RefPtr<U2FRegisterPromise> AndroidWebAuthnTokenManager::Register(
GetMainThreadEventTarget()->Dispatch(NS_NewRunnableFunction(
"java::WebAuthnTokenManager::WebAuthnMakeCredential",
[aInfo, aForceNoneAttestation]() {
[self = RefPtr{this}, aInfo, aForceNoneAttestation]() {
AssertIsOnMainThread();
// Produce the credential exclusion list
@ -193,9 +222,22 @@ RefPtr<U2FRegisterPromise> AndroidWebAuthnTokenManager::Register(
const_cast<void*>(static_cast<const void*>(uidBuf.Elements())),
uidBuf.Length());
java::WebAuthnTokenManager::WebAuthnMakeCredential(
auto result = java::WebAuthnTokenManager::WebAuthnMakeCredential(
credentialBundle, uid, challenge, idList, transportList,
authSelBundle, extensionsBundle);
auto geckoResult = java::GeckoResult::LocalRef(std::move(result));
// This is likely running on the main thread, so we'll always dispatch
// to the background for state updates.
MozPromise<AndroidWebAuthnResult, AndroidWebAuthnResult,
false>::FromGeckoResult(geckoResult)
->Then(
GetMainThreadSerialEventTarget(), __func__,
[self = std::move(self)](const AndroidWebAuthnResult& aValue) {
self->HandleRegisterResult(aValue);
},
[self = std::move(self)](const AndroidWebAuthnResult& aValue) {
self->HandleRegisterResult(aValue);
});
}));
return mRegisterPromise.Ensure(__func__);
@ -203,6 +245,10 @@ RefPtr<U2FRegisterPromise> AndroidWebAuthnTokenManager::Register(
void AndroidWebAuthnTokenManager::HandleRegisterResult(
const AndroidWebAuthnResult& aResult) {
if (!gAndroidPBackgroundThread) {
// Promise is already rejected when shutting down background thread
return;
}
// This is likely running on the main thread, so we'll always dispatch to the
// background for state updates.
if (aResult.IsError()) {
@ -234,7 +280,8 @@ RefPtr<U2FSignPromise> AndroidWebAuthnTokenManager::Sign(
ClearPromises();
GetMainThreadEventTarget()->Dispatch(NS_NewRunnableFunction(
"java::WebAuthnTokenManager::WebAuthnGetAssertion", [aInfo]() {
"java::WebAuthnTokenManager::WebAuthnGetAssertion",
[self = RefPtr{this}, aInfo]() {
AssertIsOnMainThread();
jni::ObjectArray::LocalRef idList =
@ -298,9 +345,20 @@ RefPtr<U2FSignPromise> AndroidWebAuthnTokenManager::Sign(
GECKOBUNDLE_FINISH(assertionBundle);
GECKOBUNDLE_FINISH(extensionsBundle);
java::WebAuthnTokenManager::WebAuthnGetAssertion(
auto result = java::WebAuthnTokenManager::WebAuthnGetAssertion(
challenge, idList, transportList, assertionBundle,
extensionsBundle);
auto geckoResult = java::GeckoResult::LocalRef(std::move(result));
MozPromise<AndroidWebAuthnResult, AndroidWebAuthnResult,
false>::FromGeckoResult(geckoResult)
->Then(
GetMainThreadSerialEventTarget(), __func__,
[self = std::move(self)](const AndroidWebAuthnResult& aValue) {
self->HandleSignResult(aValue);
},
[self = std::move(self)](const AndroidWebAuthnResult& aValue) {
self->HandleSignResult(aValue);
});
}));
return mSignPromise.Ensure(__func__);
@ -308,6 +366,10 @@ RefPtr<U2FSignPromise> AndroidWebAuthnTokenManager::Sign(
void AndroidWebAuthnTokenManager::HandleSignResult(
const AndroidWebAuthnResult& aResult) {
if (!gAndroidPBackgroundThread) {
// Promise is already rejected when shutting down background thread
return;
}
// This is likely running on the main thread, so we'll always dispatch to the
// background for state updates.
if (aResult.IsError()) {
@ -340,5 +402,41 @@ void AndroidWebAuthnTokenManager::Cancel() {
ClearPromises();
}
AndroidWebAuthnResult::AndroidWebAuthnResult(
const java::WebAuthnTokenManager::MakeCredentialResponse::LocalRef&
aResponse) {
mClientDataJSON.Assign(
reinterpret_cast<const char*>(
aResponse->ClientDataJson()->GetElements().Elements()),
aResponse->ClientDataJson()->Length());
mKeyHandle.Assign(reinterpret_cast<uint8_t*>(
aResponse->KeyHandle()->GetElements().Elements()),
aResponse->KeyHandle()->Length());
mAttObj.Assign(reinterpret_cast<uint8_t*>(
aResponse->AttestationObject()->GetElements().Elements()),
aResponse->AttestationObject()->Length());
}
AndroidWebAuthnResult::AndroidWebAuthnResult(
const java::WebAuthnTokenManager::GetAssertionResponse::LocalRef&
aResponse) {
mClientDataJSON.Assign(
reinterpret_cast<const char*>(
aResponse->ClientDataJson()->GetElements().Elements()),
aResponse->ClientDataJson()->Length());
mKeyHandle.Assign(reinterpret_cast<uint8_t*>(
aResponse->KeyHandle()->GetElements().Elements()),
aResponse->KeyHandle()->Length());
mAuthData.Assign(reinterpret_cast<uint8_t*>(
aResponse->AuthData()->GetElements().Elements()),
aResponse->AuthData()->Length());
mSignature.Assign(reinterpret_cast<uint8_t*>(
aResponse->Signature()->GetElements().Elements()),
aResponse->Signature()->Length());
mUserHandle.Assign(reinterpret_cast<uint8_t*>(
aResponse->UserHandle()->GetElements().Elements()),
aResponse->UserHandle()->Length());
}
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -9,6 +9,7 @@
#include "mozilla/dom/CryptoBuffer.h"
#include "mozilla/dom/U2FTokenTransport.h"
#include "mozilla/java/WebAuthnTokenManagerNatives.h"
namespace mozilla {
namespace dom {
@ -32,7 +33,15 @@ class AndroidWebAuthnResult {
explicit AndroidWebAuthnResult(const nsAString& aErrorCode)
: mErrorCode(aErrorCode) {}
explicit AndroidWebAuthnResult() {}
explicit AndroidWebAuthnResult(
const java::WebAuthnTokenManager::MakeCredentialResponse::LocalRef&
aResponse);
explicit AndroidWebAuthnResult(
const java::WebAuthnTokenManager::GetAssertionResponse::LocalRef&
aResponse);
AndroidWebAuthnResult() = delete;
bool IsError() const { return NS_FAILED(GetError()); }
@ -114,13 +123,13 @@ class AndroidWebAuthnTokenManager final : public U2FTokenTransport {
void Drop() override;
static AndroidWebAuthnTokenManager* GetInstance();
private:
void HandleRegisterResult(const AndroidWebAuthnResult& aResult);
void HandleSignResult(const AndroidWebAuthnResult& aResult);
static AndroidWebAuthnTokenManager* GetInstance();
private:
void ClearPromises() {
mRegisterPromise.RejectIfExists(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
mSignPromise.RejectIfExists(NS_ERROR_DOM_UNKNOWN_ERR, __func__);

Просмотреть файл

@ -118,6 +118,7 @@ import org.mozilla.gecko.util.GeckoBundle;
DIRECT,
}
@WrapForJNI
public static class MakeCredentialResponse {
public final byte[] clientDataJson;
public final byte[] keyHandle;
@ -301,7 +302,6 @@ import org.mozilla.gecko.util.GeckoBundle;
}
},
e -> {
Log.w(LOGTAG, "Failed to launch activity: ", e);
Log.w(LOGTAG, "Failed to launch activity: ", e);
result.completeExceptionally(new WebAuthnTokenManager.Exception("ABORT_ERR"));
});
@ -317,7 +317,7 @@ import org.mozilla.gecko.util.GeckoBundle;
}
@WrapForJNI(calledFrom = "gecko")
private static void webAuthnMakeCredential(
private static GeckoResult<MakeCredentialResponse> webAuthnMakeCredential(
final GeckoBundle credentialBundle,
final ByteBuffer userId,
final ByteBuffer challenge,
@ -327,8 +327,6 @@ import org.mozilla.gecko.util.GeckoBundle;
final GeckoBundle extensions) {
final ArrayList<WebAuthnPublicCredential> excludeList;
// TODO: Return a GeckoResult instead, Bug 1550116
final byte[] challBytes = new byte[challenge.remaining()];
final byte[] userBytes = new byte[userId.remaining()];
try {
@ -338,44 +336,28 @@ import org.mozilla.gecko.util.GeckoBundle;
excludeList = WebAuthnPublicCredential.CombineBuffers(idList, transportList);
} catch (final RuntimeException e) {
Log.w(LOGTAG, "Couldn't extract nio byte arrays!", e);
webAuthnMakeCredentialReturnError("UNKNOWN_ERR");
return;
return GeckoResult.fromException(new WebAuthnTokenManager.Exception("UNKNOWN_ERR"));
}
try {
final GeckoResult<MakeCredentialResponse> result =
makeCredential(
credentialBundle,
userBytes,
challBytes,
excludeList.toArray(new WebAuthnPublicCredential[0]),
authenticatorSelection,
extensions);
result.accept(
cred -> {
webAuthnMakeCredentialFinish(
cred.clientDataJson, cred.keyHandle, cred.attestationObject);
},
e -> {
webAuthnGetAssertionReturnError(e.getMessage());
});
return makeCredential(
credentialBundle,
userBytes,
challBytes,
excludeList.toArray(new WebAuthnPublicCredential[0]),
authenticatorSelection,
extensions);
} catch (final Exception e) {
// We need to ensure we catch any possible exception here in order to ensure
// that the Promise on the content side is appropriately rejected. In particular,
// we will get `NoClassDefFoundError` if we're running on a device that does not
// have Google Play Services.
Log.w(LOGTAG, "Couldn't make credential", e);
webAuthnMakeCredentialReturnError("UNKNOWN_ERR");
return GeckoResult.fromException(new WebAuthnTokenManager.Exception("UNKNOWN_ERR"));
}
}
@WrapForJNI(dispatchTo = "gecko")
/* package */ static native void webAuthnMakeCredentialFinish(
final byte[] clientDataJson, final byte[] keyHandle, final byte[] attestationObject);
@WrapForJNI(dispatchTo = "gecko")
/* package */ static native void webAuthnMakeCredentialReturnError(String errorCode);
@WrapForJNI
public static class GetAssertionResponse {
public final byte[] clientDataJson;
public final byte[] keyHandle;
@ -412,7 +394,7 @@ import org.mozilla.gecko.util.GeckoBundle;
return new WebAuthnTokenManager.Exception(responseData.getErrorCode().name());
}
public static GeckoResult<GetAssertionResponse> getAssertion(
private static GeckoResult<GetAssertionResponse> getAssertion(
final byte[] challenge,
final WebAuthnTokenManager.WebAuthnPublicCredential[] allowList,
final GeckoBundle assertionBundle,
@ -533,7 +515,7 @@ import org.mozilla.gecko.util.GeckoBundle;
}
@WrapForJNI(calledFrom = "gecko")
private static void webAuthnGetAssertion(
private static GeckoResult<GetAssertionResponse> webAuthnGetAssertion(
final ByteBuffer challenge,
final Object[] idList,
final ByteBuffer transportList,
@ -541,53 +523,27 @@ import org.mozilla.gecko.util.GeckoBundle;
final GeckoBundle extensions) {
final ArrayList<WebAuthnPublicCredential> allowList;
// TODO: Return a GeckoResult instead, Bug 1550116
final byte[] challBytes = new byte[challenge.remaining()];
try {
challenge.get(challBytes);
allowList = WebAuthnPublicCredential.CombineBuffers(idList, transportList);
} catch (final RuntimeException e) {
Log.w(LOGTAG, "Couldn't extract nio byte arrays!", e);
webAuthnGetAssertionReturnError("UNKNOWN_ERR");
return;
return GeckoResult.fromException(new WebAuthnTokenManager.Exception("UNKNOWN_ERR"));
}
try {
getAssertion(
challBytes,
allowList.toArray(new WebAuthnPublicCredential[0]),
assertionBundle,
extensions)
.accept(
response -> {
webAuthnGetAssertionFinish(
response.clientDataJson,
response.keyHandle,
response.authData,
response.signature,
response.userHandle);
},
e -> {
webAuthnGetAssertionReturnError(e.getMessage());
});
return getAssertion(
challBytes,
allowList.toArray(new WebAuthnPublicCredential[0]),
assertionBundle,
extensions);
} catch (final java.lang.Exception e) {
Log.w(LOGTAG, "Couldn't get assertion", e);
webAuthnGetAssertionReturnError("UNKNOWN_ERR");
return GeckoResult.fromException(new WebAuthnTokenManager.Exception("UNKNOWN_ERR"));
}
}
@WrapForJNI(dispatchTo = "gecko")
/* package */ static native void webAuthnGetAssertionFinish(
final byte[] clientDataJson,
final byte[] keyHandle,
final byte[] authData,
final byte[] signature,
final byte[] userHandle);
@WrapForJNI(dispatchTo = "gecko")
/* package */ static native void webAuthnGetAssertionReturnError(String errorCode);
@WrapForJNI(calledFrom = "gecko")
private static GeckoResult<Boolean> webAuthnIsUserVerifyingPlatformAuthenticatorAvailable() {
final Task<Boolean> task;

Просмотреть файл

@ -1,79 +0,0 @@
/* -*- Mode: c++; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*-
* 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 WebAuthnTokenManager_h
#define WebAuthnTokenManager_h
#include "mozilla/dom/AndroidWebAuthnTokenManager.h"
#include "mozilla/java/WebAuthnTokenManagerNatives.h"
namespace mozilla {
class WebAuthnTokenManager final
: public java::WebAuthnTokenManager::Natives<WebAuthnTokenManager> {
public:
static void WebAuthnMakeCredentialFinish(
jni::ByteArray::Param aClientDataJson, jni::ByteArray::Param aKeyHandle,
jni::ByteArray::Param aAttestationObject) {
mozilla::dom::AndroidWebAuthnResult result;
result.mClientDataJSON.Assign(
reinterpret_cast<const char*>(
aClientDataJson->GetElements().Elements()),
aClientDataJson->Length());
result.mKeyHandle.Assign(
reinterpret_cast<uint8_t*>(aKeyHandle->GetElements().Elements()),
aKeyHandle->Length());
result.mAttObj.Assign(reinterpret_cast<uint8_t*>(
aAttestationObject->GetElements().Elements()),
aAttestationObject->Length());
mozilla::dom::AndroidWebAuthnTokenManager::GetInstance()
->HandleRegisterResult(std::move(result));
}
static void WebAuthnMakeCredentialReturnError(jni::String::Param aErrorCode) {
mozilla::dom::AndroidWebAuthnResult result(aErrorCode->ToString());
mozilla::dom::AndroidWebAuthnTokenManager::GetInstance()
->HandleRegisterResult(std::move(result));
}
static void WebAuthnGetAssertionFinish(jni::ByteArray::Param aClientDataJson,
jni::ByteArray::Param aKeyHandle,
jni::ByteArray::Param aAuthData,
jni::ByteArray::Param aSignature,
jni::ByteArray::Param aUserHandle) {
mozilla::dom::AndroidWebAuthnResult result;
result.mClientDataJSON.Assign(
reinterpret_cast<const char*>(
aClientDataJson->GetElements().Elements()),
aClientDataJson->Length());
result.mKeyHandle.Assign(
reinterpret_cast<uint8_t*>(aKeyHandle->GetElements().Elements()),
aKeyHandle->Length());
result.mAuthData.Assign(
reinterpret_cast<uint8_t*>(aAuthData->GetElements().Elements()),
aAuthData->Length());
result.mSignature.Assign(
reinterpret_cast<uint8_t*>(aSignature->GetElements().Elements()),
aSignature->Length());
result.mUserHandle.Assign(
reinterpret_cast<uint8_t*>(aUserHandle->GetElements().Elements()),
aUserHandle->Length());
mozilla::dom::AndroidWebAuthnTokenManager::GetInstance()->HandleSignResult(
std::move(result));
}
static void WebAuthnGetAssertionReturnError(jni::String::Param aErrorCode) {
mozilla::dom::AndroidWebAuthnResult result(aErrorCode->ToString());
mozilla::dom::AndroidWebAuthnTokenManager::GetInstance()->HandleSignResult(
std::move(result));
}
};
} // namespace mozilla
#endif

Просмотреть файл

@ -3,3 +3,6 @@
[java.lang.IllegalArgumentException = skip:true]
<init>(Ljava/lang/String;)V =
[java.lang.Throwable = skip:true]
getMessage()Ljava/lang/String; =

Просмотреть файл

@ -75,7 +75,6 @@
#include "Telemetry.h"
#include "WebExecutorSupport.h"
#include "Base64UtilsSupport.h"
#include "WebAuthnTokenManager.h"
#ifdef DEBUG_ANDROID_EVENTS
# define EVLOG(args...) ALOG(args)
@ -437,7 +436,6 @@ nsAppShell::nsAppShell()
mozilla::widget::Base64UtilsSupport::Init();
nsWindow::InitNatives();
mozilla::gl::AndroidSurfaceTexture::Init();
mozilla::WebAuthnTokenManager::Init();
mozilla::widget::GeckoTelemetryDelegate::Init();
java::GeckoThread::SetState(java::GeckoThread::State::JNI_READY());