mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-03 23:30:46 +00:00
Backed out 4 changesets (bug 1838932, bug 1864544, bug 1853951, bug 1824215) for causing build bustages in WinWebAuthnService.cpp CLOSED TREE
Backed out changeset 8199d5ce1bfa (bug 1864544) Backed out changeset 1084e05b2914 (bug 1853951) Backed out changeset 55320a72238f (bug 1824215) Backed out changeset 876476ccd759 (bug 1838932)
This commit is contained in:
parent
565db2a48e
commit
320a766374
@ -933,7 +933,6 @@ function verifySectionFieldDetails(sections, expectedSectionsInfo) {
|
||||
section: "",
|
||||
contactType: "",
|
||||
addressType: "",
|
||||
credentialType: "",
|
||||
},
|
||||
...expectedSection.default,
|
||||
...expectedFieldDetail,
|
||||
|
@ -14,13 +14,6 @@ webauthn-pin-required-prompt = Please enter the PIN for your device.
|
||||
|
||||
webauthn-select-sign-result-unknown-account = Unknown account
|
||||
|
||||
webauthn-a-passkey-label = Use a passkey
|
||||
webauthn-another-passkey-label = Use another passkey
|
||||
|
||||
# Variables:
|
||||
# $domain (String): the domain of the site.
|
||||
webauthn-specific-passkey-label = Passkey for { $domain }
|
||||
|
||||
# Variables:
|
||||
# $retriesLeft (Number): number of tries left
|
||||
webauthn-uv-invalid-long-prompt =
|
||||
|
@ -38,11 +38,6 @@
|
||||
#define DEFINED_AUTOCOMPLETE_FIELD_CONTACT_HINT
|
||||
#endif
|
||||
|
||||
#ifndef AUTOCOMPLETE_CREDENTIAL_TYPE
|
||||
#define AUTOCOMPLETE_CREDENTIAL_TYPE(name_, value_)
|
||||
#define DEFINED_AUTOCOMPLETE_CREDENTIAL_TYPE
|
||||
#endif
|
||||
|
||||
#ifndef AUTOCOMPLETE_CATEGORY
|
||||
#define AUTOCOMPLETE_CATEGORY(name_, value_)
|
||||
#define DEFINED_AUTOCOMPLETE_CATEGORY
|
||||
@ -185,9 +180,6 @@ AUTOCOMPLETE_FIELD_CONTACT_HINT(MOBILE, "mobile")
|
||||
AUTOCOMPLETE_FIELD_CONTACT_HINT(FAX, "fax")
|
||||
AUTOCOMPLETE_FIELD_CONTACT_HINT(PAGER, "pager")
|
||||
|
||||
// Credential types
|
||||
AUTOCOMPLETE_CREDENTIAL_TYPE(WEBAUTHN, "webauthn")
|
||||
|
||||
AUTOCOMPLETE_CATEGORY(NORMAL, "normal")
|
||||
AUTOCOMPLETE_CATEGORY(CONTACT, "contact")
|
||||
//-----------------------------------------------------
|
||||
@ -227,11 +219,6 @@ AUTOCOMPLETE_CATEGORY(CONTACT, "contact")
|
||||
#undef DEFINED_AUTOCOMPLETE_FIELD_CONTACT_HINT
|
||||
#endif
|
||||
|
||||
#ifdef DEFINED_AUTOCOMPLETE_CREDENTIAL_TYPE
|
||||
#undef AUTOCOMPLETE_CREDENTIAL_TYPE
|
||||
#undef DEFINED_AUTOCOMPLETE_CREDENTIAL_TYPE
|
||||
#endif
|
||||
|
||||
#ifdef DEFINED_AUTOCOMPLETE_CATEGORY
|
||||
#undef AUTOCOMPLETE_CATEGORY
|
||||
#undef DEFINED_AUTOCOMPLETE_CATEGORY
|
||||
|
@ -549,13 +549,6 @@ enum AutocompleteFieldContactHint : uint8_t {
|
||||
#undef AUTOCOMPLETE_FIELD_CONTACT_HINT
|
||||
};
|
||||
|
||||
enum AutocompleteCredentialType : uint8_t {
|
||||
#define AUTOCOMPLETE_CREDENTIAL_TYPE(name_, value_) \
|
||||
eAutocompleteCredentialType_##name_,
|
||||
#include "AutocompleteFieldList.h"
|
||||
#undef AUTOCOMPLETE_CREDENTIAL_TYPE
|
||||
};
|
||||
|
||||
enum AutocompleteCategory {
|
||||
#define AUTOCOMPLETE_CATEGORY(name_, value_) eAutocompleteCategory_##name_,
|
||||
#include "AutocompleteFieldList.h"
|
||||
@ -612,13 +605,6 @@ static const nsAttrValue::EnumTable kAutocompleteContactFieldHintTable[] = {
|
||||
#undef AUTOCOMPLETE_FIELD_CONTACT_HINT
|
||||
{nullptr, 0}};
|
||||
|
||||
static const nsAttrValue::EnumTable kAutocompleteCredentialTypeTable[] = {
|
||||
#define AUTOCOMPLETE_CREDENTIAL_TYPE(name_, value_) \
|
||||
{value_, eAutocompleteCredentialType_##name_},
|
||||
#include "AutocompleteFieldList.h"
|
||||
#undef AUTOCOMPLETE_CREDENTIAL_TYPE
|
||||
{nullptr, 0}};
|
||||
|
||||
namespace {
|
||||
|
||||
static PLDHashTable* sEventListenerManagersHash;
|
||||
@ -1179,18 +1165,6 @@ nsContentUtils::SerializeAutocompleteAttribute(
|
||||
}
|
||||
aResult += info.mFieldName;
|
||||
}
|
||||
|
||||
// The autocomplete attribute value "webauthn" is interpreted as both a
|
||||
// field name and a credential type. The corresponding IDL-exposed autofill
|
||||
// value is "webauthn", not "webauthn webauthn".
|
||||
if (!info.mCredentialType.IsEmpty() &&
|
||||
!(info.mCredentialType.Equals(u"webauthn"_ns) &&
|
||||
info.mCredentialType.Equals(aResult))) {
|
||||
if (!aResult.IsEmpty()) {
|
||||
aResult += ' ';
|
||||
}
|
||||
aResult += info.mCredentialType;
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
@ -1224,7 +1198,7 @@ nsContentUtils::InternalSerializeAutocompleteAttribute(
|
||||
}
|
||||
|
||||
uint32_t numTokens = aAttrVal->GetAtomCount();
|
||||
if (!numTokens || numTokens > INT32_MAX) {
|
||||
if (!numTokens) {
|
||||
return eAutocompleteAttrState_Invalid;
|
||||
}
|
||||
|
||||
@ -1232,46 +1206,6 @@ nsContentUtils::InternalSerializeAutocompleteAttribute(
|
||||
nsString tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
|
||||
AutocompleteCategory category;
|
||||
nsAttrValue enumValue;
|
||||
nsAutoString credentialTypeStr;
|
||||
|
||||
bool result = enumValue.ParseEnumValue(
|
||||
tokenString, kAutocompleteCredentialTypeTable, false);
|
||||
if (result) {
|
||||
if (!enumValue.Equals(u"webauthn"_ns, eIgnoreCase) || numTokens > 5) {
|
||||
return eAutocompleteAttrState_Invalid;
|
||||
}
|
||||
enumValue.ToString(credentialTypeStr);
|
||||
ASCIIToLower(credentialTypeStr);
|
||||
// category is Credential and the indexth token is "webauthn"
|
||||
if (index == 0) {
|
||||
aInfo.mFieldName.Assign(credentialTypeStr);
|
||||
aInfo.mCredentialType.Assign(credentialTypeStr);
|
||||
return eAutocompleteAttrState_Valid;
|
||||
}
|
||||
|
||||
--index;
|
||||
tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
|
||||
|
||||
// Only the Normal and Contact categories are allowed with webauthn
|
||||
// - disallow Credential
|
||||
if (enumValue.ParseEnumValue(tokenString, kAutocompleteCredentialTypeTable,
|
||||
false)) {
|
||||
return eAutocompleteAttrState_Invalid;
|
||||
}
|
||||
// - disallow Off and Automatic
|
||||
if (enumValue.ParseEnumValue(tokenString, kAutocompleteFieldNameTable,
|
||||
false)) {
|
||||
if (enumValue.Equals(u"off"_ns, eIgnoreCase) ||
|
||||
enumValue.Equals(u"on"_ns, eIgnoreCase)) {
|
||||
return eAutocompleteAttrState_Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
// Proceed to process the remaining tokens as if "webauthn" was not present.
|
||||
// We need to decrement numTokens to enforce the correct per-category limits
|
||||
// on the maximum number of tokens.
|
||||
--numTokens;
|
||||
}
|
||||
|
||||
bool unsupported = false;
|
||||
if (!aGrantAllValidValue) {
|
||||
@ -1282,10 +1216,9 @@ nsContentUtils::InternalSerializeAutocompleteAttribute(
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoString fieldNameStr;
|
||||
result =
|
||||
nsAutoString str;
|
||||
bool result =
|
||||
enumValue.ParseEnumValue(tokenString, kAutocompleteFieldNameTable, false);
|
||||
|
||||
if (result) {
|
||||
// Off/Automatic/Normal categories.
|
||||
if (enumValue.Equals(u"off"_ns, eIgnoreCase) ||
|
||||
@ -1293,10 +1226,9 @@ nsContentUtils::InternalSerializeAutocompleteAttribute(
|
||||
if (numTokens > 1) {
|
||||
return eAutocompleteAttrState_Invalid;
|
||||
}
|
||||
enumValue.ToString(fieldNameStr);
|
||||
ASCIIToLower(fieldNameStr);
|
||||
aInfo.mFieldName.Assign(fieldNameStr);
|
||||
aInfo.mCredentialType.Assign(credentialTypeStr);
|
||||
enumValue.ToString(str);
|
||||
ASCIIToLower(str);
|
||||
aInfo.mFieldName.Assign(str);
|
||||
aInfo.mCanAutomaticallyPersist =
|
||||
!enumValue.Equals(u"off"_ns, eIgnoreCase);
|
||||
return eAutocompleteAttrState_Valid;
|
||||
@ -1331,11 +1263,10 @@ nsContentUtils::InternalSerializeAutocompleteAttribute(
|
||||
category = eAutocompleteCategory_CONTACT;
|
||||
}
|
||||
|
||||
enumValue.ToString(fieldNameStr);
|
||||
ASCIIToLower(fieldNameStr);
|
||||
enumValue.ToString(str);
|
||||
ASCIIToLower(str);
|
||||
aInfo.mFieldName.Assign(str);
|
||||
|
||||
aInfo.mFieldName.Assign(fieldNameStr);
|
||||
aInfo.mCredentialType.Assign(credentialTypeStr);
|
||||
aInfo.mCanAutomaticallyPersist = !enumValue.ParseEnumValue(
|
||||
tokenString, kAutocompleteNoPersistFieldNameTable, false);
|
||||
|
||||
@ -1402,7 +1333,6 @@ nsContentUtils::InternalSerializeAutocompleteAttribute(
|
||||
aInfo.mAddressType.Truncate();
|
||||
aInfo.mContactType.Truncate();
|
||||
aInfo.mFieldName.Truncate();
|
||||
aInfo.mCredentialType.Truncate();
|
||||
|
||||
return eAutocompleteAttrState_Invalid;
|
||||
}
|
||||
|
@ -163,8 +163,9 @@ already_AddRefed<Promise> CredentialsContainer::Get(
|
||||
return CreateAndRejectWithNotAllowed(mParent, aRv);
|
||||
}
|
||||
|
||||
if (conditionallyMediated &&
|
||||
!StaticPrefs::security_webauthn_enable_conditional_mediation()) {
|
||||
if (conditionallyMediated) {
|
||||
// Conditional mediation for WebAuthn Get() will be implemented in
|
||||
// Bug 1838932.
|
||||
RefPtr<Promise> promise = CreatePromise(mParent, aRv);
|
||||
if (!promise) {
|
||||
return nullptr;
|
||||
@ -175,8 +176,8 @@ already_AddRefed<Promise> CredentialsContainer::Get(
|
||||
}
|
||||
|
||||
EnsureWebAuthnManager();
|
||||
return mManager->GetAssertion(aOptions.mPublicKey.Value(),
|
||||
conditionallyMediated, aOptions.mSignal, aRv);
|
||||
return mManager->GetAssertion(aOptions.mPublicKey.Value(), aOptions.mSignal,
|
||||
aRv);
|
||||
}
|
||||
|
||||
if (aOptions.mIdentity.WasPassed() &&
|
||||
|
@ -33,14 +33,6 @@ var values = [
|
||||
["tel-extension", ""],
|
||||
["foobar", ""],
|
||||
["section-blue", ""],
|
||||
[" WEBAUTHN ", "webauthn"],
|
||||
|
||||
// One token + WebAuthn credential type
|
||||
["on webauthn", ""],
|
||||
["off webauthn", ""],
|
||||
["webauthn webauthn", ""],
|
||||
["username WebAuthn", "username webauthn"],
|
||||
["current-PASSWORD webauthn", "current-password webauthn"],
|
||||
|
||||
// Two tokens
|
||||
["on off", ""],
|
||||
@ -64,11 +56,6 @@ var values = [
|
||||
["fax url", ""],
|
||||
["section-blue name", "section-blue name"],
|
||||
["section-blue tel", "section-blue tel"],
|
||||
["webauthn username", ""],
|
||||
|
||||
// Two tokens + WebAuthn credential type
|
||||
["fax url webauthn", ""],
|
||||
["shipping tel webauthn", "shipping tel webauthn"],
|
||||
|
||||
// Three tokens
|
||||
["billing invalid tel", ""],
|
||||
@ -84,11 +71,6 @@ var values = [
|
||||
["section-blue home address-level2", ""],
|
||||
["section-blue shipping name", "section-blue shipping name"],
|
||||
["section-blue mobile tel", "section-blue mobile tel"],
|
||||
["shipping webauthn tel", ""],
|
||||
|
||||
// Three tokens + WebAuthn credential type
|
||||
["invalid mobile tel webauthn", ""],
|
||||
["section-blue shipping name webauthn", "section-blue shipping name webauthn"],
|
||||
|
||||
// Four tokens
|
||||
["billing billing mobile tel", ""],
|
||||
@ -96,19 +78,10 @@ var values = [
|
||||
["secti shipping work address-line1", ""],
|
||||
["section-blue shipping home name", ""],
|
||||
["section-blue shipping mobile tel", "section-blue shipping mobile tel"],
|
||||
["section-blue webauthn mobile tel", ""],
|
||||
|
||||
// Four tokens + WebAuthn credential type
|
||||
["section-blue shipping home name webauthn", ""],
|
||||
["section-blue shipping mobile tel webauthn", "section-blue shipping mobile tel webauthn"],
|
||||
|
||||
// Five tokens (invalid)
|
||||
["billing billing billing mobile tel", ""],
|
||||
["section-blue section-blue billing mobile tel", ""],
|
||||
["section-blue section-blue billing webauthn tel", ""],
|
||||
|
||||
// Five tokens + WebAuthn credential type (invalid)
|
||||
["billing billing billing mobile tel webauthn", ""],
|
||||
];
|
||||
|
||||
var types = [undefined, "hidden", "text", "search"]; // Valid types for all non-multiline hints.
|
||||
|
@ -44,14 +44,6 @@ var values = [
|
||||
["tel-extension", {fieldName: "tel-extension"}, ""],
|
||||
["foobar", {}, ""],
|
||||
["section-blue", {}, ""],
|
||||
[" WEBAUTHN ", {fieldName: "webauthn", credentialType: "webauthn"}, "webauthn"],
|
||||
|
||||
// One token + WebAuthn credential type
|
||||
["on webauthn", {}, ""],
|
||||
["off webauthn", {}, ""],
|
||||
["webauthn webauthn", {}, ""],
|
||||
["username WebAuthn", {fieldName: "username", credentialType: "webauthn"}, "username webauthn"],
|
||||
["current-PASSWORD webauthn", {fieldName: "current-password", credentialType: "webauthn", canAutomaticallyPersist: false}, "current-password webauthn"],
|
||||
|
||||
// Two tokens
|
||||
["on off", {}, ""],
|
||||
@ -75,11 +67,6 @@ var values = [
|
||||
["fax url", {}, ""],
|
||||
["section-blue name", {section: "section-blue", fieldName: "name"}, "section-blue name"],
|
||||
["section-blue tel", {section: "section-blue", fieldName: "tel"}, "section-blue tel"],
|
||||
["webauthn username", {}, ""],
|
||||
|
||||
// Two tokens + WebAuthn credential type
|
||||
["fax url webauthn", {}, ""],
|
||||
["shipping tel webauthn", {addressType: "shipping", fieldName: "tel", credentialType: "webauthn"}, "shipping tel webauthn"],
|
||||
|
||||
// Three tokens
|
||||
["billing invalid tel", {}, ""],
|
||||
@ -95,11 +82,6 @@ var values = [
|
||||
["section-blue home address-level2", {}, ""],
|
||||
["section-blue shipping name", {section: "section-blue", addressType: "shipping", fieldName: "name"}, "section-blue shipping name"],
|
||||
["section-blue mobile tel", {section: "section-blue", contactType: "mobile", fieldName: "tel"}, "section-blue mobile tel"],
|
||||
["shipping webauthn tel", {}, ""],
|
||||
|
||||
// Three tokens + WebAuthn credential type
|
||||
["invalid mobile tel webauthn", {}, ""],
|
||||
["section-blue shipping name webauthn", {section: "section-blue", addressType: "shipping", fieldName: "name", credentialType: "webauthn"}, "section-blue shipping name webauthn"],
|
||||
|
||||
// Four tokens
|
||||
["billing billing mobile tel", {}, ""],
|
||||
@ -107,19 +89,10 @@ var values = [
|
||||
["secti shipping work address-line1", {}, ""],
|
||||
["section-blue shipping home name", {}, ""],
|
||||
["section-blue shipping mobile tel", {section: "section-blue", addressType: "shipping", contactType: "mobile", fieldName: "tel"}, "section-blue shipping mobile tel"],
|
||||
["section-blue webauthn mobile tel", {}, ""],
|
||||
|
||||
// Four tokens + WebAuthn credential type
|
||||
["section-blue shipping home name webauthn", {}, ""],
|
||||
["section-blue shipping mobile tel webauthn", {section: "section-blue", addressType: "shipping", contactType: "mobile", fieldName: "tel", credentialType: "webauthn"}, "section-blue shipping mobile tel webauthn"],
|
||||
|
||||
// Five tokens (invalid)
|
||||
["billing billing billing mobile tel", {}, ""],
|
||||
["section-blue section-blue billing mobile tel", {}, ""],
|
||||
["section-blue section-blue billing webauthn tel", {}, ""],
|
||||
|
||||
// Five tokens + WebAuthn credential type (invalid)
|
||||
["billing billing billing mobile tel webauthn", {}, ""],
|
||||
];
|
||||
|
||||
var autocompleteInfoFieldIds = ["input", "select"];
|
||||
@ -170,8 +143,6 @@ function testAutocompleteInfoValue(aEnabled) {
|
||||
"Checking autocompleteInfo.contactType for " + field + ": " + test[0]);
|
||||
is(info.fieldName, "fieldName" in test[1] ? test[1].fieldName : "",
|
||||
"Checking autocompleteInfo.fieldName for " + field + ": " + test[0]);
|
||||
is(info.credentialType, "credentialType" in test[1] ? test[1].credentialType: "",
|
||||
"Checking autocompleteInfo.credentialType for " + field + ": " + test[0]);
|
||||
is(info.canAutomaticallyPersist, "canAutomaticallyPersist" in test[1] ? test[1].canAutomaticallyPersist : true,
|
||||
"Checking autocompleteInfo.canAutomaticallyPersist for " + field + ": " + test[0]);
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ AndroidWebAuthnService::GetIsUVPAA(bool* aAvailable) {
|
||||
|
||||
NS_IMETHODIMP
|
||||
AndroidWebAuthnService::MakeCredential(uint64_t aTransactionId,
|
||||
uint64_t aBrowsingContextId,
|
||||
uint64_t browsingContextId,
|
||||
nsIWebAuthnRegisterArgs* aArgs,
|
||||
nsIWebAuthnRegisterPromise* aPromise) {
|
||||
Reset();
|
||||
@ -201,18 +201,11 @@ AndroidWebAuthnService::MakeCredential(uint64_t aTransactionId,
|
||||
|
||||
NS_IMETHODIMP
|
||||
AndroidWebAuthnService::GetAssertion(uint64_t aTransactionId,
|
||||
uint64_t aBrowsingContextId,
|
||||
uint64_t browsingContextId,
|
||||
nsIWebAuthnSignArgs* aArgs,
|
||||
nsIWebAuthnSignPromise* aPromise) {
|
||||
Reset();
|
||||
|
||||
bool conditionallyMediated;
|
||||
Unused << aArgs->GetConditionallyMediated(&conditionallyMediated);
|
||||
if (conditionallyMediated) {
|
||||
aPromise->Reject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
|
||||
"java::WebAuthnTokenManager::WebAuthnGetAssertion",
|
||||
[self = RefPtr{this}, aArgs = RefPtr{aArgs},
|
||||
@ -306,8 +299,7 @@ AndroidWebAuthnService::GetAssertion(uint64_t aTransactionId,
|
||||
|
||||
NS_IMETHODIMP
|
||||
AndroidWebAuthnService::Reset() {
|
||||
mRegisterCredPropsRk.reset();
|
||||
|
||||
mRegisterCredPropsRk = Nothing();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -316,34 +308,6 @@ AndroidWebAuthnService::Cancel(uint64_t aTransactionId) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AndroidWebAuthnService::HasPendingConditionalGet(uint64_t aBrowsingContextId,
|
||||
const nsAString& aOrigin,
|
||||
uint64_t* aRv) {
|
||||
// Signal that there is no pending conditional get request, so the caller
|
||||
// will not attempt to call GetAutoFillEntries, SelectAutoFillEntry, or
|
||||
// ResumeConditionalGet (as these are not implemented).
|
||||
*aRv = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AndroidWebAuthnService::GetAutoFillEntries(
|
||||
uint64_t aTransactionId, nsTArray<RefPtr<nsIWebAuthnAutoFillEntry>>& aRv) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AndroidWebAuthnService::SelectAutoFillEntry(
|
||||
uint64_t aTransactionId, const nsTArray<uint8_t>& aCredentialId) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AndroidWebAuthnService::ResumeConditionalGet(uint64_t aTransactionId) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AndroidWebAuthnService::PinCallback(uint64_t aTransactionId,
|
||||
const nsACString& aPin) {
|
||||
|
@ -846,34 +846,6 @@ MacOSWebAuthnService::Cancel(uint64_t aTransactionId) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MacOSWebAuthnService::HasPendingConditionalGet(uint64_t aBrowsingContextId,
|
||||
const nsAString& aOrigin,
|
||||
uint64_t* aRv) {
|
||||
// Signal that there is no pending conditional get request, so the caller
|
||||
// will not attempt to call GetAutoFillEntries, SelectAutoFillEntry, or
|
||||
// ResumeConditionalGet (as these are not implemented).
|
||||
*aRv = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MacOSWebAuthnService::GetAutoFillEntries(
|
||||
uint64_t aTransactionId, nsTArray<RefPtr<nsIWebAuthnAutoFillEntry>>& aRv) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MacOSWebAuthnService::SelectAutoFillEntry(
|
||||
uint64_t aTransactionId, const nsTArray<uint8_t>& aCredentialId) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MacOSWebAuthnService::ResumeConditionalGet(uint64_t aTransactionId) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MacOSWebAuthnService::PinCallback(uint64_t aTransactionId,
|
||||
const nsACString& aPin) {
|
||||
|
@ -121,7 +121,6 @@ struct WebAuthnGetAssertionInfo {
|
||||
WebAuthnScopedCredential[] AllowList;
|
||||
WebAuthnExtension[] Extensions;
|
||||
nsString userVerificationRequirement;
|
||||
bool ConditionallyMediated;
|
||||
uint64_t BrowsingContextId;
|
||||
};
|
||||
|
||||
|
@ -138,12 +138,8 @@ already_AddRefed<Promise> PublicKeyCredential::IsConditionalMediationAvailable(
|
||||
if (aError.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
// Support for conditional mediation will be added in Bug 1838932
|
||||
promise->MaybeResolve(false);
|
||||
#else
|
||||
promise->MaybeResolve(
|
||||
StaticPrefs::security_webauthn_enable_conditional_mediation());
|
||||
#endif
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
|
@ -248,10 +248,4 @@ WebAuthnSignArgs::GetTimeoutMS(uint32_t* aTimeoutMS) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebAuthnSignArgs::GetConditionallyMediated(bool* aConditionallyMediated) {
|
||||
*aConditionallyMediated = mInfo.ConditionallyMediated();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
@ -1,35 +0,0 @@
|
||||
/* 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 "WebAuthnAutoFillEntry.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
NS_IMPL_ISUPPORTS(WebAuthnAutoFillEntry, nsIWebAuthnAutoFillEntry)
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebAuthnAutoFillEntry::GetProvider(uint8_t* aProvider) {
|
||||
*aProvider = mProvider;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebAuthnAutoFillEntry::GetUserName(nsAString& aUserName) {
|
||||
aUserName.Assign(mUserName);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebAuthnAutoFillEntry::GetRpId(nsAString& aRpId) {
|
||||
aRpId.Assign(mRpId);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebAuthnAutoFillEntry::GetCredentialId(nsTArray<uint8_t>& aCredentialId) {
|
||||
aCredentialId.Assign(mCredentialId);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace mozilla::dom
|
@ -1,51 +0,0 @@
|
||||
/* 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_WebAuthnAutoFillEntry_h_
|
||||
#define mozilla_dom_WebAuthnAutoFillEntry_h_
|
||||
|
||||
#include "nsIWebAuthnService.h"
|
||||
#include "nsString.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
# include <windows.h>
|
||||
# include "winwebauthn/webauthn.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
class WebAuthnAutoFillEntry final : public nsIWebAuthnAutoFillEntry {
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIWEBAUTHNAUTOFILLENTRY
|
||||
|
||||
WebAuthnAutoFillEntry(uint8_t aProvider, const nsAString& aUserName,
|
||||
const nsAString& aRpId,
|
||||
const nsTArray<uint8_t>& aCredentialId)
|
||||
: mProvider(aProvider), mUserName(aUserName), mRpId(aRpId) {
|
||||
mCredentialId.Assign(aCredentialId);
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
explicit WebAuthnAutoFillEntry(
|
||||
PCWEBAUTHN_CREDENTIAL_DETAILS aCredentialDetails) {
|
||||
mProvider = nsIWebAuthnAutoFillEntry::PROVIDER_PLATFORM_WINDOWS;
|
||||
mUserName.Assign(aCredentialDetails->pUserInformation->pwszName);
|
||||
mRpId.Assign(aCredentialDetails->pRpInformation->pwszId);
|
||||
mCredentialId.AppendElements(aCredentialDetails->pbCredentialID,
|
||||
aCredentialDetails->cbCredentialID);
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
~WebAuthnAutoFillEntry() = default;
|
||||
|
||||
uint8_t mProvider;
|
||||
nsString mUserName;
|
||||
nsString mRpId;
|
||||
nsTArray<uint8_t> mCredentialId;
|
||||
};
|
||||
|
||||
} // namespace mozilla::dom
|
||||
#endif // mozilla_dom_WebAuthnAutoFillEntry_h_
|
@ -486,7 +486,6 @@ const size_t MAX_ALLOWED_CREDENTIALS = 20;
|
||||
|
||||
already_AddRefed<Promise> WebAuthnManager::GetAssertion(
|
||||
const PublicKeyCredentialRequestOptions& aOptions,
|
||||
const bool aConditionallyMediated,
|
||||
const Optional<OwningNonNull<AbortSignal>>& aSignal, ErrorResult& aError) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
@ -672,7 +671,7 @@ already_AddRefed<Promise> WebAuthnManager::GetAssertion(
|
||||
WebAuthnGetAssertionInfo info(origin, NS_ConvertUTF8toUTF16(rpId), challenge,
|
||||
clientDataJSON, adjustedTimeout, allowList,
|
||||
extensions, aOptions.mUserVerification,
|
||||
aConditionallyMediated, context->Top()->Id());
|
||||
context->Top()->Id());
|
||||
|
||||
// Set up the transaction state (including event listeners, etc). Fallible
|
||||
// operations should not be performed below this line, as we must not leave
|
||||
|
@ -93,7 +93,6 @@ class WebAuthnManager final : public WebAuthnManagerBase, public AbortFollower {
|
||||
|
||||
already_AddRefed<Promise> GetAssertion(
|
||||
const PublicKeyCredentialRequestOptions& aOptions,
|
||||
const bool aConditionallyMediated,
|
||||
const Optional<OwningNonNull<AbortSignal>>& aSignal, ErrorResult& aError);
|
||||
|
||||
already_AddRefed<Promise> Store(const Credential& aCredential,
|
||||
|
@ -2,10 +2,7 @@
|
||||
* 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/Services.h"
|
||||
#include "mozilla/StaticPrefs_security.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "WebAuthnService.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
@ -35,35 +32,12 @@ WebAuthnService::GetAssertion(uint64_t aTransactionId,
|
||||
uint64_t browsingContextId,
|
||||
nsIWebAuthnSignArgs* aArgs,
|
||||
nsIWebAuthnSignPromise* aPromise) {
|
||||
nsresult rv;
|
||||
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
|
||||
rv = mTestService->GetAssertion(aTransactionId, browsingContextId, aArgs,
|
||||
aPromise);
|
||||
} else {
|
||||
rv = mPlatformService->GetAssertion(aTransactionId, browsingContextId,
|
||||
return mTestService->GetAssertion(aTransactionId, browsingContextId, aArgs,
|
||||
aPromise);
|
||||
}
|
||||
return mPlatformService->GetAssertion(aTransactionId, browsingContextId,
|
||||
aArgs, aPromise);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// If this is a conditionally mediated request, notify observers that there
|
||||
// is a pending transaction. This is mainly useful in tests.
|
||||
bool conditionallyMediated;
|
||||
Unused << aArgs->GetConditionallyMediated(&conditionallyMediated);
|
||||
if (conditionallyMediated) {
|
||||
nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(__func__, []() {
|
||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||
if (os) {
|
||||
os->NotifyObservers(nullptr, "webauthn:conditional-get-pending",
|
||||
nullptr);
|
||||
}
|
||||
}));
|
||||
NS_DispatchToMainThread(runnable.forget());
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -74,44 +48,6 @@ WebAuthnService::GetIsUVPAA(bool* aAvailable) {
|
||||
return mPlatformService->GetIsUVPAA(aAvailable);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebAuthnService::HasPendingConditionalGet(uint64_t aBrowsingContextId,
|
||||
const nsAString& aOrigin,
|
||||
uint64_t* aRv) {
|
||||
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
|
||||
return mTestService->HasPendingConditionalGet(aBrowsingContextId, aOrigin,
|
||||
aRv);
|
||||
}
|
||||
return mPlatformService->HasPendingConditionalGet(aBrowsingContextId, aOrigin,
|
||||
aRv);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebAuthnService::GetAutoFillEntries(
|
||||
uint64_t aTransactionId, nsTArray<RefPtr<nsIWebAuthnAutoFillEntry>>& aRv) {
|
||||
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
|
||||
return mTestService->GetAutoFillEntries(aTransactionId, aRv);
|
||||
}
|
||||
return mPlatformService->GetAutoFillEntries(aTransactionId, aRv);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebAuthnService::SelectAutoFillEntry(uint64_t aTransactionId,
|
||||
const nsTArray<uint8_t>& aCredentialId) {
|
||||
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
|
||||
return mTestService->SelectAutoFillEntry(aTransactionId, aCredentialId);
|
||||
}
|
||||
return mPlatformService->SelectAutoFillEntry(aTransactionId, aCredentialId);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebAuthnService::ResumeConditionalGet(uint64_t aTransactionId) {
|
||||
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
|
||||
return mTestService->ResumeConditionalGet(aTransactionId);
|
||||
}
|
||||
return mPlatformService->ResumeConditionalGet(aTransactionId);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebAuthnService::Reset() {
|
||||
if (StaticPrefs::security_webauth_webauthn_enable_softtoken()) {
|
||||
|
@ -44,10 +44,6 @@ static decltype(WebAuthNCancelCurrentOperation)*
|
||||
static decltype(WebAuthNGetErrorName)* gWinWebauthnGetErrorName = nullptr;
|
||||
static decltype(WebAuthNGetApiVersionNumber)* gWinWebauthnGetApiVersionNumber =
|
||||
nullptr;
|
||||
static decltype(WebAuthNGetPlatformCredentialList)*
|
||||
gWinWebauthnGetPlatformCredentialList = nullptr;
|
||||
static decltype(WebAuthNFreePlatformCredentialList)*
|
||||
gWinWebauthnFreePlatformCredentialList = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -62,7 +58,7 @@ NS_IMPL_ISUPPORTS(WinWebAuthnService, nsIWebAuthnService)
|
||||
/* static */
|
||||
nsresult WinWebAuthnService::EnsureWinWebAuthnModuleLoaded() {
|
||||
{
|
||||
StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
|
||||
StaticAutoReadLock lock(gWinWebAuthnModuleLock);
|
||||
if (gWinWebAuthnModule) {
|
||||
// The module is already loaded.
|
||||
return NS_OK;
|
||||
@ -135,21 +131,6 @@ nsresult WinWebAuthnService::EnsureWinWebAuthnModuleLoaded() {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (gWinWebauthnGetApiVersionNumber() >= WEBAUTHN_API_VERSION_4) {
|
||||
gWinWebauthnGetPlatformCredentialList =
|
||||
reinterpret_cast<decltype(WebAuthNGetPlatformCredentialList)*>(
|
||||
GetProcAddress(gWinWebAuthnModule,
|
||||
"WebAuthNGetPlatformCredentialList"));
|
||||
gWinWebauthnFreePlatformCredentialList =
|
||||
reinterpret_cast<decltype(WebAuthNFreePlatformCredentialList)*>(
|
||||
GetProcAddress(gWinWebAuthnModule,
|
||||
"WebAuthNFreePlatformCredentialList"));
|
||||
if (!(gWinWebauthnGetPlatformCredentialList &&
|
||||
gWinWebauthnFreePlatformCredentialList)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
}
|
||||
|
||||
markModuleUnusable.release();
|
||||
return NS_OK;
|
||||
}
|
||||
@ -167,7 +148,7 @@ bool WinWebAuthnService::AreWebAuthNApisAvailable() {
|
||||
nsresult rv = EnsureWinWebAuthnModuleLoaded();
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
|
||||
StaticAutoReadLock lock(gWinWebAuthnModuleLock);
|
||||
return gWinWebAuthnModule &&
|
||||
gWinWebauthnGetApiVersionNumber() >= kMinWinWebAuthNApiVersion;
|
||||
}
|
||||
@ -179,7 +160,7 @@ WinWebAuthnService::GetIsUVPAA(bool* aAvailable) {
|
||||
|
||||
if (WinWebAuthnService::AreWebAuthNApisAvailable()) {
|
||||
BOOL isUVPAA = FALSE;
|
||||
StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
|
||||
StaticAutoReadLock lock(gWinWebAuthnModuleLock);
|
||||
*aAvailable = gWinWebAuthnModule && gWinWebauthnIsUVPAA(&isUVPAA) == S_OK &&
|
||||
isUVPAA == TRUE;
|
||||
} else {
|
||||
@ -197,20 +178,12 @@ NS_IMETHODIMP
|
||||
WinWebAuthnService::Reset() {
|
||||
// Reset will never be the first function to use gWinWebAuthnModule, so
|
||||
// we shouldn't try to initialize it here.
|
||||
auto guard = mTransactionState.Lock();
|
||||
if (guard->isSome()) {
|
||||
StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
|
||||
if (mTransactionId.isSome()) {
|
||||
StaticAutoReadLock lock(gWinWebAuthnModuleLock);
|
||||
if (gWinWebAuthnModule) {
|
||||
const GUID cancellationId = guard->ref().cancellationId;
|
||||
gWinWebauthnCancelCurrentOperation(&cancellationId);
|
||||
gWinWebauthnCancelCurrentOperation(&mCancellationId);
|
||||
}
|
||||
if (guard->ref().pendingSignPromise.isSome()) {
|
||||
// This request was never dispatched to the platform API, so
|
||||
// we need to reject the promise ourselves.
|
||||
guard->ref().pendingSignPromise.ref()->Reject(
|
||||
NS_ERROR_DOM_NOT_ALLOWED_ERR);
|
||||
}
|
||||
guard->reset();
|
||||
mTransactionId.reset();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -218,37 +191,31 @@ WinWebAuthnService::Reset() {
|
||||
|
||||
NS_IMETHODIMP
|
||||
WinWebAuthnService::MakeCredential(uint64_t aTransactionId,
|
||||
uint64_t aBrowsingContextId,
|
||||
uint64_t browsingContextId,
|
||||
nsIWebAuthnRegisterArgs* aArgs,
|
||||
nsIWebAuthnRegisterPromise* aPromise) {
|
||||
nsresult rv = EnsureWinWebAuthnModuleLoaded();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
Reset();
|
||||
auto guard = mTransactionState.Lock();
|
||||
StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
|
||||
GUID cancellationId;
|
||||
if (gWinWebauthnGetCancellationId(&cancellationId) != S_OK) {
|
||||
// caller will reject promise
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
mTransactionId = Some(aTransactionId);
|
||||
{
|
||||
StaticAutoReadLock lock(gWinWebAuthnModuleLock);
|
||||
if (gWinWebauthnGetCancellationId(&mCancellationId) != S_OK) {
|
||||
// caller will reject promise
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
}
|
||||
*guard = Some(TransactionState{
|
||||
aTransactionId,
|
||||
aBrowsingContextId,
|
||||
Nothing(),
|
||||
Nothing(),
|
||||
cancellationId,
|
||||
});
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
|
||||
"WinWebAuthnService::MakeCredential",
|
||||
[self = RefPtr{this}, aArgs = RefPtr{aArgs}, aPromise = RefPtr{aPromise},
|
||||
cancellationId]() mutable {
|
||||
aCancellationId = mCancellationId]() mutable {
|
||||
// Take a read lock on gWinWebAuthnModuleLock to prevent the module from
|
||||
// being unloaded while the operation is in progress. This does not
|
||||
// prevent the operation from being cancelled, so it does not block a
|
||||
// clean shutdown.
|
||||
StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
|
||||
StaticAutoReadLock lock(gWinWebAuthnModuleLock);
|
||||
if (!gWinWebAuthnModule) {
|
||||
aPromise->Reject(NS_ERROR_DOM_UNKNOWN_ERR);
|
||||
return;
|
||||
@ -516,8 +483,8 @@ WinWebAuthnService::MakeCredential(uint64_t aTransactionId,
|
||||
winRequireResidentKey,
|
||||
winUserVerificationReq,
|
||||
winAttestation,
|
||||
0, // Flags
|
||||
&cancellationId, // CancellationId
|
||||
0, // Flags
|
||||
&aCancellationId, // CancellationId
|
||||
pExcludeCredentialList,
|
||||
WEBAUTHN_ENTERPRISE_ATTESTATION_NONE,
|
||||
WEBAUTHN_LARGE_BLOB_SUPPORT_NONE,
|
||||
@ -595,65 +562,31 @@ WinWebAuthnService::MakeCredential(uint64_t aTransactionId,
|
||||
|
||||
NS_IMETHODIMP
|
||||
WinWebAuthnService::GetAssertion(uint64_t aTransactionId,
|
||||
uint64_t aBrowsingContextId,
|
||||
uint64_t browsingContextId,
|
||||
nsIWebAuthnSignArgs* aArgs,
|
||||
nsIWebAuthnSignPromise* aPromise) {
|
||||
nsresult rv = EnsureWinWebAuthnModuleLoaded();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
Reset();
|
||||
|
||||
auto guard = mTransactionState.Lock();
|
||||
|
||||
GUID cancellationId;
|
||||
mTransactionId = Some(aTransactionId);
|
||||
{
|
||||
StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
|
||||
if (gWinWebauthnGetCancellationId(&cancellationId) != S_OK) {
|
||||
StaticAutoReadLock lock(gWinWebAuthnModuleLock);
|
||||
if (gWinWebauthnGetCancellationId(&mCancellationId) != S_OK) {
|
||||
// caller will reject promise
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
*guard = Some(TransactionState{
|
||||
aTransactionId,
|
||||
aBrowsingContextId,
|
||||
Some(RefPtr{aArgs}),
|
||||
Some(RefPtr{aPromise}),
|
||||
cancellationId,
|
||||
});
|
||||
|
||||
bool conditionallyMediated;
|
||||
Unused << aArgs->GetConditionallyMediated(&conditionallyMediated);
|
||||
if (!conditionallyMediated) {
|
||||
DoGetAssertion(Nothing(), guard);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void WinWebAuthnService::DoGetAssertion(
|
||||
Maybe<nsTArray<uint8_t>>&& aSelectedCredentialId,
|
||||
const TransactionStateMutex::AutoLock& aGuard) {
|
||||
if (aGuard->isNothing() || aGuard->ref().pendingSignArgs.isNothing() ||
|
||||
aGuard->ref().pendingSignPromise.isNothing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Take the pending Args and Promise to prevent repeated calls to
|
||||
// DoGetAssertion for this transaction.
|
||||
RefPtr<nsIWebAuthnSignArgs> aArgs = aGuard->ref().pendingSignArgs.extract();
|
||||
RefPtr<nsIWebAuthnSignPromise> aPromise =
|
||||
aGuard->ref().pendingSignPromise.extract();
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
|
||||
"WinWebAuthnService::MakeCredential",
|
||||
[self = RefPtr{this}, aArgs, aPromise,
|
||||
aSelectedCredentialId = std::move(aSelectedCredentialId),
|
||||
aCancellationId = aGuard->ref().cancellationId]() mutable {
|
||||
[self = RefPtr{this}, aArgs = RefPtr{aArgs}, aPromise = RefPtr{aPromise},
|
||||
aCancellationId = mCancellationId]() mutable {
|
||||
// Take a read lock on gWinWebAuthnModuleLock to prevent the module from
|
||||
// being unloaded while the operation is in progress. This does not
|
||||
// prevent the operation from being cancelled, so it does not block a
|
||||
// clean shutdown.
|
||||
StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
|
||||
StaticAutoReadLock lock(gWinWebAuthnModuleLock);
|
||||
if (!gWinWebAuthnModule) {
|
||||
aPromise->Reject(NS_ERROR_DOM_UNKNOWN_ERR);
|
||||
return;
|
||||
@ -715,15 +648,10 @@ void WinWebAuthnService::DoGetAssertion(
|
||||
|
||||
// allow Credentials
|
||||
nsTArray<nsTArray<uint8_t>> allowList;
|
||||
Unused << aArgs->GetAllowList(allowList);
|
||||
|
||||
nsTArray<uint8_t> allowListTransports;
|
||||
if (aSelectedCredentialId.isSome()) {
|
||||
allowList.AppendElement(aSelectedCredentialId.extract());
|
||||
allowListTransports.AppendElement(
|
||||
MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_INTERNAL);
|
||||
} else {
|
||||
Unused << aArgs->GetAllowList(allowList);
|
||||
Unused << aArgs->GetAllowListTransports(allowListTransports);
|
||||
}
|
||||
Unused << aArgs->GetAllowListTransports(allowListTransports);
|
||||
|
||||
if (allowList.Length() != allowListTransports.Length()) {
|
||||
aPromise->Reject(NS_ERROR_DOM_UNKNOWN_ERR);
|
||||
@ -838,115 +766,6 @@ void WinWebAuthnService::DoGetAssertion(
|
||||
}));
|
||||
|
||||
NS_DispatchBackgroundTask(runnable, NS_DISPATCH_EVENT_MAY_BLOCK);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WinWebAuthnService::HasPendingConditionalGet(uint64_t aBrowsingContextId,
|
||||
const nsAString& aOrigin,
|
||||
uint64_t* aRv) {
|
||||
auto guard = mTransactionState.Lock();
|
||||
if (guard->isNothing() ||
|
||||
guard->ref().browsingContextId != aBrowsingContextId ||
|
||||
guard->ref().pendingSignArgs.isNothing()) {
|
||||
*aRv = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsString origin;
|
||||
Unused << guard->ref().pendingSignArgs.ref()->GetOrigin(origin);
|
||||
if (origin != aOrigin) {
|
||||
*aRv = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
*aRv = guard->ref().transactionId;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WinWebAuthnService::GetAutoFillEntries(
|
||||
uint64_t aTransactionId, nsTArray<RefPtr<nsIWebAuthnAutoFillEntry>>& aRv) {
|
||||
auto guard = mTransactionState.Lock();
|
||||
if (guard->isNothing() || guard->ref().transactionId != aTransactionId ||
|
||||
guard->ref().pendingSignArgs.isNothing()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
StaticAutoReadLock moduleLock(gWinWebAuthnModuleLock);
|
||||
if (!gWinWebAuthnModule) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
aRv.Clear();
|
||||
|
||||
if (gWinWebauthnGetApiVersionNumber() < WEBAUTHN_API_VERSION_4) {
|
||||
// GetPlatformCredentialList was added in version 4. Earlier versions
|
||||
// can still present a generic "Use a Passkey" autofill entry, so
|
||||
// this isn't an error.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsString rpId;
|
||||
Unused << guard->ref().pendingSignArgs.ref()->GetRpId(rpId);
|
||||
|
||||
WEBAUTHN_GET_CREDENTIALS_OPTIONS getCredentialsOptions{
|
||||
WEBAUTHN_GET_CREDENTIALS_OPTIONS_VERSION_1,
|
||||
rpId.get(), // pwszRpId
|
||||
FALSE, // bBrowserInPrivateMode
|
||||
};
|
||||
PWEBAUTHN_CREDENTIAL_DETAILS_LIST pCredentialList = nullptr;
|
||||
HRESULT hr = gWinWebauthnGetPlatformCredentialList(&getCredentialsOptions,
|
||||
&pCredentialList);
|
||||
// WebAuthNGetPlatformCredentialList has an _Outptr_result_maybenull_
|
||||
// annotation and a comment "Returns NTE_NOT_FOUND when credentials are
|
||||
// not found."
|
||||
if (pCredentialList == nullptr) {
|
||||
if (hr != NTE_NOT_FOUND) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
MOZ_ASSERT(hr == S_OK);
|
||||
for (size_t i = 0; i < pCredentialList->cCredentialDetails; i++) {
|
||||
RefPtr<nsIWebAuthnAutoFillEntry> entry(
|
||||
new WebAuthnAutoFillEntry(pCredentialList->ppCredentialDetails[i]));
|
||||
aRv.AppendElement(entry);
|
||||
}
|
||||
gWinWebauthnFreePlatformCredentialList(pCredentialList);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WinWebAuthnService::SelectAutoFillEntry(
|
||||
uint64_t aTransactionId, const nsTArray<uint8_t>& aCredentialId) {
|
||||
auto guard = mTransactionState.Lock();
|
||||
if (guard->isNothing() || guard->ref().transactionId != aTransactionId ||
|
||||
guard->ref().pendingSignArgs.isNothing()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsTArray<nsTArray<uint8_t>> allowList;
|
||||
Unused << guard->ref().pendingSignArgs.ref()->GetAllowList(allowList);
|
||||
if (!allowList.IsEmpty() && !allowList.Contains(aCredentialId)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
Maybe<nsTArray<uint8_t>> id;
|
||||
id.emplace();
|
||||
id.ref().Assign(aCredentialId);
|
||||
DoGetAssertion(std::move(id), guard);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WinWebAuthnService::ResumeConditionalGet(uint64_t aTransactionId) {
|
||||
auto guard = mTransactionState.Lock();
|
||||
if (guard->isNothing() || guard->ref().transactionId != aTransactionId ||
|
||||
guard->ref().pendingSignArgs.isNothing()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
DoGetAssertion(Nothing(), guard);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -22,26 +22,15 @@ class WinWebAuthnService final : public nsIWebAuthnService {
|
||||
static bool AreWebAuthNApisAvailable();
|
||||
static nsresult EnsureWinWebAuthnModuleLoaded();
|
||||
|
||||
WinWebAuthnService()
|
||||
: mTransactionState(Nothing(), "WinWebAuthnService::mTransactionState"){};
|
||||
WinWebAuthnService() = default;
|
||||
|
||||
private:
|
||||
~WinWebAuthnService();
|
||||
|
||||
uint32_t GetWebAuthNApiVersion();
|
||||
|
||||
struct TransactionState {
|
||||
uint64_t transactionId;
|
||||
uint64_t browsingContextId;
|
||||
Maybe<RefPtr<nsIWebAuthnSignArgs>> pendingSignArgs;
|
||||
Maybe<RefPtr<nsIWebAuthnSignPromise>> pendingSignPromise;
|
||||
GUID cancellationId;
|
||||
};
|
||||
|
||||
using TransactionStateMutex = DataMutex<Maybe<TransactionState>>;
|
||||
TransactionStateMutex mTransactionState;
|
||||
void DoGetAssertion(Maybe<nsTArray<uint8_t>>&& aSelectedCredentialId,
|
||||
const TransactionStateMutex::AutoLock& aGuard);
|
||||
Maybe<uint64_t> mTransactionId;
|
||||
GUID mCancellationId;
|
||||
};
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
@ -37,12 +37,12 @@ use serde_cbor;
|
||||
use serde_json::json;
|
||||
use std::fmt::Write;
|
||||
use std::sync::mpsc::{channel, Receiver, RecvError, Sender};
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use thin_vec::{thin_vec, ThinVec};
|
||||
use xpcom::interfaces::{
|
||||
nsICredentialParameters, nsIObserverService, nsIWebAuthnAttObj, nsIWebAuthnAutoFillEntry,
|
||||
nsIWebAuthnRegisterArgs, nsIWebAuthnRegisterPromise, nsIWebAuthnRegisterResult,
|
||||
nsIWebAuthnService, nsIWebAuthnSignArgs, nsIWebAuthnSignPromise, nsIWebAuthnSignResult,
|
||||
nsICredentialParameters, nsIObserverService, nsIWebAuthnAttObj, nsIWebAuthnRegisterArgs,
|
||||
nsIWebAuthnRegisterPromise, nsIWebAuthnRegisterResult, nsIWebAuthnService, nsIWebAuthnSignArgs,
|
||||
nsIWebAuthnSignPromise, nsIWebAuthnSignResult,
|
||||
};
|
||||
use xpcom::{xpcom_method, RefPtr};
|
||||
mod about_webauthn_controller;
|
||||
@ -542,7 +542,8 @@ impl TransactionPromise {
|
||||
|
||||
enum TransactionArgs {
|
||||
Register(/* timeout */ u64, RegisterArgs),
|
||||
Sign(/* timeout */ u64, SignArgs),
|
||||
// Bug 1838932 - we'll need to cache SignArgs once we support conditional mediation
|
||||
// Sign(/* timeout */ u64, SignArgs),
|
||||
}
|
||||
|
||||
struct TransactionState {
|
||||
@ -763,9 +764,9 @@ impl AuthrsService {
|
||||
browsing_context_id,
|
||||
pending_args: Some(TransactionArgs::Register(timeout_ms as u64, info)),
|
||||
promise: TransactionPromise::Register(promise),
|
||||
interactive_receiver: None,
|
||||
pin_receiver: None,
|
||||
selection_receiver: None,
|
||||
interactive_receiver: None,
|
||||
puat_cache: None,
|
||||
});
|
||||
|
||||
@ -915,8 +916,8 @@ impl AuthrsService {
|
||||
|
||||
let mut allow_list = ThinVec::new();
|
||||
unsafe { args.GetAllowList(&mut allow_list) }.to_result()?;
|
||||
let allow_list = allow_list
|
||||
.iter()
|
||||
let allow_list: Vec<_> = allow_list
|
||||
.iter_mut()
|
||||
.map(|id| PublicKeyCredentialDescriptor {
|
||||
id: id.to_vec(),
|
||||
transports: vec![],
|
||||
@ -940,79 +941,9 @@ impl AuthrsService {
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let mut conditionally_mediated = false;
|
||||
unsafe { args.GetConditionallyMediated(&mut conditionally_mediated) }.to_result()?;
|
||||
|
||||
let info = SignArgs {
|
||||
client_data_hash: client_data_hash_arr,
|
||||
relying_party_id: relying_party_id.to_string(),
|
||||
origin: origin.to_string(),
|
||||
allow_list,
|
||||
user_verification_req,
|
||||
user_presence_req: true,
|
||||
extensions: AuthenticationExtensionsClientInputs {
|
||||
app_id,
|
||||
..Default::default()
|
||||
},
|
||||
pin: None,
|
||||
use_ctap1_fallback: !static_prefs::pref!("security.webauthn.ctap2"),
|
||||
};
|
||||
|
||||
let mut guard = self.transaction.lock().unwrap();
|
||||
*guard = Some(TransactionState {
|
||||
tid,
|
||||
browsing_context_id,
|
||||
pending_args: Some(TransactionArgs::Sign(timeout_ms as u64, info)),
|
||||
promise: TransactionPromise::Sign(promise),
|
||||
pin_receiver: None,
|
||||
selection_receiver: None,
|
||||
interactive_receiver: None,
|
||||
puat_cache: None,
|
||||
});
|
||||
|
||||
if !conditionally_mediated {
|
||||
// Immediately proceed to the modal UI flow.
|
||||
self.do_get_assertion(None, guard)
|
||||
} else {
|
||||
// Cache the request and wait for the conditional UI to request autofill entries, etc.
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn do_get_assertion(
|
||||
&self,
|
||||
mut selected_credential_id: Option<Vec<u8>>,
|
||||
mut guard: MutexGuard<Option<TransactionState>>,
|
||||
) -> Result<(), nsresult> {
|
||||
let Some(state) = guard.as_mut() else {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
};
|
||||
let browsing_context_id = state.browsing_context_id;
|
||||
let tid = state.tid;
|
||||
let (timeout_ms, mut info) = match state.pending_args.take() {
|
||||
Some(TransactionArgs::Sign(timeout_ms, info)) => (timeout_ms, info),
|
||||
_ => return Err(NS_ERROR_FAILURE),
|
||||
};
|
||||
|
||||
if let Some(id) = selected_credential_id.take() {
|
||||
if info.allow_list.is_empty() {
|
||||
info.allow_list.push(PublicKeyCredentialDescriptor {
|
||||
id,
|
||||
transports: vec![],
|
||||
});
|
||||
} else {
|
||||
// We need to ensure that the selected credential id
|
||||
// was in the original allow_list.
|
||||
info.allow_list.retain(|cred| cred.id == id);
|
||||
if info.allow_list.is_empty() {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (status_tx, status_rx) = channel::<StatusUpdate>();
|
||||
let status_transaction = self.transaction.clone();
|
||||
let status_origin = info.origin.to_string();
|
||||
let status_origin = origin.to_string();
|
||||
RunnableBuilder::new("AuthrsService::GetAssertion::StatusReceiver", move || {
|
||||
let _ = status_callback(
|
||||
status_rx,
|
||||
@ -1025,8 +956,8 @@ impl AuthrsService {
|
||||
.may_block(true)
|
||||
.dispatch_background_task()?;
|
||||
|
||||
let uniq_allowed_cred = if info.allow_list.len() == 1 {
|
||||
info.allow_list.first().cloned()
|
||||
let uniq_allowed_cred = if allow_list.len() == 1 {
|
||||
allow_list.first().cloned()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@ -1062,6 +993,21 @@ impl AuthrsService {
|
||||
}),
|
||||
);
|
||||
|
||||
let info = SignArgs {
|
||||
client_data_hash: client_data_hash_arr,
|
||||
relying_party_id: relying_party_id.to_string(),
|
||||
origin: origin.to_string(),
|
||||
allow_list,
|
||||
user_verification_req,
|
||||
user_presence_req: true,
|
||||
extensions: AuthenticationExtensionsClientInputs {
|
||||
app_id,
|
||||
..Default::default()
|
||||
},
|
||||
pin: None,
|
||||
use_ctap1_fallback: !static_prefs::pref!("security.webauthn.ctap2"),
|
||||
};
|
||||
|
||||
// TODO(Bug 1855290) Remove this presence prompt
|
||||
send_prompt(
|
||||
BrowserPromptType::Presence,
|
||||
@ -1070,6 +1016,17 @@ impl AuthrsService {
|
||||
Some(browsing_context_id),
|
||||
)?;
|
||||
|
||||
*self.transaction.lock().unwrap() = Some(TransactionState {
|
||||
tid,
|
||||
browsing_context_id,
|
||||
pending_args: None,
|
||||
promise: TransactionPromise::Sign(promise),
|
||||
interactive_receiver: None,
|
||||
pin_receiver: None,
|
||||
selection_receiver: None,
|
||||
puat_cache: None,
|
||||
});
|
||||
|
||||
// As in `register`, we are intentionally avoiding `AuthenticatorService` here.
|
||||
if static_prefs::pref!("security.webauth.webauthn_enable_usbtoken") {
|
||||
self.usb_token_manager.lock().unwrap().sign(
|
||||
@ -1088,79 +1045,6 @@ impl AuthrsService {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
xpcom_method!(has_pending_conditional_get => HasPendingConditionalGet(aBrowsingContextId: u64, aOrigin: *const nsAString) -> u64);
|
||||
fn has_pending_conditional_get(
|
||||
&self,
|
||||
browsing_context_id: u64,
|
||||
origin: &nsAString,
|
||||
) -> Result<u64, nsresult> {
|
||||
let mut guard = self.transaction.lock().unwrap();
|
||||
let Some(state) = guard.as_mut() else {
|
||||
return Ok(0);
|
||||
};
|
||||
let Some(TransactionArgs::Sign(_, info)) = state.pending_args.as_ref() else {
|
||||
return Ok(0);
|
||||
};
|
||||
if state.browsing_context_id != browsing_context_id {
|
||||
return Ok(0);
|
||||
}
|
||||
if !info.origin.eq(&origin.to_string()) {
|
||||
return Ok(0);
|
||||
}
|
||||
Ok(state.tid)
|
||||
}
|
||||
|
||||
xpcom_method!(get_autofill_entries => GetAutoFillEntries(aTransactionId: u64) -> ThinVec<Option<RefPtr<nsIWebAuthnAutoFillEntry>>>);
|
||||
fn get_autofill_entries(
|
||||
&self,
|
||||
tid: u64,
|
||||
) -> Result<ThinVec<Option<RefPtr<nsIWebAuthnAutoFillEntry>>>, nsresult> {
|
||||
let mut guard = self.transaction.lock().unwrap();
|
||||
let Some(state) = guard.as_mut() else {
|
||||
return Err(NS_ERROR_NOT_AVAILABLE);
|
||||
};
|
||||
if state.tid != tid {
|
||||
return Err(NS_ERROR_NOT_AVAILABLE);
|
||||
}
|
||||
let Some(TransactionArgs::Sign(_, info)) = state.pending_args.as_ref() else {
|
||||
return Err(NS_ERROR_NOT_AVAILABLE);
|
||||
};
|
||||
if static_prefs::pref!("security.webauth.webauthn_enable_usbtoken") {
|
||||
// We don't currently support silent discovery for credentials on USB tokens.
|
||||
return Ok(thin_vec![]);
|
||||
} else if static_prefs::pref!("security.webauth.webauthn_enable_softtoken") {
|
||||
return self
|
||||
.test_token_manager
|
||||
.get_autofill_entries(&info.relying_party_id, &info.allow_list);
|
||||
} else {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
xpcom_method!(select_autofill_entry => SelectAutoFillEntry(aTid: u64, aCredentialId: *const ThinVec<u8>));
|
||||
fn select_autofill_entry(&self, tid: u64, credential_id: &ThinVec<u8>) -> Result<(), nsresult> {
|
||||
let mut guard = self.transaction.lock().unwrap();
|
||||
let Some(state) = guard.as_mut() else {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
};
|
||||
if tid != state.tid {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
self.do_get_assertion(Some(credential_id.to_vec()), guard)
|
||||
}
|
||||
|
||||
xpcom_method!(resume_conditional_get => ResumeConditionalGet(aTid: u64));
|
||||
fn resume_conditional_get(&self, tid: u64) -> Result<(), nsresult> {
|
||||
let mut guard = self.transaction.lock().unwrap();
|
||||
let Some(state) = guard.as_mut() else {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
};
|
||||
if tid != state.tid {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
self.do_get_assertion(None, guard)
|
||||
}
|
||||
|
||||
xpcom_method!(cancel => Cancel(aTransactionId: u64));
|
||||
fn cancel(&self, tid: u64) -> Result<(), nsresult> {
|
||||
{
|
||||
@ -1335,9 +1219,9 @@ impl AuthrsService {
|
||||
browsing_context_id: 0,
|
||||
pending_args: None,
|
||||
promise: TransactionPromise::Listen,
|
||||
interactive_receiver: None,
|
||||
pin_receiver: None,
|
||||
selection_receiver: None,
|
||||
interactive_receiver: None,
|
||||
puat_cache: None,
|
||||
});
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ use authenticator::{RegisterResult, SignResult, StatusUpdate};
|
||||
use base64::Engine;
|
||||
use moz_task::RunnableBuilder;
|
||||
use nserror::{nsresult, NS_ERROR_FAILURE, NS_ERROR_INVALID_ARG, NS_OK};
|
||||
use nsstring::{nsACString, nsAString, nsCString, nsString};
|
||||
use nsstring::{nsACString, nsCString};
|
||||
use rand::{thread_rng, RngCore};
|
||||
use std::cell::{Ref, RefCell};
|
||||
use std::collections::{hash_map::Entry, HashMap};
|
||||
@ -42,7 +42,7 @@ use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use thin_vec::ThinVec;
|
||||
use xpcom::interfaces::{nsICredentialParameters, nsIWebAuthnAutoFillEntry};
|
||||
use xpcom::interfaces::nsICredentialParameters;
|
||||
use xpcom::{xpcom_method, RefPtr};
|
||||
|
||||
// All TestTokens use this fixed, randomly generated, AAGUID
|
||||
@ -128,8 +128,8 @@ impl TestToken {
|
||||
thread_rng().fill_bytes(&mut pin_token);
|
||||
Self {
|
||||
protocol: FidoProtocol::CTAP2,
|
||||
transport,
|
||||
versions,
|
||||
transport,
|
||||
has_resident_key,
|
||||
has_user_verification,
|
||||
is_user_consenting,
|
||||
@ -671,34 +671,6 @@ impl CredentialParameters {
|
||||
}
|
||||
}
|
||||
|
||||
#[xpcom(implement(nsIWebAuthnAutoFillEntry), atomic)]
|
||||
struct WebAuthnAutoFillEntry {
|
||||
rp: String,
|
||||
credential_id: Vec<u8>,
|
||||
}
|
||||
|
||||
impl WebAuthnAutoFillEntry {
|
||||
xpcom_method!(get_provider => GetProvider() -> u8);
|
||||
fn get_provider(&self) -> Result<u8, nsresult> {
|
||||
Ok(nsIWebAuthnAutoFillEntry::PROVIDER_TEST_TOKEN)
|
||||
}
|
||||
|
||||
xpcom_method!(get_user_name => GetUserName() -> nsAString);
|
||||
fn get_user_name(&self) -> Result<nsString, nsresult> {
|
||||
Ok(nsString::from("Test User"))
|
||||
}
|
||||
|
||||
xpcom_method!(get_rp_id => GetRpId() -> nsAString);
|
||||
fn get_rp_id(&self) -> Result<nsString, nsresult> {
|
||||
Ok(nsString::from(&self.rp))
|
||||
}
|
||||
|
||||
xpcom_method!(get_credential_id => GetCredentialId() -> ThinVec<u8>);
|
||||
fn get_credential_id(&self) -> Result<ThinVec<u8>, nsresult> {
|
||||
Ok(self.credential_id.as_slice().into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct TestTokenManager {
|
||||
state: Arc<Mutex<HashMap<u64, TestToken>>>,
|
||||
@ -930,45 +902,4 @@ impl TestTokenManager {
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn get_autofill_entries(
|
||||
&self,
|
||||
rp_id: &str,
|
||||
credential_filter: &Vec<PublicKeyCredentialDescriptor>,
|
||||
) -> Result<ThinVec<Option<RefPtr<nsIWebAuthnAutoFillEntry>>>, nsresult> {
|
||||
let guard = self.state.lock().map_err(|_| NS_ERROR_FAILURE)?;
|
||||
let mut entries = ThinVec::new();
|
||||
|
||||
for token in guard.values() {
|
||||
let credentials = token.get_credentials();
|
||||
for credential in credentials.deref() {
|
||||
// The relying party ID must match.
|
||||
if !rp_id.eq(&credential.rp.id) {
|
||||
continue;
|
||||
}
|
||||
// Only discoverable credentials are admissible.
|
||||
if !credential.is_discoverable_credential {
|
||||
continue;
|
||||
}
|
||||
// Only credentials listed in the credential filter (if it is
|
||||
// non-empty) are admissible.
|
||||
if credential_filter.len() > 0
|
||||
&& credential_filter
|
||||
.iter()
|
||||
.find(|cred| cred.id == credential.id)
|
||||
.is_none()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let entry = WebAuthnAutoFillEntry::allocate(InitWebAuthnAutoFillEntry {
|
||||
rp: credential.rp.id.clone(),
|
||||
credential_id: credential.id.clone(),
|
||||
})
|
||||
.query_interface::<nsIWebAuthnAutoFillEntry>()
|
||||
.ok_or(NS_ERROR_FAILURE)?;
|
||||
entries.push(Some(entry));
|
||||
}
|
||||
}
|
||||
Ok(entries)
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,6 @@ UNIFIED_SOURCES += [
|
||||
"AuthenticatorResponse.cpp",
|
||||
"PublicKeyCredential.cpp",
|
||||
"WebAuthnArgs.cpp",
|
||||
"WebAuthnAutoFillEntry.cpp",
|
||||
"WebAuthnManager.cpp",
|
||||
"WebAuthnManagerBase.cpp",
|
||||
"WebAuthnPromiseHolder.cpp",
|
||||
|
@ -93,6 +93,4 @@ interface nsIWebAuthnSignArgs : nsISupports {
|
||||
// Arguably we don't need to pass it through since WebAuthnController can
|
||||
// cancel transactions.
|
||||
readonly attribute unsigned long timeoutMS;
|
||||
|
||||
readonly attribute bool conditionallyMediated;
|
||||
};
|
||||
|
@ -18,20 +18,6 @@ interface nsICredentialParameters : nsISupports
|
||||
readonly attribute uint32_t signCount;
|
||||
};
|
||||
|
||||
[scriptable, uuid(686d552e-a39d-4ba2-8127-faca54274039)]
|
||||
interface nsIWebAuthnAutoFillEntry: nsISupports
|
||||
{
|
||||
const octet PROVIDER_UNKNOWN = 0;
|
||||
const octet PROVIDER_TEST_TOKEN = 1;
|
||||
const octet PROVIDER_PLATFORM_WINDOWS = 2;
|
||||
const octet PROVIDER_PLATFORM_MACOS = 3;
|
||||
const octet PROVIDER_PLATFORM_ANDROID = 4;
|
||||
|
||||
readonly attribute octet provider;
|
||||
readonly attribute AString userName;
|
||||
readonly attribute AString rpId;
|
||||
readonly attribute Array<uint8_t> credentialId;
|
||||
};
|
||||
|
||||
[scriptable, uuid(e236a9b4-a26f-11ed-b6cc-07a9834e19b1)]
|
||||
interface nsIWebAuthnService : nsISupports
|
||||
@ -61,27 +47,6 @@ interface nsIWebAuthnService : nsISupports
|
||||
// "cancel" button.
|
||||
void cancel(in uint64_t aTransactionId);
|
||||
|
||||
// `hasPendingConditionalGet` returns the transaction ID of a pending
|
||||
// conditionally-mediated getAssertion promise. The browsing context and
|
||||
// origin arguments must match those of the pending promise. If there is no
|
||||
// pending getAssertion promise, or the browsing context and origin do not
|
||||
// match, then `hasPendingConditionalGet` returns 0.
|
||||
uint64_t hasPendingConditionalGet(in uint64_t aBrowsingContextId, in AString aOrigin);
|
||||
|
||||
// If there is a pending conditionally-mediated getAssertion promise with
|
||||
// transaction ID equal to `aTransactionId`, `getAutoFillEntries` returns
|
||||
// an nsIWebAuthnAutoFillEntry for each silently discoverable credential
|
||||
// that can be used to fullfill the request.
|
||||
Array<nsIWebAuthnAutoFillEntry> getAutoFillEntries(in uint64_t aTransactionId);
|
||||
|
||||
// A pending conditionally-mediated getAssertion promise is resolved by
|
||||
// calling `selectAutoFillEntry` or `resumeConditionalGet`.
|
||||
// `selectAutoFillEntry` specifies the credential ID that should be used to
|
||||
// fulfill the request, whereas `resumeConditionalGet` indicates that any
|
||||
// allowed credential can be used.
|
||||
void selectAutoFillEntry(in uint64_t aTransactionId, in Array<uint8_t> aCredentialId);
|
||||
void resumeConditionalGet(in uint64_t aTransactionId);
|
||||
|
||||
void pinCallback(in uint64_t aTransactionId, in ACString aPin);
|
||||
void resumeMakeCredential(in uint64_t aTransactionId, in bool aForceNoneAttestation);
|
||||
void selectionCallback(in uint64_t aTransactionId, in uint64_t aIndex);
|
||||
|
@ -11,7 +11,6 @@ prefs = [
|
||||
"security.webauth.webauthn_enable_softtoken=true",
|
||||
"security.webauth.webauthn_enable_usbtoken=false",
|
||||
"security.webauthn.ctap2=true",
|
||||
"security.webauthn.enable_conditional_mediation=true",
|
||||
]
|
||||
|
||||
["browser_abort_visibility.js"]
|
||||
@ -26,8 +25,6 @@ skip-if = [
|
||||
"win11_2009", # Test not relevant on 1903+
|
||||
]
|
||||
|
||||
["browser_webauthn_conditional_mediation.js"]
|
||||
|
||||
["browser_webauthn_ipaddress.js"]
|
||||
|
||||
["browser_webauthn_prompts.js"]
|
||||
|
@ -1,124 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_URL = "https://example.com";
|
||||
|
||||
let gAuthenticatorId = add_virtual_authenticator();
|
||||
let gExpectNotAllowedError = expectError("NotAllowed");
|
||||
let gPendingConditionalGetSubject = "webauthn:conditional-get-pending";
|
||||
let gWebAuthnService = Cc["@mozilla.org/webauthn/service;1"].getService(
|
||||
Ci.nsIWebAuthnService
|
||||
);
|
||||
|
||||
add_task(async function test_webauthn_resume_conditional_get() {
|
||||
// Open a new tab.
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
|
||||
|
||||
// TODO: Is there a better way to get the browsing context id?
|
||||
let browser = tab.linkedBrowser.browsingContext.embedderElement;
|
||||
let browsingContextId = browser.browsingContext.id;
|
||||
|
||||
let transactionId = gWebAuthnService.hasPendingConditionalGet(
|
||||
browsingContextId,
|
||||
TEST_URL
|
||||
);
|
||||
ok(transactionId == 0, "should not have a pending conditional get");
|
||||
|
||||
let requestStarted = TestUtils.topicObserved(gPendingConditionalGetSubject);
|
||||
|
||||
let active = true;
|
||||
let promise = promiseWebAuthnGetAssertionDiscoverable(tab, "conditional")
|
||||
.then(arrivingHereIsBad)
|
||||
.catch(gExpectNotAllowedError)
|
||||
.then(() => (active = false));
|
||||
|
||||
await requestStarted;
|
||||
|
||||
transactionId = gWebAuthnService.hasPendingConditionalGet(0, TEST_URL);
|
||||
ok(
|
||||
transactionId == 0,
|
||||
"hasPendingConditionalGet should check the browsing context id"
|
||||
);
|
||||
|
||||
transactionId = gWebAuthnService.hasPendingConditionalGet(
|
||||
browsingContextId,
|
||||
"https://example.org"
|
||||
);
|
||||
ok(transactionId == 0, "hasPendingConditionalGet should check the origin");
|
||||
|
||||
transactionId = gWebAuthnService.hasPendingConditionalGet(
|
||||
browsingContextId,
|
||||
TEST_URL
|
||||
);
|
||||
ok(transactionId != 0, "should have a pending conditional get");
|
||||
|
||||
ok(active, "request should still be active");
|
||||
|
||||
gWebAuthnService.resumeConditionalGet(transactionId);
|
||||
await promise;
|
||||
|
||||
ok(!active, "request should not be active");
|
||||
|
||||
// Close tab.
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
add_task(async function test_webauthn_select_autofill_entry() {
|
||||
// Open a new tab.
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
|
||||
|
||||
// Add credentials
|
||||
let cred1 = await addCredential(gAuthenticatorId, "example.com");
|
||||
let cred2 = await addCredential(gAuthenticatorId, "example.com");
|
||||
|
||||
// TODO: Is there a better way to get the browsing context id?
|
||||
let browser = tab.linkedBrowser.browsingContext.embedderElement;
|
||||
let browsingContextId = browser.browsingContext.id;
|
||||
|
||||
let transactionId = gWebAuthnService.hasPendingConditionalGet(
|
||||
browsingContextId,
|
||||
TEST_URL
|
||||
);
|
||||
ok(transactionId == 0, "should not have a pending conditional get");
|
||||
|
||||
let requestStarted = TestUtils.topicObserved(gPendingConditionalGetSubject);
|
||||
|
||||
let active = true;
|
||||
let promise = promiseWebAuthnGetAssertionDiscoverable(tab, "conditional")
|
||||
.catch(arrivingHereIsBad)
|
||||
.then(() => (active = false));
|
||||
|
||||
await requestStarted;
|
||||
|
||||
transactionId = gWebAuthnService.hasPendingConditionalGet(
|
||||
browsingContextId,
|
||||
TEST_URL
|
||||
);
|
||||
ok(transactionId != 0, "should have a pending conditional get");
|
||||
|
||||
let autoFillEntries = gWebAuthnService.getAutoFillEntries(transactionId);
|
||||
ok(
|
||||
autoFillEntries.length == 2 &&
|
||||
autoFillEntries[0].rpId == "example.com" &&
|
||||
autoFillEntries[1].rpId == "example.com",
|
||||
"should have two autofill entries for example.com"
|
||||
);
|
||||
|
||||
gWebAuthnService.selectAutoFillEntry(
|
||||
transactionId,
|
||||
autoFillEntries[0].credentialId
|
||||
);
|
||||
let result = await promise;
|
||||
|
||||
ok(!active, "request should not be active");
|
||||
|
||||
// Remove credentials
|
||||
gWebAuthnService.removeCredential(gAuthenticatorId, cred1);
|
||||
gWebAuthnService.removeCredential(gAuthenticatorId, cred2);
|
||||
|
||||
// Close tab.
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
});
|
@ -206,27 +206,19 @@ function promiseWebAuthnGetAssertion(tab, key_handle = null, extensions = {}) {
|
||||
);
|
||||
}
|
||||
|
||||
function promiseWebAuthnGetAssertionDiscoverable(
|
||||
tab,
|
||||
mediation = "optional",
|
||||
extensions = {}
|
||||
) {
|
||||
return ContentTask.spawn(
|
||||
tab.linkedBrowser,
|
||||
[extensions, mediation],
|
||||
([extensions, mediation]) => {
|
||||
let challenge = content.crypto.getRandomValues(new Uint8Array(16));
|
||||
function promiseWebAuthnGetAssertionDiscoverable(tab, extensions = {}) {
|
||||
return ContentTask.spawn(tab.linkedBrowser, [extensions], ([extensions]) => {
|
||||
let challenge = content.crypto.getRandomValues(new Uint8Array(16));
|
||||
|
||||
let publicKey = {
|
||||
challenge,
|
||||
extensions,
|
||||
rpId: content.document.domain,
|
||||
allowCredentials: [],
|
||||
};
|
||||
let publicKey = {
|
||||
challenge,
|
||||
extensions,
|
||||
rpId: content.document.domain,
|
||||
allowCredentials: [],
|
||||
};
|
||||
|
||||
return content.navigator.credentials.get({ publicKey, mediation });
|
||||
}
|
||||
);
|
||||
return content.navigator.credentials.get({ publicKey });
|
||||
});
|
||||
}
|
||||
|
||||
function checkRpIdHash(rpIdHash, hostname) {
|
||||
|
@ -13,7 +13,6 @@ prefs = [
|
||||
"security.webauth.webauthn_enable_softtoken=true",
|
||||
"security.webauth.webauthn_enable_usbtoken=false",
|
||||
"security.webauthn.ctap2=true",
|
||||
"security.webauthn.enable_conditional_mediation=true",
|
||||
]
|
||||
|
||||
["test_webauthn_abort_signal.html"]
|
||||
|
@ -14,6 +14,5 @@ dictionary AutocompleteInfo {
|
||||
DOMString addressType = "";
|
||||
DOMString contactType = "";
|
||||
DOMString fieldName = "";
|
||||
DOMString credentialType = "";
|
||||
boolean canAutomaticallyPersist = true;
|
||||
};
|
||||
|
@ -13812,16 +13812,6 @@
|
||||
value: true
|
||||
mirror: always
|
||||
|
||||
# WebAuthn conditional mediation
|
||||
- name: security.webauthn.enable_conditional_mediation
|
||||
type: RelaxedAtomicBool
|
||||
#if defined(XP_MACOSX)
|
||||
value: false
|
||||
#else
|
||||
value: @IS_EARLY_BETA_OR_EARLIER@
|
||||
#endif
|
||||
mirror: always
|
||||
|
||||
# Dispatch WebAuthn requests to virtual authenticators (mutually exclusive
|
||||
# with and webauthn_enable_usbtoken)
|
||||
- name: security.webauth.webauthn_enable_softtoken
|
||||
|
@ -92,7 +92,5 @@ user_pref("security.webauth.webauthn_enable_softtoken", true);
|
||||
user_pref("security.webauth.webauthn_enable_usbtoken", false);
|
||||
// Disable the WebAuthn direct attestation consent prompt.
|
||||
user_pref("security.webauth.webauthn_testing_allow_direct_attestation", true);
|
||||
// Enable WebAuthn conditional mediation.
|
||||
user_pref("security.webauthn.enable_conditional_mediation", true);
|
||||
// Disable captive portal service
|
||||
user_pref("network.captive-portal-service.enabled", false);
|
||||
|
@ -77,3 +77,9 @@
|
||||
|
||||
[one-time-code is an allowed autocomplete field name]
|
||||
expected: FAIL
|
||||
|
||||
[webauthn is an allowed autocomplete field name]
|
||||
expected: FAIL
|
||||
|
||||
[Serialize combinations of section, mode, contact, field, and credential]
|
||||
expected: FAIL
|
||||
|
@ -1,9 +1,3 @@
|
||||
[conditional-mediation.https.html]
|
||||
[Conditional mediation supported]
|
||||
expected:
|
||||
if os == "android": FAIL
|
||||
PASS
|
||||
[Conditional mediation not supported]
|
||||
expected:
|
||||
if os == "android": PASS
|
||||
FAIL
|
||||
expected: FAIL
|
||||
|
@ -32,7 +32,6 @@ export class FieldDetail {
|
||||
section = "";
|
||||
addressType = "";
|
||||
contactType = "";
|
||||
credentialType = "";
|
||||
|
||||
// When a field is split into N fields, we use part to record which field it is
|
||||
// For example, a credit card number field is split into 4 fields, the value of
|
||||
@ -57,7 +56,6 @@ export class FieldDetail {
|
||||
this.section = autocompleteInfo.section;
|
||||
this.addressType = autocompleteInfo.addressType;
|
||||
this.contactType = autocompleteInfo.contactType;
|
||||
this.credentialType = autocompleteInfo.credentialType;
|
||||
} else if (confidence) {
|
||||
this.reason = "fathom";
|
||||
this.confidence = confidence;
|
||||
|
@ -673,7 +673,6 @@ export class LoginAutoComplete {
|
||||
isProbablyANewPasswordField,
|
||||
scenarioName: scenario?.constructor.name,
|
||||
inputMaxLength: inputElement.maxLength,
|
||||
isWebAuthn: this.#isWebAuthnCredentials(autocompleteInfo),
|
||||
}
|
||||
);
|
||||
|
||||
@ -704,15 +703,6 @@ export class LoginAutoComplete {
|
||||
this.#cachedNewPasswordScore.set(inputElement, score);
|
||||
return score >= threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} autocompleteInfo
|
||||
* @returns whether the non-autofill credential type (https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#non-autofill-credential-type)
|
||||
* of the input field is "webauthn"
|
||||
*/
|
||||
#isWebAuthnCredentials(autocompleteInfo) {
|
||||
return autocompleteInfo.credentialType == "webauthn";
|
||||
}
|
||||
}
|
||||
|
||||
let gAutoCompleteListener = {
|
||||
|
@ -33,7 +33,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||
LoginHelper: "resource://gre/modules/LoginHelper.sys.mjs",
|
||||
MigrationUtils: "resource:///modules/MigrationUtils.sys.mjs",
|
||||
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
|
||||
WebAuthnFeature: "resource://gre/modules/WebAuthnFeature.sys.mjs",
|
||||
PasswordGenerator: "resource://gre/modules/PasswordGenerator.sys.mjs",
|
||||
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
|
||||
});
|
||||
@ -385,10 +384,6 @@ export class LoginManagerParent extends JSWindowActorParent {
|
||||
);
|
||||
return this.#generateRelayUsername(context.origin);
|
||||
}
|
||||
|
||||
case "PasswordManager:promptForAuthenticator": {
|
||||
return this.#promptForAuthenticator(data.selection);
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@ -527,11 +522,6 @@ export class LoginManagerParent extends JSWindowActorParent {
|
||||
return lazy.FirefoxRelay.generateUsername(browser, origin);
|
||||
}
|
||||
|
||||
async #promptForAuthenticator(selection) {
|
||||
const browser = lazy.LoginHelper.getBrowserForPrompt(this.getRootBrowser());
|
||||
return lazy.WebAuthnFeature.promptForAuthenticator(browser, selection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the remaining number of import suggestion impressions with debounce
|
||||
* to allow multiple popups showing the "same" items to count as one.
|
||||
@ -720,7 +710,6 @@ export class LoginManagerParent extends JSWindowActorParent {
|
||||
isProbablyANewPasswordField,
|
||||
scenarioName,
|
||||
inputMaxLength,
|
||||
isWebAuthn,
|
||||
}
|
||||
) {
|
||||
// Note: previousResult is a regular object, not an
|
||||
@ -818,31 +807,16 @@ export class LoginManagerParent extends JSWindowActorParent {
|
||||
// doesn't support structured cloning.
|
||||
let jsLogins = lazy.LoginHelper.loginsToVanillaObjects(matchingLogins);
|
||||
|
||||
let autocompleteItems = [];
|
||||
|
||||
if (!hasBeenTypePassword) {
|
||||
autocompleteItems.push(
|
||||
...(await lazy.FirefoxRelay.autocompleteItemsAsync({
|
||||
formOrigin,
|
||||
scenarioName,
|
||||
hasInput: !!searchStringLower.length,
|
||||
}))
|
||||
);
|
||||
}
|
||||
autocompleteItems.push(
|
||||
...(await lazy.WebAuthnFeature.autocompleteItemsAsync(
|
||||
this._overrideBrowsingContextId ??
|
||||
this.getRootBrowser().browsingContext.id,
|
||||
formOrigin,
|
||||
scenarioName,
|
||||
isWebAuthn
|
||||
))
|
||||
);
|
||||
|
||||
return {
|
||||
generatedPassword,
|
||||
importable: await getImportableLogins(formOrigin),
|
||||
autocompleteItems,
|
||||
autocompleteItems: hasBeenTypePassword
|
||||
? []
|
||||
: await lazy.FirefoxRelay.autocompleteItemsAsync({
|
||||
formOrigin,
|
||||
scenarioName,
|
||||
hasInput: !!searchStringLower.length,
|
||||
}),
|
||||
logins: jsLogins,
|
||||
willAutoSaveGeneratedPassword,
|
||||
};
|
||||
|
@ -1,129 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
import {
|
||||
LoginHelper,
|
||||
ParentAutocompleteOption,
|
||||
} from "resource://gre/modules/LoginHelper.sys.mjs";
|
||||
|
||||
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
|
||||
|
||||
const lazy = {};
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
lazy,
|
||||
"webauthnService",
|
||||
"@mozilla.org/webauthn/service;1",
|
||||
"nsIWebAuthnService"
|
||||
);
|
||||
|
||||
ChromeUtils.defineLazyGetter(
|
||||
lazy,
|
||||
"strings",
|
||||
() => new Localization(["browser/webauthnDialog.ftl"])
|
||||
);
|
||||
ChromeUtils.defineLazyGetter(lazy, "log", () =>
|
||||
LoginHelper.createLogger("WebAuthnFeature")
|
||||
);
|
||||
|
||||
if (Services.appinfo.processType !== Services.appinfo.PROCESS_TYPE_DEFAULT) {
|
||||
throw new Error(
|
||||
"PasskeySupport.sys.mjs should only run in the parent process"
|
||||
);
|
||||
}
|
||||
|
||||
class WebAuthnSupport {
|
||||
async *#getAutocompleteItemsAsync(browsingContextId, formOrigin) {
|
||||
let transactionId = lazy.webauthnService.hasPendingConditionalGet(
|
||||
browsingContextId,
|
||||
formOrigin
|
||||
);
|
||||
if (transactionId == 0) {
|
||||
// No pending transaction
|
||||
return;
|
||||
}
|
||||
let credentials = lazy.webauthnService.getAutoFillEntries(transactionId);
|
||||
|
||||
let labels = credentials.map(x => ({
|
||||
id: "webauthn-specific-passkey-label",
|
||||
args: { domain: x.rpId },
|
||||
}));
|
||||
if (!credentials.length) {
|
||||
labels.push({ id: "webauthn-a-passkey-label" });
|
||||
} else {
|
||||
labels.push({ id: "webauthn-another-passkey-label" });
|
||||
}
|
||||
const formattedLabels = await lazy.strings.formatValues(labels);
|
||||
for (let i = 0; i < credentials.length; i++) {
|
||||
yield new ParentAutocompleteOption(
|
||||
"chrome://browser/content/logos/etp-mobile.svg", // Bug 1867622: replace this with final icon
|
||||
credentials[i].userName,
|
||||
formattedLabels[i],
|
||||
"PasswordManager:promptForAuthenticator",
|
||||
{
|
||||
selection: {
|
||||
transactionId,
|
||||
credentialId: credentials[i].credentialId,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
// `getAutoFillEntries` may not return all of the credentials on the device
|
||||
// (in particular it will not include credentials with a protection policy
|
||||
// that forbids silent discovery), so we include a catch-all entry in the
|
||||
// list. If the user selects this entry, the WebAuthn transaction will
|
||||
// proceed using the modal UI.
|
||||
yield new ParentAutocompleteOption(
|
||||
"chrome://browser/content/logos/etp-mobile.svg", // Bug 1867622: replace this with final icon
|
||||
formattedLabels[formattedLabels.length - 1],
|
||||
"",
|
||||
"PasswordManager:promptForAuthenticator",
|
||||
{
|
||||
selection: {
|
||||
transactionId,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {int} browsingContextId the browsing context ID associated with this request
|
||||
* @param {string} formOrigin
|
||||
* @param {string} scenarioName can be "SignUpFormScenario" or undefined
|
||||
* @param {string} isWebAuthn indicates whether "webauthn" was included in the input's autocomplete value
|
||||
* @returns {ParentAutocompleteOption} the optional WebAuthn autocomplete item
|
||||
*/
|
||||
async autocompleteItemsAsync(
|
||||
browsingContextId,
|
||||
formOrigin,
|
||||
scenarioName,
|
||||
isWebAuthn
|
||||
) {
|
||||
const result = [];
|
||||
if (scenarioName !== "SignUpFormScenario" || isWebAuthn) {
|
||||
for await (const item of this.#getAutocompleteItemsAsync(
|
||||
browsingContextId,
|
||||
formOrigin
|
||||
)) {
|
||||
result.push(item);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async promptForAuthenticator(browser, selection) {
|
||||
lazy.log.info("Prompting to authenticate with relying party.");
|
||||
if (selection.credentialId) {
|
||||
lazy.webauthnService.selectAutoFillEntry(
|
||||
selection.transactionId,
|
||||
selection.credentialId
|
||||
);
|
||||
} else {
|
||||
lazy.webauthnService.resumeConditionalGet(selection.transactionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const WebAuthnFeature = new WebAuthnSupport();
|
@ -35,7 +35,6 @@ EXTRA_JS_MODULES += [
|
||||
"integrations/FirefoxRelay.sys.mjs",
|
||||
"integrations/FirefoxRelayTelemetry.mjs",
|
||||
"integrations/FirefoxRelayUtils.sys.mjs",
|
||||
"integrations/WebAuthnFeature.sys.mjs",
|
||||
"SignUpFormRuleset.sys.mjs",
|
||||
]
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user