Commit 6722a719 authored by Adam Langley's avatar Adam Langley Committed by Commit Bot

webauthn: implement getClientExtensionResults

The results of client extensions, after some discussion, have settled on
being a dictionary returned by a callback on the
|PublicKeyCredential|[1].

This change implements that callback and plumbs through the results of
the |appid| extension[2], since that's the only extension that we
currently implement.

[1] https://w3c.github.io/webauthn/#iface-pkcredential
[2] https://w3c.github.io/webauthn/#sctn-appid-extension

Bug: 818303
Change-Id: Ie7963229cc74fa209cb1c61d00e0ff63e361ace3
Reviewed-on: https://chromium-review.googlesource.com/946732Reviewed-by: default avatarRobert Sesek <rsesek@chromium.org>
Reviewed-by: default avatarPavel Feldman <pfeldman@chromium.org>
Reviewed-by: default avatarBalazs Engedy <engedy@chromium.org>
Commit-Queue: Adam Langley <agl@chromium.org>
Cr-Commit-Position: refs/heads/master@{#541257}
parent 80afff39
...@@ -220,7 +220,8 @@ CreateMakeCredentialResponse(const std::string& client_data_json, ...@@ -220,7 +220,8 @@ CreateMakeCredentialResponse(const std::string& client_data_json,
webauth::mojom::GetAssertionAuthenticatorResponsePtr CreateGetAssertionResponse( webauth::mojom::GetAssertionAuthenticatorResponsePtr CreateGetAssertionResponse(
const std::string& client_data_json, const std::string& client_data_json,
device::SignResponseData response_data) { device::SignResponseData response_data,
bool echo_appid_extension) {
auto response = webauth::mojom::GetAssertionAuthenticatorResponse::New(); auto response = webauth::mojom::GetAssertionAuthenticatorResponse::New();
auto common_info = webauth::mojom::CommonCredentialInfo::New(); auto common_info = webauth::mojom::CommonCredentialInfo::New();
common_info->client_data_json.assign(client_data_json.begin(), common_info->client_data_json.assign(client_data_json.begin(),
...@@ -231,6 +232,7 @@ webauth::mojom::GetAssertionAuthenticatorResponsePtr CreateGetAssertionResponse( ...@@ -231,6 +232,7 @@ webauth::mojom::GetAssertionAuthenticatorResponsePtr CreateGetAssertionResponse(
response->authenticator_data = response_data.GetAuthenticatorDataBytes(); response->authenticator_data = response_data.GetAuthenticatorDataBytes();
response->signature = response_data.signature(); response->signature = response_data.signature();
response->user_handle.emplace(); response->user_handle.emplace();
response->echo_appid_extension = echo_appid_extension;
return response; return response;
} }
...@@ -456,6 +458,8 @@ void AuthenticatorImpl::GetAssertion( ...@@ -456,6 +458,8 @@ void AuthenticatorImpl::GetAssertion(
} }
alternative_application_parameter = std::move(appid_hash); alternative_application_parameter = std::move(appid_hash);
// TODO(agl): needs a test once a suitable, mock U2F device exists.
echo_appid_extension_ = true;
} }
DCHECK(get_assertion_response_callback_.is_null()); DCHECK(get_assertion_response_callback_.is_null());
...@@ -581,7 +585,8 @@ void AuthenticatorImpl::OnSignResponse( ...@@ -581,7 +585,8 @@ void AuthenticatorImpl::OnSignResponse(
std::move(get_assertion_response_callback_), std::move(get_assertion_response_callback_),
webauth::mojom::AuthenticatorStatus::SUCCESS, webauth::mojom::AuthenticatorStatus::SUCCESS,
CreateGetAssertionResponse(std::move(client_data_json_), CreateGetAssertionResponse(std::move(client_data_json_),
std::move(*response_data))); std::move(*response_data),
echo_appid_extension_));
return; return;
} }
NOTREACHED(); NOTREACHED();
...@@ -625,6 +630,7 @@ void AuthenticatorImpl::Cleanup() { ...@@ -625,6 +630,7 @@ void AuthenticatorImpl::Cleanup() {
make_credential_response_callback_.Reset(); make_credential_response_callback_.Reset();
get_assertion_response_callback_.Reset(); get_assertion_response_callback_.Reset();
client_data_json_.clear(); client_data_json_.clear();
echo_appid_extension_ = false;
} }
} // namespace content } // namespace content
...@@ -137,6 +137,12 @@ class CONTENT_EXPORT AuthenticatorImpl : public webauth::mojom::Authenticator { ...@@ -137,6 +137,12 @@ class CONTENT_EXPORT AuthenticatorImpl : public webauth::mojom::Authenticator {
std::unique_ptr<base::OneShotTimer> timer_; std::unique_ptr<base::OneShotTimer> timer_;
RenderFrameHost* render_frame_host_; RenderFrameHost* render_frame_host_;
service_manager::Connector* connector_ = nullptr; service_manager::Connector* connector_ = nullptr;
// Whether or not a GetAssertion call should return a PublicKeyCredential
// instance whose getClientExtensionResults() method yields a
// AuthenticationExtensions dictionary that contains the `appid: true`
// extension output.
bool echo_appid_extension_ = false;
base::WeakPtrFactory<AuthenticatorImpl> weak_factory_; base::WeakPtrFactory<AuthenticatorImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(AuthenticatorImpl); DISALLOW_COPY_AND_ASSIGN(AuthenticatorImpl);
......
...@@ -10,7 +10,7 @@ PASS PublicKeyCredential interface: attribute rawId ...@@ -10,7 +10,7 @@ PASS PublicKeyCredential interface: attribute rawId
PASS Unscopable handled correctly for rawId property on PublicKeyCredential PASS Unscopable handled correctly for rawId property on PublicKeyCredential
PASS PublicKeyCredential interface: attribute response PASS PublicKeyCredential interface: attribute response
PASS Unscopable handled correctly for response property on PublicKeyCredential PASS Unscopable handled correctly for response property on PublicKeyCredential
FAIL PublicKeyCredential interface: operation getClientExtensionResults() assert_own_property: interface prototype object missing non-static operation expected property "getClientExtensionResults" missing PASS PublicKeyCredential interface: operation getClientExtensionResults()
PASS Unscopable handled correctly for getClientExtensionResults() on PublicKeyCredential PASS Unscopable handled correctly for getClientExtensionResults() on PublicKeyCredential
PASS PublicKeyCredential interface: operation isUserVerifyingPlatformAuthenticatorAvailable() PASS PublicKeyCredential interface: operation isUserVerifyingPlatformAuthenticatorAvailable()
PASS Unscopable handled correctly for isUserVerifyingPlatformAuthenticatorAvailable() on PublicKeyCredential PASS Unscopable handled correctly for isUserVerifyingPlatformAuthenticatorAvailable() on PublicKeyCredential
......
...@@ -5234,6 +5234,7 @@ interface PublicKeyCredential : Credential ...@@ -5234,6 +5234,7 @@ interface PublicKeyCredential : Credential
getter rawId getter rawId
getter response getter response
method constructor method constructor
method getClientExtensionResults
interface PushManager interface PushManager
static getter supportedContentEncodings static getter supportedContentEncodings
attribute @@toStringTag attribute @@toStringTag
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// https://w3c.github.io/webauthn/#dictdef-authenticationextensionsclientoutputs
dictionary AuthenticationExtensionsClientOutputs {
boolean appid;
};
...@@ -323,7 +323,8 @@ void OnMakePublicKeyCredentialComplete( ...@@ -323,7 +323,8 @@ void OnMakePublicKeyCredentialComplete(
AuthenticatorAttestationResponse::Create(client_data_buffer, AuthenticatorAttestationResponse::Create(client_data_buffer,
attestation_buffer); attestation_buffer);
resolver->Resolve(PublicKeyCredential::Create(credential->info->id, raw_id, resolver->Resolve(PublicKeyCredential::Create(credential->info->id, raw_id,
authenticator_response)); authenticator_response,
{} /* extensions_outputs */));
} else { } else {
DCHECK(!credential); DCHECK(!credential);
resolver->Reject(CredentialManagerErrorToDOMException( resolver->Reject(CredentialManagerErrorToDOMException(
...@@ -360,8 +361,11 @@ void OnGetAssertionComplete( ...@@ -360,8 +361,11 @@ void OnGetAssertionComplete(
AuthenticatorAssertionResponse::Create(client_data_buffer, AuthenticatorAssertionResponse::Create(client_data_buffer,
authenticator_buffer, authenticator_buffer,
signature_buffer, user_handle); signature_buffer, user_handle);
AuthenticationExtensionsClientOutputs extension_outputs;
extension_outputs.setAppid(credential->echo_appid_extension);
resolver->Resolve(PublicKeyCredential::Create(credential->info->id, raw_id, resolver->Resolve(PublicKeyCredential::Create(credential->info->id, raw_id,
authenticator_response)); authenticator_response,
extension_outputs));
} else { } else {
DCHECK(!credential); DCHECK(!credential);
resolver->Reject(CredentialManagerErrorToDOMException( resolver->Reject(CredentialManagerErrorToDOMException(
......
...@@ -20,16 +20,20 @@ constexpr char kPublicKeyCredentialType[] = "public-key"; ...@@ -20,16 +20,20 @@ constexpr char kPublicKeyCredentialType[] = "public-key";
PublicKeyCredential* PublicKeyCredential::Create( PublicKeyCredential* PublicKeyCredential::Create(
const String& id, const String& id,
DOMArrayBuffer* raw_id, DOMArrayBuffer* raw_id,
AuthenticatorResponse* response) { AuthenticatorResponse* response,
return new PublicKeyCredential(id, raw_id, response); const AuthenticationExtensionsClientOutputs& extension_outputs) {
return new PublicKeyCredential(id, raw_id, response, extension_outputs);
} }
PublicKeyCredential::PublicKeyCredential(const String& id, PublicKeyCredential::PublicKeyCredential(
const String& id,
DOMArrayBuffer* raw_id, DOMArrayBuffer* raw_id,
AuthenticatorResponse* response) AuthenticatorResponse* response,
const AuthenticationExtensionsClientOutputs& extension_outputs)
: Credential(id, kPublicKeyCredentialType), : Credential(id, kPublicKeyCredentialType),
raw_id_(raw_id), raw_id_(raw_id),
response_(response) {} response_(response),
extension_outputs_(extension_outputs) {}
ScriptPromise ScriptPromise
PublicKeyCredential::isUserVerifyingPlatformAuthenticatorAvailable( PublicKeyCredential::isUserVerifyingPlatformAuthenticatorAvailable(
...@@ -39,6 +43,11 @@ PublicKeyCredential::isUserVerifyingPlatformAuthenticatorAvailable( ...@@ -39,6 +43,11 @@ PublicKeyCredential::isUserVerifyingPlatformAuthenticatorAvailable(
DOMException::Create(kNotSupportedError, "Operation not implemented.")); DOMException::Create(kNotSupportedError, "Operation not implemented."));
} }
void PublicKeyCredential::getClientExtensionResults(
AuthenticationExtensionsClientOutputs& result) const {
result = extension_outputs_;
}
void PublicKeyCredential::Trace(blink::Visitor* visitor) { void PublicKeyCredential::Trace(blink::Visitor* visitor) {
visitor->Trace(raw_id_); visitor->Trace(raw_id_);
visitor->Trace(response_); visitor->Trace(response_);
......
...@@ -6,9 +6,10 @@ ...@@ -6,9 +6,10 @@
#define PublicKeyCredential_h #define PublicKeyCredential_h
#include "core/typed_arrays/DOMArrayBuffer.h" #include "core/typed_arrays/DOMArrayBuffer.h"
#include "modules/ModulesExport.h" #include "modules/credentialmanager/AuthenticationExtensionsClientOutputs.h"
#include "modules/credentialmanager/AuthenticatorResponse.h" #include "modules/credentialmanager/AuthenticatorResponse.h"
#include "modules/credentialmanager/Credential.h" #include "modules/credentialmanager/Credential.h"
#include "modules/ModulesExport.h"
#include "platform/heap/Handle.h" #include "platform/heap/Handle.h"
namespace blink { namespace blink {
...@@ -21,26 +22,32 @@ class MODULES_EXPORT PublicKeyCredential final : public Credential { ...@@ -21,26 +22,32 @@ class MODULES_EXPORT PublicKeyCredential final : public Credential {
DEFINE_WRAPPERTYPEINFO(); DEFINE_WRAPPERTYPEINFO();
public: public:
static PublicKeyCredential* Create(const String& id, static PublicKeyCredential* Create(
const String& id,
DOMArrayBuffer* raw_id, DOMArrayBuffer* raw_id,
AuthenticatorResponse*); AuthenticatorResponse*,
const AuthenticationExtensionsClientOutputs&);
DOMArrayBuffer* rawId() const { return raw_id_.Get(); } DOMArrayBuffer* rawId() const { return raw_id_.Get(); }
AuthenticatorResponse* response() const { return response_.Get(); } AuthenticatorResponse* response() const { return response_.Get(); }
static ScriptPromise isUserVerifyingPlatformAuthenticatorAvailable( static ScriptPromise isUserVerifyingPlatformAuthenticatorAvailable(
ScriptState*); ScriptState*);
void getClientExtensionResults(AuthenticationExtensionsClientOutputs&) const;
// Credential: // Credential:
void Trace(blink::Visitor*) override; void Trace(blink::Visitor*) override;
bool IsPublicKeyCredential() const override; bool IsPublicKeyCredential() const override;
private: private:
explicit PublicKeyCredential(const String& id, explicit PublicKeyCredential(
const String& id,
DOMArrayBuffer* raw_id, DOMArrayBuffer* raw_id,
AuthenticatorResponse*); AuthenticatorResponse*,
const AuthenticationExtensionsClientOutputs& extension_outputs);
const Member<DOMArrayBuffer> raw_id_; const Member<DOMArrayBuffer> raw_id_;
const Member<AuthenticatorResponse> response_; const Member<AuthenticatorResponse> response_;
AuthenticationExtensionsClientOutputs extension_outputs_;
}; };
} // namespace blink } // namespace blink
......
...@@ -12,5 +12,5 @@ ...@@ -12,5 +12,5 @@
[SameObject] readonly attribute ArrayBuffer rawId; [SameObject] readonly attribute ArrayBuffer rawId;
[SameObject] readonly attribute AuthenticatorResponse response; [SameObject] readonly attribute AuthenticatorResponse response;
[CallWith=ScriptState] static Promise <boolean> isUserVerifyingPlatformAuthenticatorAvailable(); [CallWith=ScriptState] static Promise <boolean> isUserVerifyingPlatformAuthenticatorAvailable();
// TODO(crbug.com/733033): Add extension support AuthenticationExtensionsClientOutputs getClientExtensionResults();
}; };
...@@ -464,6 +464,7 @@ modules_dictionary_idl_files = ...@@ -464,6 +464,7 @@ modules_dictionary_idl_files =
"cookie_store/CookieStoreGetOptions.idl", "cookie_store/CookieStoreGetOptions.idl",
"cookie_store/CookieStoreSetOptions.idl", "cookie_store/CookieStoreSetOptions.idl",
"credentialmanager/AuthenticationExtensionsClientInputs.idl", "credentialmanager/AuthenticationExtensionsClientInputs.idl",
"credentialmanager/AuthenticationExtensionsClientOutputs.idl",
"credentialmanager/AuthenticatorSelectionCriteria.idl", "credentialmanager/AuthenticatorSelectionCriteria.idl",
"credentialmanager/CollectedClientData.idl", "credentialmanager/CollectedClientData.idl",
"credentialmanager/CredentialCreationOptions.idl", "credentialmanager/CredentialCreationOptions.idl",
......
...@@ -57,6 +57,13 @@ struct GetAssertionAuthenticatorResponse { ...@@ -57,6 +57,13 @@ struct GetAssertionAuthenticatorResponse {
// Equivalent of the `user.id` passed into create(). // Equivalent of the `user.id` passed into create().
// Maximum 64 bytes. // Maximum 64 bytes.
array<uint8>? user_handle; array<uint8>? user_handle;
// True if getClientExtensionResults() called on the returned
// PublicKeyCredential instance should yield an
// AuthenticationExtensionsClientOutputs dictionary that contains the `appid:
// true` extension output, which indicates to the relying party that the
// `appid` extension was acted upon.
bool echo_appid_extension;
}; };
// Information about the relying party. These fields take arbitrary input. // Information about the relying party. These fields take arbitrary input.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment