Skip to content

Commit dd63c7f

Browse files
nsatragnoChromium LUCI CQ
authored andcommitted
[webauthn] Match Conditional UI API to spec
Update the WebAuthn Conditional UI API to match the proposed specification, replacing the conditionalPublicKey attribute with a "conditional" mediation value. Attempting to set the mediation to "conditional" for password or federated credentials will result in a NotSupportedError. Setting the value for WebAuthn requests will be ignored unless the WebAuthenticationConditionalUI flag is enabled. PR spec: w3c/webauthn#1576 Design doc: https://docs.google.com/document/d/1KzEWP0aoLMZ0asfw6d3-7UHJ6csTtxLA478EgptCvkk Bug: 1171985 Change-Id: Ia0045d7fd9becadeadaa362b29ddd56dc38a79b2 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2780274 Reviewed-by: Martin Kreichgauer <martinkr@google.com> Commit-Queue: Nina Satragno <nsatragno@chromium.org> Cr-Commit-Position: refs/heads/master@{#866725}
1 parent 78b542c commit dd63c7f

File tree

5 files changed

+39
-33
lines changed

5 files changed

+39
-33
lines changed

chrome/browser/ui/views/webauthn/webauthn_icon_interactive_uitest.cc

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,13 @@ IN_PROC_BROWSER_TEST_F(WebAuthUITest, ConditionalUI) {
108108
});
109109

110110
constexpr char kGetAssertion[] =
111-
"navigator.credentials.get({conditionalPublicKey: {"
112-
" challenge: new Uint8Array([1,2,3,4]),"
113-
" timeout: 1000,"
114-
"}}).then(c => window.domAutomationController.send(c ? 'OK' : 'c null'),"
111+
"navigator.credentials.get({"
112+
" publicKey: {"
113+
" challenge: new Uint8Array([1,2,3,4]),"
114+
" timeout: 1000,"
115+
" },"
116+
" mediation: 'conditional'"
117+
"}).then(c => window.domAutomationController.send(c ? 'OK' : 'c null'),"
115118
" e => window.domAutomationController.send(e.toString()));";
116119
content::WebContents* const web_contents =
117120
browser()->tab_strip_model()->GetActiveWebContents();

third_party/blink/renderer/modules/credentialmanager/credential_request_options.idl

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
enum CredentialMediationRequirement {
88
"silent",
99
"optional",
10-
"required"
10+
"required",
11+
"conditional"
1112
};
1213

