Diff
Modified: trunk/LayoutTests/ChangeLog (287115 => 287116)
--- trunk/LayoutTests/ChangeLog 2021-12-16 00:51:44 UTC (rev 287115)
+++ trunk/LayoutTests/ChangeLog 2021-12-16 00:52:57 UTC (rev 287116)
@@ -1,3 +1,18 @@
+2021-12-15 J Pascoe <j_pas...@apple.com>
+
+ [WebAuthn] Allow same-site, cross-origin iframe get()
+ https://bugs.webkit.org/show_bug.cgi?id=234309
+ rdar://problem/86486313
+
+ Reviewed by Brent Fulgham.
+
+ Add layout test for WebAuthn get assertions on cross-site, same-sites i-frames with
+ publickey-credentials-get feature policy.
+
+ * http/wpt/webauthn/public-key-credential-same-origin-with-ancestors.https-expected.txt:
+ * http/wpt/webauthn/public-key-credential-same-origin-with-ancestors.https.html:
+ * http/wpt/webauthn/resources/util.js:
+
2021-12-15 Ryan Haddad <ryanhad...@apple.com>
PCM: Remove old DB update and migration code, and add a unit test for destination token DB columns
Modified: trunk/LayoutTests/http/wpt/webauthn/public-key-credential-same-origin-with-ancestors.https-expected.txt (287115 => 287116)
--- trunk/LayoutTests/http/wpt/webauthn/public-key-credential-same-origin-with-ancestors.https-expected.txt 2021-12-16 00:51:44 UTC (rev 287115)
+++ trunk/LayoutTests/http/wpt/webauthn/public-key-credential-same-origin-with-ancestors.https-expected.txt 2021-12-16 00:52:57 UTC (rev 287116)
@@ -2,4 +2,7 @@
PASS Tests that a frame that doesn't share the same origin with all its ancestors could not access the API.
PASS Tests that a frame that doesn't share the same origin with all its ancestors could not access the API. 2
+PASS Tests that a frame that is same-site, cross-origin without publickey-credentials-get feature policy cannot use get().
+PASS Tests that a frame that is same-site, cross-origin with publickey-credentials-get feature policy can use get().
+PASS Tests that a frame that is cross-origin, NOT same-site with publickey-credentials-get feature policy cannot use get().
Modified: trunk/LayoutTests/http/wpt/webauthn/public-key-credential-same-origin-with-ancestors.https.html (287115 => 287116)
--- trunk/LayoutTests/http/wpt/webauthn/public-key-credential-same-origin-with-ancestors.https.html 2021-12-16 00:51:44 UTC (rev 287115)
+++ trunk/LayoutTests/http/wpt/webauthn/public-key-credential-same-origin-with-ancestors.https.html 2021-12-16 00:52:57 UTC (rev 287116)
@@ -22,6 +22,24 @@
assert_equals(message.data, "PASS.");
});
}, "Tests that a frame that doesn't share the same origin with all its ancestors could not access the API. 2");
+
+ promise_test(t => {
+ return withSameSiteIframe("samesite-iframe.html").then((message) => {
+ assert_equals(message.data, "Throw NotAllowedError: The origin of the document is not the same as its ancestors.");
+ });
+ }, "Tests that a frame that is same-site, cross-origin without publickey-credentials-get feature policy cannot use get().");
+
+ promise_test(t => {
+ return withSameSiteIframe("samesite-iframe.html", "publickey-credentials-get").then((message) => {
+ assert_equals(message.data, "PASS!");
+ });
+ }, "Tests that a frame that is same-site, cross-origin with publickey-credentials-get feature policy can use get().");
+
+ promise_test(t => {
+ return withCrossOriginIframe("samesite-iframe.html", "publickey-credentials-get").then((message) => {
+ assert_equals(message.data, "Throw NotAllowedError: The origin of the document is not the same as its ancestors.");
+ });
+ }, "Tests that a frame that is cross-origin, NOT same-site with publickey-credentials-get feature policy cannot use get().");
</script>
</body>
</html>
Added: trunk/LayoutTests/http/wpt/webauthn/resources/samesite-iframe.html (0 => 287116)
--- trunk/LayoutTests/http/wpt/webauthn/resources/samesite-iframe.html (rev 0)
+++ trunk/LayoutTests/http/wpt/webauthn/resources/samesite-iframe.html 2021-12-16 00:52:57 UTC (rev 287116)
@@ -0,0 +1,30 @@
+<script src=""
+<input type="text" id="input">
+<script>
+ const url = "" URL(window.location.href);
+ if (window.internals)
+ internals.setMockWebAuthenticationConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", payloadBase64: [testAssertionMessageBase64] } });
+
+ function messageToTop(message) {
+ top.postMessage(message, "*");
+ }
+
+ const requestOptions = {
+ publicKey: {
+ challenge: asciiToUint8Array("123456"),
+ timeout: 100
+ }
+ };
+
+ if (window.internals)
+ internals.withUserGesture(() => { input.focus(); });
+
+ navigator.credentials.get(requestOptions).then(
+ function(value) {
+ messageToTop("PASS!");
+ },
+ function(exception) {
+ messageToTop("Throw " + exception.name + ": " + exception.message);
+ }
+ );
+</script>
Modified: trunk/LayoutTests/http/wpt/webauthn/resources/util.js (287115 => 287116)
--- trunk/LayoutTests/http/wpt/webauthn/resources/util.js 2021-12-16 00:51:44 UTC (rev 287115)
+++ trunk/LayoutTests/http/wpt/webauthn/resources/util.js 2021-12-16 00:52:57 UTC (rev 287116)
@@ -304,7 +304,7 @@
});
}
-function withCrossOriginIframe(resourceFile)
+function withCrossOriginIframe(resourceFile, allow = "")
{
return new Promise((resolve) => {
waitForLoad().then((message) => {
@@ -311,11 +311,26 @@
resolve(message);
});
const frame = document.createElement("iframe");
+ frame.allow = allow;
frame.src = "" + RESOURCES_DIR + resourceFile;
document.body.appendChild(frame);
});
}
+function withSameSiteIframe(resourceFile, allow = "")
+{
+ return new Promise((resolve) => {
+ waitForLoad().then((message) => {
+ resolve(message);
+ });
+ const frame = document.createElement("iframe");
+ const host = get_host_info();
+ frame.allow = allow;
+ frame.src = "" + host.ORIGINAL_HOST + ":" + host.HTTPS_PORT2 + RESOURCES_DIR + resourceFile;
+ document.body.appendChild(frame);
+ });
+}
+
function promiseRejects(test, expected, promise, description)
{
return promise.then(test.unreached_func("Should have rejected: " + description)).catch(function(e) {
Modified: trunk/Source/WebCore/ChangeLog (287115 => 287116)
--- trunk/Source/WebCore/ChangeLog 2021-12-16 00:51:44 UTC (rev 287115)
+++ trunk/Source/WebCore/ChangeLog 2021-12-16 00:52:57 UTC (rev 287116)
@@ -1,3 +1,35 @@
+2021-12-15 J Pascoe <j_pas...@apple.com>
+
+ [WebAuthn] Allow same-site, cross-origin iframe get()
+ https://bugs.webkit.org/show_bug.cgi?id=234309
+ rdar://problem/86486313
+
+ Reviewed by Brent Fulgham.
+
+ The Web Authentication level 2 specifies a feature policy to allow get calls in
+ cross-origin i-frames. This patch implements this feature policy partially. Only
+ same-site, cross-origin i-frames are supported instead. This is for tracking prevention
+ purposes. https://w3c.github.io/webauthn/#sctn-iframe-guidance
+
+ This patch also starts passing ClientDataJSON hashes to ASC to avoid the situation
+ where WebKit includes crossOrigin or other fields in ClientDataJSON that ASC is
+ unaware of when generating ClientDataJSON.
+
+ Added layout test cases for same-site, cross-origin get calls.
+
+ * Modules/webauthn/AuthenticatorCoordinator.cpp:
+ (WebCore::AuthenticatorCoordinator::create const):
+ (WebCore::doesHaveSameSiteAsAncestors):
+ (WebCore::AuthenticatorCoordinator::discoverFromExternalSource const):
+ * Modules/webauthn/WebAuthenticationUtils.cpp:
+ (WebCore::buildClientDataJson):
+ * Modules/webauthn/WebAuthenticationUtils.h:
+ * html/FeaturePolicy.cpp:
+ (WebCore::policyTypeName):
+ (WebCore::FeaturePolicy::parse):
+ (WebCore::FeaturePolicy::allows const):
+ * html/FeaturePolicy.h:
+
2021-12-15 Brent Fulgham <bfulg...@apple.com>
Clean-up: Adopt Page::forEachDocument in some missed spots
Modified: trunk/Source/WebCore/Modules/credentialmanagement/CredentialsContainer.cpp (287115 => 287116)
--- trunk/Source/WebCore/Modules/credentialmanagement/CredentialsContainer.cpp 2021-12-16 00:51:44 UTC (rev 287115)
+++ trunk/Source/WebCore/Modules/credentialmanagement/CredentialsContainer.cpp 2021-12-16 00:52:57 UTC (rev 287116)
@@ -37,6 +37,7 @@
#include "JSDOMPromiseDeferred.h"
#include "Page.h"
#include "SecurityOrigin.h"
+#include "WebAuthenticationConstants.h"
namespace WebCore {
@@ -45,19 +46,27 @@
{
}
-bool CredentialsContainer::doesHaveSameOriginAsItsAncestors()
+WebAuthn::Scope CredentialsContainer::scope()
{
- // The following implements https://w3c.github.io/webappsec-credential-management/#same-origin-with-its-ancestors
- // as of 14 November 2017.
if (!m_document)
- return false;
+ return WebAuthn::Scope::CrossOrigin;
+ bool isSameOrigin = true;
+ bool isSameSite = true;
auto& origin = m_document->securityOrigin();
+ auto& url = ""
for (auto* document = m_document->parentDocument(); document; document = document->parentDocument()) {
+ if (!origin.isSameOriginDomain(document->securityOrigin()) && !areRegistrableDomainsEqual(url, document->url()))
+ isSameSite = false;
if (!origin.isSameOriginAs(document->securityOrigin()))
- return false;
+ isSameOrigin = false;
}
- return true;
+
+ if (isSameOrigin)
+ return WebAuthn::Scope::SameOrigin;
+ if (isSameSite)
+ return WebAuthn::Scope::SameSite;
+ return WebAuthn::Scope::CrossOrigin;
}
void CredentialsContainer::get(CredentialRequestOptions&& options, CredentialPromise&& promise)
@@ -89,7 +98,7 @@
return;
}
- m_document->page()->authenticatorCoordinator().discoverFromExternalSource(*m_document, options.publicKey.value(), doesHaveSameOriginAsItsAncestors(), WTFMove(options.signal), WTFMove(promise));
+ m_document->page()->authenticatorCoordinator().discoverFromExternalSource(*m_document, options.publicKey.value(), scope(), WTFMove(options.signal), WTFMove(promise));
}
void CredentialsContainer::store(const BasicCredential&, CredentialPromise&& promise)
@@ -124,7 +133,7 @@
return;
}
- m_document->page()->authenticatorCoordinator().create(*m_document, options.publicKey.value(), doesHaveSameOriginAsItsAncestors(), WTFMove(options.signal), WTFMove(promise));
+ m_document->page()->authenticatorCoordinator().create(*m_document, options.publicKey.value(), scope(), WTFMove(options.signal), WTFMove(promise));
}
void CredentialsContainer::preventSilentAccess(DOMPromiseDeferred<void>&& promise) const
Modified: trunk/Source/WebCore/Modules/credentialmanagement/CredentialsContainer.h (287115 => 287116)
--- trunk/Source/WebCore/Modules/credentialmanagement/CredentialsContainer.h 2021-12-16 00:51:44 UTC (rev 287115)
+++ trunk/Source/WebCore/Modules/credentialmanagement/CredentialsContainer.h 2021-12-16 00:52:57 UTC (rev 287116)
@@ -32,6 +32,10 @@
#include <wtf/RefCounted.h>
#include <wtf/WeakPtr.h>
+namespace WebAuthn {
+enum class Scope;
+}
+
namespace WebCore {
class Document;
@@ -54,7 +58,7 @@
private:
CredentialsContainer(WeakPtr<Document>&&);
- bool doesHaveSameOriginAsItsAncestors();
+ WebAuthn::Scope scope();
WeakPtr<Document> m_document;
};
Modified: trunk/Source/WebCore/Modules/webauthn/AuthenticatorCoordinator.cpp (287115 => 287116)
--- trunk/Source/WebCore/Modules/webauthn/AuthenticatorCoordinator.cpp 2021-12-16 00:51:44 UTC (rev 287115)
+++ trunk/Source/WebCore/Modules/webauthn/AuthenticatorCoordinator.cpp 2021-12-16 00:52:57 UTC (rev 287116)
@@ -34,6 +34,7 @@
#include "AuthenticatorCoordinatorClient.h"
#include "AuthenticatorResponseData.h"
#include "Document.h"
+#include "FeaturePolicy.h"
#include "JSBasicCredential.h"
#include "JSDOMPromiseDeferred.h"
#include "PublicKeyCredential.h"
@@ -104,7 +105,7 @@
m_client = WTFMove(client);
}
-void AuthenticatorCoordinator::create(const Document& document, const PublicKeyCredentialCreationOptions& options, bool sameOriginWithAncestors, RefPtr<AbortSignal>&& abortSignal, CredentialPromise&& promise) const
+void AuthenticatorCoordinator::create(const Document& document, const PublicKeyCredentialCreationOptions& options, WebAuthn::Scope scope, RefPtr<AbortSignal>&& abortSignal, CredentialPromise&& promise) const
{
using namespace AuthenticatorCoordinatorInternal;
@@ -114,7 +115,7 @@
// The following implements https://www.w3.org/TR/webauthn/#createCredential as of 5 December 2017.
// Step 1, 3, 16 are handled by the caller.
// Step 2.
- if (!sameOriginWithAncestors) {
+ if (scope != WebAuthn::Scope::SameOrigin) {
promise.reject(Exception { NotAllowedError, "The origin of the document is not the same as its ancestors."_s });
return;
}
@@ -148,7 +149,7 @@
options.extensions = AuthenticationExtensionsClientInputs { String(), processGoogleLegacyAppIdSupportExtension(options.extensions, options.rp.id) };
// Step 13-15.
- auto clientDataJson = buildClientDataJson(ClientDataType::Create, options.challenge, callerOrigin);
+ auto clientDataJson = buildClientDataJson(ClientDataType::Create, options.challenge, callerOrigin, scope);
auto clientDataJsonHash = buildClientDataJsonHash(clientDataJson);
// Step 4, 17-21.
@@ -175,7 +176,7 @@
m_client->makeCredential(*frame, callerOrigin, clientDataJsonHash, options, WTFMove(callback));
}
-void AuthenticatorCoordinator::discoverFromExternalSource(const Document& document, const PublicKeyCredentialRequestOptions& options, bool sameOriginWithAncestors, RefPtr<AbortSignal>&& abortSignal, CredentialPromise&& promise) const
+void AuthenticatorCoordinator::discoverFromExternalSource(const Document& document, const PublicKeyCredentialRequestOptions& options, WebAuthn::Scope scope, RefPtr<AbortSignal>&& abortSignal, CredentialPromise&& promise) const
{
using namespace AuthenticatorCoordinatorInternal;
@@ -185,7 +186,8 @@
// The following implements https://www.w3.org/TR/webauthn/#createCredential as of 5 December 2017.
// Step 1, 3, 13 are handled by the caller.
// Step 2.
- if (!sameOriginWithAncestors) {
+ // This implements https://www.w3.org/TR/webauthn-2/#sctn-permissions-policy except only same-site, cross-origin is permitted.
+ if (scope != WebAuthn::Scope::SameOrigin && !(scope == WebAuthn::Scope::SameSite && isFeaturePolicyAllowedByDocumentAndAllOwners(FeaturePolicy::Type::PublickeyCredentialsGetRule, document, LogFeaturePolicyFailure::No))) {
promise.reject(Exception { NotAllowedError, "The origin of the document is not the same as its ancestors."_s });
return;
}
@@ -219,7 +221,7 @@
}
// Step 10-12.
- auto clientDataJson = buildClientDataJson(ClientDataType::Get, options.challenge, callerOrigin);
+ auto clientDataJson = buildClientDataJson(ClientDataType::Get, options.challenge, callerOrigin, scope);
auto clientDataJsonHash = buildClientDataJsonHash(clientDataJson);
// Step 4, 14-19.
Modified: trunk/Source/WebCore/Modules/webauthn/AuthenticatorCoordinator.h (287115 => 287116)
--- trunk/Source/WebCore/Modules/webauthn/AuthenticatorCoordinator.h 2021-12-16 00:51:44 UTC (rev 287115)
+++ trunk/Source/WebCore/Modules/webauthn/AuthenticatorCoordinator.h 2021-12-16 00:52:57 UTC (rev 287116)
@@ -31,6 +31,10 @@
#include <wtf/Forward.h>
#include <wtf/Noncopyable.h>
+namespace WebAuthn {
+enum class Scope;
+}
+
namespace WebCore {
class AbortSignal;
@@ -53,8 +57,8 @@
WEBCORE_EXPORT void setClient(std::unique_ptr<AuthenticatorCoordinatorClient>&&);
// The following methods implement static methods of PublicKeyCredential.
- void create(const Document&, const PublicKeyCredentialCreationOptions&, bool sameOriginWithAncestors, RefPtr<AbortSignal>&&, CredentialPromise&&) const;
- void discoverFromExternalSource(const Document&, const PublicKeyCredentialRequestOptions&, bool sameOriginWithAncestors, RefPtr<AbortSignal>&&, CredentialPromise&&) const;
+ void create(const Document&, const PublicKeyCredentialCreationOptions&, WebAuthn::Scope, RefPtr<AbortSignal>&&, CredentialPromise&&) const;
+ void discoverFromExternalSource(const Document&, const PublicKeyCredentialRequestOptions&, WebAuthn::Scope, RefPtr<AbortSignal>&&, CredentialPromise&&) const;
void isUserVerifyingPlatformAuthenticatorAvailable(DOMPromiseDeferred<IDLBoolean>&&) const;
void resetUserGestureRequirement();
Modified: trunk/Source/WebCore/Modules/webauthn/WebAuthenticationConstants.h (287115 => 287116)
--- trunk/Source/WebCore/Modules/webauthn/WebAuthenticationConstants.h 2021-12-16 00:51:44 UTC (rev 287115)
+++ trunk/Source/WebCore/Modules/webauthn/WebAuthenticationConstants.h 2021-12-16 00:52:57 UTC (rev 287116)
@@ -80,3 +80,13 @@
const char LocalAuthenticatiorAccessGroup[] = "com.apple.webkit.webauthn";
} // namespace WebCore
+
+namespace WebAuthn {
+
+enum class Scope {
+ CrossOrigin,
+ SameOrigin,
+ SameSite
+};
+
+} // namespace WebAuthn
Modified: trunk/Source/WebCore/Modules/webauthn/WebAuthenticationUtils.cpp (287115 => 287116)
--- trunk/Source/WebCore/Modules/webauthn/WebAuthenticationUtils.cpp 2021-12-16 00:51:44 UTC (rev 287115)
+++ trunk/Source/WebCore/Modules/webauthn/WebAuthenticationUtils.cpp 2021-12-16 00:52:57 UTC (rev 287116)
@@ -134,7 +134,7 @@
}
// FIXME(181948): Add token binding ID.
-Ref<ArrayBuffer> buildClientDataJson(ClientDataType type, const BufferSource& challenge, const SecurityOrigin& origin)
+Ref<ArrayBuffer> buildClientDataJson(ClientDataType type, const BufferSource& challenge, const SecurityOrigin& origin, WebAuthn::Scope scope)
{
auto object = JSON::Object::create();
switch (type) {
@@ -147,6 +147,8 @@
}
object->setString("challenge"_s, base64URLEncodeToString(challenge.data(), challenge.length()));
object->setString("origin"_s, origin.toRawString());
+ if (scope != WebAuthn::Scope::SameOrigin)
+ object->setBoolean("crossOrigin"_s, scope != WebAuthn::Scope::SameOrigin);
auto utf8JSONString = object->toJSONString().utf8();
Modified: trunk/Source/WebCore/Modules/webauthn/WebAuthenticationUtils.h (287115 => 287116)
--- trunk/Source/WebCore/Modules/webauthn/WebAuthenticationUtils.h 2021-12-16 00:51:44 UTC (rev 287115)
+++ trunk/Source/WebCore/Modules/webauthn/WebAuthenticationUtils.h 2021-12-16 00:52:57 UTC (rev 287116)
@@ -52,7 +52,7 @@
// https://www.w3.org/TR/webauthn/#attestation-object
WEBCORE_EXPORT Vector<uint8_t> buildAttestationObject(Vector<uint8_t>&& authData, String&& format, cbor::CBORValue::MapValue&& statementMap, const AttestationConveyancePreference&);
-WEBCORE_EXPORT Ref<ArrayBuffer> buildClientDataJson(ClientDataType /*type*/, const BufferSource& challenge, const SecurityOrigin& /*origin*/);
+WEBCORE_EXPORT Ref<ArrayBuffer> buildClientDataJson(ClientDataType /*type*/, const BufferSource& challenge, const SecurityOrigin& /*origin*/, WebAuthn::Scope);
WEBCORE_EXPORT Vector<uint8_t> buildClientDataJsonHash(const ArrayBuffer& clientDataJson);
Modified: trunk/Source/WebCore/html/FeaturePolicy.cpp (287115 => 287116)
--- trunk/Source/WebCore/html/FeaturePolicy.cpp 2021-12-16 00:51:44 UTC (rev 287115)
+++ trunk/Source/WebCore/html/FeaturePolicy.cpp 2021-12-16 00:52:57 UTC (rev 287116)
@@ -67,6 +67,10 @@
case FeaturePolicy::Type::Magnetometer:
return "Magnetometer";
#endif
+#if ENABLE(WEB_AUTHN)
+ case FeaturePolicy::Type::PublickeyCredentialsGetRule:
+ return "PublickeyCredentialsGet";
+#endif
#if ENABLE(WEBXR)
case FeaturePolicy::Type::XRSpatialTracking:
return "XRSpatialTracking";
@@ -184,6 +188,9 @@
bool isAccelerometerInitialized = false;
bool isMagnetometerInitialized = false;
#endif
+#if ENABLE(WEB_AUTHN)
+ bool isPublickeyCredentialsGetInitialized = false;
+#endif
#if ENABLE(WEBXR)
bool isXRSpatialTrackingInitialized = false;
#endif
@@ -251,6 +258,13 @@
continue;
}
#endif
+#if ENABLE(WEB_AUTHN)
+ if (item.startsWith("publickey-credentials-get")) {
+ isPublickeyCredentialsGetInitialized = true;
+ updateList(document, policy.m_publickeyCredentialsGetRule, item.substring(26));
+ continue;
+ }
+#endif
#if ENABLE(WEBXR)
if (item.startsWith("xr-spatial-tracking")) {
isXRSpatialTrackingInitialized = true;
@@ -283,6 +297,10 @@
if (!isMagnetometerInitialized)
policy.m_magnetometerRule.allowedList.add(document.securityOrigin().data());
#endif
+#if ENABLE(WEB_AUTHN)
+ if (!isPublickeyCredentialsGetInitialized)
+ policy.m_publickeyCredentialsGetRule.allowedList.add(document.securityOrigin().data());
+#endif
#if ENABLE(WEBXR)
if (!isXRSpatialTrackingInitialized)
policy.m_xrSpatialTrackingRule.allowedList.add(document.securityOrigin().data());
@@ -338,6 +356,10 @@
case Type::Magnetometer:
return isAllowedByFeaturePolicy(m_magnetometerRule, origin);
#endif
+#if ENABLE(WEB_AUTHN)
+ case Type::PublickeyCredentialsGetRule:
+ return isAllowedByFeaturePolicy(m_publickeyCredentialsGetRule, origin);
+#endif
#if ENABLE(WEBXR)
case Type::XRSpatialTracking:
return isAllowedByFeaturePolicy(m_xrSpatialTrackingRule, origin);
Modified: trunk/Source/WebCore/html/FeaturePolicy.h (287115 => 287116)
--- trunk/Source/WebCore/html/FeaturePolicy.h 2021-12-16 00:51:44 UTC (rev 287115)
+++ trunk/Source/WebCore/html/FeaturePolicy.h 2021-12-16 00:52:57 UTC (rev 287116)
@@ -53,6 +53,9 @@
Accelerometer,
Magnetometer,
#endif
+#if ENABLE(WEB_AUTHN)
+ PublickeyCredentialsGetRule,
+#endif
#if ENABLE(WEBXR)
XRSpatialTracking,
#endif
@@ -81,6 +84,9 @@
AllowRule m_accelerometerRule;
AllowRule m_magnetometerRule;
#endif
+#if ENABLE(WEB_AUTHN)
+ AllowRule m_publickeyCredentialsGetRule;
+#endif
#if ENABLE(WEBXR)
AllowRule m_xrSpatialTrackingRule;
#endif
Modified: trunk/Source/WebKit/ChangeLog (287115 => 287116)
--- trunk/Source/WebKit/ChangeLog 2021-12-16 00:51:44 UTC (rev 287115)
+++ trunk/Source/WebKit/ChangeLog 2021-12-16 00:52:57 UTC (rev 287116)
@@ -1,3 +1,30 @@
+2021-12-15 J Pascoe <j_pas...@apple.com>
+
+ [WebAuthn] Allow same-site, cross-origin iframe get()
+ https://bugs.webkit.org/show_bug.cgi?id=234309
+ rdar://problem/86486313
+
+ Reviewed by Brent Fulgham.
+
+ The Web Authentication level 2 specifies a feature policy to allow get calls in
+ cross-origin i-frames. This patch implements this feature policy partially. Only
+ same-site, cross-origin i-frames are supported instead. This is for tracking prevention
+ purposes. https://w3c.github.io/webauthn/#sctn-iframe-guidance
+
+ This patch also starts passing ClientDataJSON hashes to ASC to avoid the situation
+ where WebKit includes crossOrigin or other fields in ClientDataJSON that ASC is
+ unaware of when generating ClientDataJSON.
+
+ Added layout test cases for same-site, cross-origin get calls.
+
+ * Platform/spi/Cocoa/AuthenticationServicesCoreSPI.h:
+ * UIProcess/API/Cocoa/_WKWebAuthenticationPanel.mm:
+ (produceClientDataJson):
+ * UIProcess/WebAuthentication/Cocoa/WebAuthenticatorCoordinatorProxy.mm:
+ (WebKit::configureRegistrationRequestContext):
+ (WebKit::configurationAssertionRequestContext):
+ (WebKit::WebAuthenticatorCoordinatorProxy::contextForRequest):
+
2021-12-15 Alex Christensen <achristen...@webkit.org>
Remove ProxyServer
Modified: trunk/Source/WebKit/Platform/spi/Cocoa/AuthenticationServicesCoreSPI.h (287115 => 287116)
--- trunk/Source/WebKit/Platform/spi/Cocoa/AuthenticationServicesCoreSPI.h 2021-12-16 00:51:44 UTC (rev 287115)
+++ trunk/Source/WebKit/Platform/spi/Cocoa/AuthenticationServicesCoreSPI.h 2021-12-16 00:52:57 UTC (rev 287116)
@@ -25,10 +25,7 @@
#pragma once
-#if HAVE(UNIFIED_ASC_AUTH_UI)
-#import <AuthenticationServicesCore/AuthenticationServicesCore.h>
-#import <AuthenticationServicesCore/AuthenticationServicesCorePrivate.h>
-#elif HAVE(ASC_AUTH_UI)
+#if HAVE(ASC_AUTH_UI) || HAVE(UNIFIED_ASC_AUTH_UI)
NS_ASSUME_NONNULL_BEGIN
@@ -71,6 +68,14 @@
@end
+@interface ASCAuthorizationRemotePresenter : NSObject
+
+#if TARGET_OS_OSX
+- (void)presentWithWindow:(NSWindow *)window daemonEndpoint:(NSXPCListenerEndpoint *)daemonEndpoint completionHandler:(void (^)(id <ASCCredentialProtocol>, NSError *))completionHandler;
+#endif
+
+@end
+
@class ASCCredentialRequestContext;
extern NSString * const ASCAuthorizationPresentationContextDataKey;
@@ -105,16 +110,65 @@
ASCSecurityKeyPublicKeyCredentialKindAssertionPlaceholder,
};
+@class ASCPublicKeyCredentialDescriptor;
+
+@interface ASCPublicKeyCredentialDescriptor : NSObject <NSSecureCoding>
+
+- (instancetype)initWithCredentialID:(NSData *)credentialID transports:(nullable NSArray<NSString *> *)allowedTransports;
+
+@property (nonatomic, readonly) NSData *credentialID;
+@property (nonatomic, nullable, readonly) NSArray<NSString *> *transports;
+
+@end
+
+@class ASCPublicKeyCredentialDescriptor;
+
+typedef NS_ENUM(NSUInteger, ASCPublicKeyCredentialKind) {
+ ASCPublicKeyCredentialKindPlatform = 1,
+ ASCPublicKeyCredentialKindSecurityKey,
+};
+
+@interface ASCPublicKeyCredentialAssertionOptions : NSObject <NSSecureCoding>
+
+- (instancetype)initWithKind:(ASCPublicKeyCredentialKind)credentialKind relyingPartyIdentifier:(NSString *)relyingPartyIdentifier challenge:(NSData *)challenge userVerificationPreference:(nullable NSString *)userVerificationPreference allowedCredentials:(nullable NSArray<ASCPublicKeyCredentialDescriptor *> *)allowedCredentials;
+
+- (instancetype)initWithKind:(ASCPublicKeyCredentialKind)credentialKind relyingPartyIdentifier:(NSString *)relyingPartyIdentifier clientDataHash:(NSData *)clientDataHash userVerificationPreference:(nullable NSString *)userVerificationPreference allowedCredentials:(nullable NSArray<ASCPublicKeyCredentialDescriptor *> *)allowedCredentials;
+
+@property (nonatomic, readonly) ASCPublicKeyCredentialKind credentialKind;
+@property (nonatomic, copy, readonly) NSString *relyingPartyIdentifier;
+@property (nonatomic, nullable, copy, readonly) NSData *challenge;
+// If clientDataHash is null, then gets generated from challenge and relyingPartyIdentifier.
+@property (nonatomic, nullable, copy) NSData *clientDataHash;
+@property (nonatomic, nullable, readonly, copy) NSString *userVerificationPreference;
+
+@property (nonatomic, nullable, readonly, copy) NSArray<ASCPublicKeyCredentialDescriptor *> *allowedCredentials;
+
+@end
+
+
+typedef NS_OPTIONS(NSUInteger, ASCCredentialRequestTypes) {
+ ASCCredentialRequestTypeNone = 0,
+ ASCCredentialRequestTypePassword = 1 << 0,
+ ASCCredentialRequestTypeAppleID = 1 << 1,
+ ASCCredentialRequestTypePlatformPublicKeyRegistration = 1 << 2,
+ ASCCredentialRequestTypePlatformPublicKeyAssertion = 1 << 3,
+ ASCCredentialRequestTypeSecurityKeyPublicKeyRegistration = 1 << 4,
+ ASCCredentialRequestTypeSecurityKeyPublicKeyAssertion = 1 << 5,
+};
+
@interface ASCPublicKeyCredentialCreationOptions : NSObject <NSSecureCoding>
-@property (nonatomic, copy) NSData *challenge;
+@property (nonatomic, nullable, copy) NSData *challenge;
+@property (nonatomic, nullable, copy) NSData *clientDataHash;
@property (nonatomic, copy) NSString *relyingPartyIdentifier;
@property (nonatomic, copy) NSString *userName;
@property (nonatomic, copy) NSData *userIdentifier;
@property (nonatomic, copy) NSString *userDisplayName;
@property (nonatomic, copy) NSArray<NSNumber *> *supportedAlgorithmIdentifiers;
+@property (nonatomic, nullable, copy) NSString *userVerificationPreference;
@property (nonatomic) BOOL shouldRequireResidentKey;
+@property (nonatomic, copy) NSArray<ASCPublicKeyCredentialDescriptor *> *excludedCredentials;
@end
@@ -149,10 +203,115 @@
@end
+@interface ASCCredentialRequestContext : NSObject <NSSecureCoding>
+
+- (instancetype)init NS_UNAVAILABLE;
++ (instancetype)new NS_UNAVAILABLE;
+
+- (instancetype)initWithRequestTypes:(ASCCredentialRequestTypes)requestTypes;
+
+@property (nonatomic, readonly) NSUInteger requestTypes;
+@property (nonatomic, nullable, copy) NSString *relyingPartyIdentifier;
+
+@property (nonatomic, nullable, copy) ASCPublicKeyCredentialCreationOptions *platformKeyCredentialCreationOptions;
+@property (nonatomic, nullable, copy) ASCPublicKeyCredentialCreationOptions *securityKeyCredentialCreationOptions;
+
+@property (nonatomic, nullable, copy) ASCPublicKeyCredentialAssertionOptions *platformKeyCredentialAssertionOptions;
+@property (nonatomic, nullable, copy) ASCPublicKeyCredentialAssertionOptions *securityKeyCredentialAssertionOptions;
+
+@end
+
@protocol ASCCredentialProtocol <NSObject, NSSecureCoding>
@end
+@class _WKAuthenticatorAttestationResponse;
+
+@interface ASCPlatformPublicKeyCredentialRegistration : NSObject <ASCCredentialProtocol>
+
+- (instancetype)initWithRelyingPartyIdentifier:(NSString *)relyingPartyIdentifier attestationObject:(NSData *)attestationObject rawClientDataJSON:(NSData *)rawClientDataJSON credentialID:(NSData *)credentialID;
+
+- (instancetype)initWithRelyingPartyIdentifier:(NSString *)relyingPartyIdentifier authenticatorAttestationResponse:(_WKAuthenticatorAttestationResponse *)attestationResponse rawClientDataJSON:(NSData *)rawClientDataJSON;
+
+@property (nonatomic, copy, readonly) NSData *credentialID;
+@property (nonatomic, copy, readonly) NSString *relyingPartyIdentifier;
+@property (nonatomic, copy, readonly) NSData *attestationObject;
+@property (nonatomic, copy, readonly) NSData *rawClientDataJSON;
+
++ (instancetype)new NS_UNAVAILABLE;
+- (instancetype)init NS_UNAVAILABLE;
+
+@end
+
+@interface ASCSecurityKeyPublicKeyCredentialRegistration : NSObject <ASCCredentialProtocol>
+
+- (instancetype)initWithRelyingPartyIdentifier:(NSString *)relyingPartyIdentifier authenticatorAttestationResponse:(_WKAuthenticatorAttestationResponse *)attestationResponse;
+
+@property (nonatomic, copy, readonly) NSData *credentialID;
+@property (nonatomic, copy, readonly) NSData *rawClientDataJSON;
+@property (nonatomic, copy, readonly) NSString *relyingPartyIdentifier;
+@property (nonatomic, copy, readonly) NSData *attestationObject;
+
+@end
+
+@class _WKAuthenticatorAssertionResponse;
+
+@interface ASCPlatformPublicKeyCredentialAssertion : NSObject <ASCCredentialProtocol>
+
+- (instancetype)initWithRelyingPartyIdentifier:(NSString *)relyingPartyIdentifier authenticatorAssertionResponse:(_WKAuthenticatorAssertionResponse *)assertionResponse;
+
+@property (nonatomic, copy, readonly) NSData *credentialID;
+@property (nonatomic, copy, readonly) NSData *rawClientDataJSON;
+@property (nonatomic, copy, readonly) NSString *relyingPartyIdentifier;
+@property (nonatomic, copy, readonly) NSData *authenticatorData;
+@property (nonatomic, copy, readonly) NSData *signature;
+@property (nonatomic, copy, readonly, nullable) NSData *userHandle;
+
+@end
+
+@interface ASCSecurityKeyPublicKeyCredentialAssertion : NSObject <ASCCredentialProtocol>
+
+- (instancetype)initWithRelyingPartyIdentifier:(NSString *)relyingPartyIdentifier authenticatorAssertionResponse:(_WKAuthenticatorAssertionResponse *)assertionResponse;
+
+@property (nonatomic, copy, readonly) NSData *credentialID;
+@property (nonatomic, copy, readonly) NSString *relyingPartyIdentifier;
+@property (nonatomic, copy, readonly) NSData *authenticatorData;
+@property (nonatomic, copy, readonly) NSData *signature;
+@property (nonatomic, copy, readonly, nullable) NSData *userHandle;
+@property (nonatomic, copy, readonly) NSData *rawClientDataJSON;
+
+@end
+
+@protocol ASCAgentProtocol <NSObject>
+
+#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
+- (void)performAuthorizationRequestsForContext:(ASCCredentialRequestContext *)context withCompletionHandler:(void (^)(id <ASCCredentialProtocol> _Nullable credential, NSError * _Nullable error))completionHandler;
+#elif TARGET_OS_OSX || TARGET_OS_MACCATALYST
+- (void)performAuthorizationRequestsForContext:(ASCCredentialRequestContext *)context withClearanceHandler:(void (^)(NSXPCListenerEndpoint * _Nullable daemonEndpoint, NSError * _Nullable error))clearanceHandler;
+
+- (void)beginAuthorizationForApplicationIdentifier:(NSString *)applicationIdentifier fromEndpoint:(NSXPCListenerEndpoint *)listenerEndpoint;
+
+- (void)userSelectedLoginChoice:(id <ASCLoginChoiceProtocol>)loginChoice authenticatedContext:(LAContext *)context completionHandler:(void (^)(id <ASCCredentialProtocol> _Nullable, NSError * _Nullable))completionHandler;
+
+- (void)requestCompletedWithCredential:(nullable id<ASCCredentialProtocol>)credential error:(nullable NSError *)error;
+#endif
+
+@end
+
+@interface ASCAgentProxy : NSObject <ASCAgentProtocol>
+
+- (instancetype)init;
+
+#if TARGET_OS_OSX
+- (instancetype)initWithEndpoint:(NSXPCListenerEndpoint *)endpoint;
+#endif
+
+- (void)invalidate;
+
+- (void)reconnectIfNecessary;
+
+@end
+
// FIXME(219767): Remove ASCAppleIDCredential.
@interface ASCAppleIDCredential : NSObject <ASCCredentialProtocol>
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebAuthenticationPanel.mm (287115 => 287116)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebAuthenticationPanel.mm 2021-12-16 00:51:44 UTC (rev 287115)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/_WKWebAuthenticationPanel.mm 2021-12-16 00:52:57 UTC (rev 287116)
@@ -87,7 +87,7 @@
auto challengeBuffer = ArrayBuffer::tryCreate(reinterpret_cast<const uint8_t*>(challenge.bytes), challenge.length);
auto securityOrigin = WebCore::SecurityOrigin::createFromString(origin);
- auto clientDataJson = buildClientDataJson(clientDataType, WebCore::BufferSource(challengeBuffer), securityOrigin);
+ auto clientDataJson = buildClientDataJson(clientDataType, WebCore::BufferSource(challengeBuffer), securityOrigin, WebAuthn::Scope::SameOrigin);
return adoptNS([[NSData alloc] initWithBytes:clientDataJson->data() length:clientDataJson->byteLength()]);
}
Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/WebAuthenticatorCoordinatorProxy.mm (287115 => 287116)
--- trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/WebAuthenticatorCoordinatorProxy.mm 2021-12-16 00:51:44 UTC (rev 287115)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/WebAuthenticatorCoordinatorProxy.mm 2021-12-16 00:52:57 UTC (rev 287116)
@@ -50,6 +50,11 @@
return ArrayBuffer::create(reinterpret_cast<const uint8_t*>(data.bytes), data.length);
}
+static inline RetainPtr<NSData> toNSData(const Vector<uint8_t>& data)
+{
+ return adoptNS([[NSData alloc] initWithBytes:data.data() length:data.size()]);
+}
+
static inline RetainPtr<NSString> toNSString(UserVerificationRequirement userVerificationRequirement)
{
switch (userVerificationRequirement) {
@@ -145,7 +150,7 @@
return adoptNS([allocASCPublicKeyCredentialDescriptorInstance() initWithCredentialID:WebCore::toNSData(descriptor.id).get() transports:transports.get()]);
}
-static RetainPtr<ASCCredentialRequestContext> configureRegistrationRequestContext(const PublicKeyCredentialCreationOptions& options)
+static RetainPtr<ASCCredentialRequestContext> configureRegistrationRequestContext(const PublicKeyCredentialCreationOptions& options, Vector<uint8_t> hash)
{
ASCCredentialRequestTypes requestTypes = ASCCredentialRequestTypePlatformPublicKeyRegistration | ASCCredentialRequestTypeSecurityKeyPublicKeyRegistration;
@@ -169,7 +174,10 @@
auto credentialCreationOptions = adoptNS([allocASCPublicKeyCredentialCreationOptionsInstance() init]);
- [credentialCreationOptions setChallenge:WebCore::toNSData(options.challenge).get()];
+ if ([credentialCreationOptions respondsToSelector:@selector(setClientDataHash:)])
+ [credentialCreationOptions setClientDataHash:toNSData(hash).get()];
+ else
+ [credentialCreationOptions setChallenge:WebCore::toNSData(options.challenge).get()];
[credentialCreationOptions setRelyingPartyIdentifier:options.rp.id];
[credentialCreationOptions setUserName:options.user.name];
[credentialCreationOptions setUserIdentifier:WebCore::toNSData(options.user.id).get()];
@@ -202,7 +210,7 @@
return requestContext;
}
-static RetainPtr<ASCCredentialRequestContext> configurationAssertionRequestContext(const PublicKeyCredentialRequestOptions& options)
+static RetainPtr<ASCCredentialRequestContext> configurationAssertionRequestContext(const PublicKeyCredentialRequestOptions& options, Vector<uint8_t> hash)
{
ASCCredentialRequestTypes requestTypes = ASCCredentialRequestTypePlatformPublicKeyAssertion | ASCCredentialRequestTypeSecurityKeyPublicKeyAssertion;
@@ -227,13 +235,30 @@
auto requestContext = adoptNS([allocASCCredentialRequestContextInstance() initWithRequestTypes:requestTypes]);
[requestContext setRelyingPartyIdentifier:options.rpId];
- auto challenge = WebCore::toNSData(options.challenge);
+ if (requestTypes & ASCCredentialRequestTypePlatformPublicKeyAssertion) {
+ auto assertionOptions = adoptNS(allocASCPublicKeyCredentialAssertionOptionsInstance());
+ if ([assertionOptions respondsToSelector:@selector(initWithKind:relyingPartyIdentifier:clientDataHash:userVerificationPreference:allowedCredentials:)]) {
+ auto nsHash = toNSData(hash);
+ [assertionOptions initWithKind:ASCPublicKeyCredentialKindPlatform relyingPartyIdentifier:options.rpId clientDataHash:nsHash.get() userVerificationPreference:userVerification.get() allowedCredentials:allowedCredentials.get()];
+ } else {
+ auto challenge = WebCore::toNSData(options.challenge);
+ [assertionOptions initWithKind:ASCPublicKeyCredentialKindPlatform relyingPartyIdentifier:options.rpId challenge:challenge.get() userVerificationPreference:userVerification.get() allowedCredentials:allowedCredentials.get()];
+ }
- if (requestTypes & ASCCredentialRequestTypePlatformPublicKeyAssertion)
- [requestContext setPlatformKeyCredentialAssertionOptions:[allocASCPublicKeyCredentialAssertionOptionsInstance() initWithKind:ASCPublicKeyCredentialKindPlatform relyingPartyIdentifier:options.rpId challenge:challenge.get() userVerificationPreference:userVerification.get() allowedCredentials:allowedCredentials.get()]];
+ [requestContext setPlatformKeyCredentialAssertionOptions:assertionOptions.get()];
+ }
- if (requestTypes & ASCCredentialRequestTypeSecurityKeyPublicKeyAssertion)
- [requestContext setSecurityKeyCredentialAssertionOptions:[allocASCPublicKeyCredentialAssertionOptionsInstance() initWithKind:ASCPublicKeyCredentialKindSecurityKey relyingPartyIdentifier:options.rpId challenge:challenge.get() userVerificationPreference:userVerification.get() allowedCredentials:allowedCredentials.get()]];
+ if (requestTypes & ASCCredentialRequestTypeSecurityKeyPublicKeyAssertion) {
+ auto assertionOptions = adoptNS(allocASCPublicKeyCredentialAssertionOptionsInstance());
+ if ([assertionOptions respondsToSelector:@selector(initWithKind:relyingPartyIdentifier:clientDataHash:userVerificationPreference:allowedCredentials:)]) {
+ auto nsHash = toNSData(hash);
+ [assertionOptions initWithKind:ASCPublicKeyCredentialKindSecurityKey relyingPartyIdentifier:options.rpId clientDataHash:nsHash.get() userVerificationPreference:userVerification.get() allowedCredentials:allowedCredentials.get()];
+ } else {
+ auto challenge = WebCore::toNSData(options.challenge);
+ [assertionOptions initWithKind:ASCPublicKeyCredentialKindSecurityKey relyingPartyIdentifier:options.rpId challenge:challenge.get() userVerificationPreference:userVerification.get() allowedCredentials:allowedCredentials.get()];
+ }
+ [requestContext setSecurityKeyCredentialAssertionOptions:assertionOptions.get()];
+ }
return requestContext;
}
@@ -242,9 +267,9 @@
{
RetainPtr<ASCCredentialRequestContext> result;
WTF::switchOn(requestData.options, [&](const PublicKeyCredentialCreationOptions& options) {
- result = configureRegistrationRequestContext(options);
+ result = configureRegistrationRequestContext(options, requestData.hash);
}, [&](const PublicKeyCredentialRequestOptions& options) {
- result = configurationAssertionRequestContext(options);
+ result = configurationAssertionRequestContext(options, requestData.hash);
});
return result;
}