1314
dictionary CredentialRequestOptions {
@@ -19,8 +20,6 @@ dictionary CredentialRequestOptions {
1920
[RuntimeEnabled=WebOTP] OTPCredentialRequestOptions otp;
2021

2122
PublicKeyCredentialRequestOptions publicKey;
22-
[RuntimeEnabled=WebAuthenticationConditionalUI]
23-
PublicKeyCredentialRequestOptions conditionalPublicKey;
2423

2524
AbortSignal signal;
2625
};

third_party/blink/renderer/modules/credentialmanager/credentials_container.cc

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,7 @@ ScriptPromise CredentialsContainer::get(
937937

938938
auto required_origin_type = RequiredOriginType::kSecureAndSameWithAncestors;
939939
// hasPublicKey() implies that this is a WebAuthn request.
940-
if ((options->hasPublicKey() || options->hasConditionalPublicKey()) &&
940+
if (options->hasPublicKey() &&
941941
RuntimeEnabledFeatures::
942942
WebAuthenticationGetAssertionFeaturePolicyEnabled()) {
943943
required_origin_type = RequiredOriginType::
@@ -951,17 +951,7 @@ ScriptPromise CredentialsContainer::get(
951951
return promise;
952952
}
953953

954-
if (options->hasPublicKey() || options->hasConditionalPublicKey()) {
955-
const PublicKeyCredentialRequestOptions* public_key_options;
956-
bool is_conditional_ui_request;
957-
if (options->hasPublicKey()) {
958-
public_key_options = options->publicKey();
959-
is_conditional_ui_request = false;
960-
} else {
961-
public_key_options = options->conditionalPublicKey();
962-
is_conditional_ui_request = true;
963-
}
964-
954+
if (options->hasPublicKey()) {
965955
auto cryptotoken_origin = SecurityOrigin::Create(KURL(kCryptotokenOrigin));
966956
if (!cryptotoken_origin->IsSameOriginWith(
967957
resolver->GetExecutionContext()->GetSecurityOrigin())) {
@@ -972,21 +962,21 @@ ScriptPromise CredentialsContainer::get(
972962
}
973963

974964
#if defined(OS_ANDROID)
975-
if (public_key_options->hasExtensions() &&
976-
public_key_options->extensions()->hasUvm()) {
965+
if (options->publicKey()->hasExtensions() &&
966+
options->publicKey()->extensions()->hasUvm()) {
977967
UseCounter::Count(resolver->GetExecutionContext(),
978968
WebFeature::kCredentialManagerGetWithUVM);
979969
}
980970
#endif
981-
if (!IsArrayBufferOrViewBelowSizeLimit(public_key_options->challenge())) {
971+
if (!IsArrayBufferOrViewBelowSizeLimit(options->publicKey()->challenge())) {
982972
resolver->Reject(DOMException::Create(
983973
"The `challenge` attribute exceeds the maximum allowed size.",
984974
"RangeError"));
985975
return promise;
986976
}
987-
if (public_key_options->hasExtensions()) {
988-
if (public_key_options->extensions()->hasAppid()) {
989-
const auto& appid = public_key_options->extensions()->appid();
977+
if (options->publicKey()->hasExtensions()) {
978+
if (options->publicKey()->extensions()->hasAppid()) {
979+
const auto& appid = options->publicKey()->extensions()->appid();
990980
if (!appid.IsEmpty()) {
991981
KURL appid_url(appid);
992982
if (!appid_url.IsValid()) {
@@ -998,24 +988,24 @@ ScriptPromise CredentialsContainer::get(
998988
}
999989
}
1000990
}
1001-
if (public_key_options->extensions()->hasCableRegistration()) {
991+
if (options->publicKey()->extensions()->hasCableRegistration()) {
1002992
resolver->Reject(MakeGarbageCollected<DOMException>(
1003993
DOMExceptionCode::kNotSupportedError,
1004994
"The 'cableRegistration' extension is only valid when creating "
1005995
"a credential"));
1006996
return promise;
1007997
}
1008-
if (public_key_options->extensions()->credProps()) {
998+
if (options->publicKey()->extensions()->credProps()) {
1009999
resolver->Reject(MakeGarbageCollected<DOMException>(
10101000
DOMExceptionCode::kNotSupportedError,
10111001
"The 'credProps' extension is only valid when creating "
10121002
"a credential"));
10131003
return promise;
10141004
}
1015-
if (public_key_options->extensions()->hasLargeBlob()) {
1005+
if (options->publicKey()->extensions()->hasLargeBlob()) {
10161006
DCHECK(RuntimeEnabledFeatures::
10171007
WebAuthenticationLargeBlobExtensionEnabled());
1018-
if (public_key_options->extensions()->largeBlob()->hasSupport()) {
1008+
if (options->publicKey()->extensions()->largeBlob()->hasSupport()) {
10191009
resolver->Reject(MakeGarbageCollected<DOMException>(
10201010
DOMExceptionCode::kNotSupportedError,
10211011
"The 'largeBlob' extension's 'support' parameter is only valid "
@@ -1025,7 +1015,7 @@ ScriptPromise CredentialsContainer::get(
10251015
}
10261016
}
10271017

1028-
if (!public_key_options->hasUserVerification()) {
1018+
if (!options->publicKey()->hasUserVerification()) {
10291019
resolver->DomWindow()->AddConsoleMessage(
10301020
MakeGarbageCollected<ConsoleMessage>(
10311021
mojom::blink::ConsoleMessageSource::kJavaScript,
@@ -1050,17 +1040,19 @@ ScriptPromise CredentialsContainer::get(
10501040
WTF::Bind(&AbortPublicKeyRequest, WrapPersistent(script_state)));
10511041
}
10521042

1043+
bool is_conditional_ui_request = conditionalMediationSupported() &&
1044+
options->mediation() == "conditional";
10531045
if (is_conditional_ui_request &&
1054-
public_key_options->hasAllowCredentials() &&
1055-
!public_key_options->allowCredentials().IsEmpty()) {
1046+
options->publicKey()->hasAllowCredentials() &&
1047+
!options->publicKey()->allowCredentials().IsEmpty()) {
10561048
resolver->Reject(MakeGarbageCollected<DOMException>(
10571049
DOMExceptionCode::kNotAllowedError,
10581050
"allowCredentials is not supported for conditionalPublicKey"));
10591051
return promise;
10601052
}
10611053

10621054
auto mojo_options =
1063-
MojoPublicKeyCredentialRequestOptions::From(*public_key_options);
1055+
MojoPublicKeyCredentialRequestOptions::From(*options->publicKey());
10641056
if (mojo_options) {
10651057
mojo_options->is_conditional = is_conditional_ui_request;
10661058
if (!mojo_options->relying_party_id) {
@@ -1117,6 +1109,12 @@ ScriptPromise CredentialsContainer::get(
11171109
}
11181110

11191111
CredentialMediationRequirement requirement;
1112+
if (options->mediation() == "conditional") {
1113+
resolver->Reject(MakeGarbageCollected<DOMException>(
1114+
DOMExceptionCode::kNotSupportedError,
1115+
"Conditional mediation is not supported for this credential type"));
1116+
return promise;
1117+
}
11201118
if (options->mediation() == "silent") {
11211119
UseCounter::Count(ExecutionContext::From(script_state),
11221120
WebFeature::kCredentialManagerGetMediationSilent);
@@ -1419,6 +1417,10 @@ ScriptPromise CredentialsContainer::preventSilentAccess(
14191417
return promise;
14201418
}
14211419

1420+
bool CredentialsContainer::conditionalMediationSupported() {
1421+
return RuntimeEnabledFeatures::WebAuthenticationConditionalUIEnabled();
1422+
}
1423+
14221424
void CredentialsContainer::Trace(Visitor* visitor) const {
14231425
ScriptWrappable::Trace(visitor);
14241426
Supplement<Navigator>::Trace(visitor);

third_party/blink/renderer/modules/credentialmanager/credentials_container.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class MODULES_EXPORT CredentialsContainer final : public ScriptWrappable,
3636
const CredentialCreationOptions*,
3737
ExceptionState&);
3838
ScriptPromise preventSilentAccess(ScriptState*);
39+
bool conditionalMediationSupported();
3940

4041
void Trace(Visitor*) const override;
4142
};

third_party/blink/renderer/modules/credentialmanager/credentials_container.idl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ interface CredentialsContainer {
1010
[CallWith=ScriptState, MeasureAs=CredentialManagerStore] Promise<Credential> store(Credential credential);
1111
[CallWith=ScriptState, RaisesException, MeasureAs=CredentialManagerCreate] Promise<Credential?> create(optional CredentialCreationOptions options = {});
1212
[CallWith=ScriptState, MeasureAs=CredentialManagerPreventSilentAccess] Promise<void> preventSilentAccess();
13+
[RuntimeEnabled=WebAuthenticationConditionalUI] readonly attribute boolean conditionalMediationSupported;
1314
};

0 commit comments

Comments
 (0)