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,
webauth::mojom::GetAssertionAuthenticatorResponsePtr CreateGetAssertionResponse(
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 common_info = webauth::mojom::CommonCredentialInfo::New();
common_info->client_data_json.assign(client_data_json.begin(),
......@@ -231,6 +232,7 @@ webauth::mojom::GetAssertionAuthenticatorResponsePtr CreateGetAssertionResponse(
response->authenticator_data = response_data.GetAuthenticatorDataBytes();
response->signature = response_data.signature();
response->user_handle.emplace();
response->echo_appid_extension = echo_appid_extension;
return response;
}
......@@ -456,6 +458,8 @@ void AuthenticatorImpl::GetAssertion(
}
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());
......@@ -581,7 +585,8 @@ void AuthenticatorImpl::OnSignResponse(
std::move(get_assertion_response_callback_),
webauth::mojom::AuthenticatorStatus::SUCCESS,
CreateGetAssertionResponse(std::move(client_data_json_),
std::move(*response_data)));
std::move(*response_data),
echo_appid_extension_));
return;
}
NOTREACHED();
......@@ -625,6 +630,7 @@ void AuthenticatorImpl::Cleanup() {
make_credential_response_callback_.Reset();
get_assertion_response_callback_.Reset();
client_data_json_.clear();
echo_appid_extension_ = false;
}
} // namespace content
......@@ -137,6 +137,12 @@ class CONTENT_EXPORT AuthenticatorImpl : public webauth::mojom::Authenticator {
std::unique_ptr<base::OneShotTimer> timer_;
RenderFrameHost* render_frame_host_;
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_;
DISALLOW_COPY_AND_ASSIGN(AuthenticatorImpl);
......
......@@ -10,7 +10,7 @@ PASS PublicKeyCredential interface: attribute rawId
PASS Unscopable handled correctly for rawId property on PublicKeyCredential
PASS PublicKeyCredential interface: attribute response
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 PublicKeyCredential interface: operation isUserVerifyingPlatformAuthenticatorAvailable()
PASS Unscopable handled correctly for isUserVerifyingPlatformAuthenticatorAvailable() on PublicKeyCredential
......
......@@ -5234,6 +5234,7 @@ interface PublicKeyCredential : Credential
getter rawId
getter response
method constructor
method getClientExtensionResults
interface PushManager
static getter supportedContentEncodings
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(
AuthenticatorAttestationResponse::Create(client_data_buffer,
attestation_buffer);
resolver->Resolve(PublicKeyCredential::Create(credential->info->id, raw_id,
authenticator_response));
authenticator_response,
{} /* extensions_outputs */));
} else {
DCHECK(!credential);
resolver->Reject(CredentialManagerErrorToDOMException(
......@@ -360,8 +361,11 @@ void OnGetAssertionComplete(
AuthenticatorAssertionResponse::Create(client_data_buffer,
authenticator_buffer,
signature_buffer, user_handle);
AuthenticationExtensionsClientOutputs extension_outputs;
extension_outputs.setAppid(credential->echo_appid_extension);
resolver->Resolve(PublicKeyCredential::Create(credential->info->id, raw_id,
authenticator_response));
authenticator_response,
extension_outputs));
} else {
DCHECK(!credential);
resolver->Reject(CredentialManagerErrorToDOMException(
......
......@@ -20,16 +20,20 @@ constexpr char kPublicKeyCredentialType[] = "public-key";
PublicKeyCredential* PublicKeyCredential::Create(
const String& id,
DOMArrayBuffer* raw_id,
AuthenticatorResponse* response) {
return new PublicKeyCredential(id, raw_id, response);
AuthenticatorResponse* response,
const AuthenticationExtensionsClientOutputs& extension_outputs) {
return new PublicKeyCredential(id, raw_id, response, extension_outputs);
}
PublicKeyCredential::PublicKeyCredential(const String& id,
DOMArrayBuffer* raw_id,
AuthenticatorResponse* response)
PublicKeyCredential::PublicKeyCredential(
const String& id,
DOMArrayBuffer* raw_id,
AuthenticatorResponse* response,
const AuthenticationExtensionsClientOutputs& extension_outputs)
: Credential(id, kPublicKeyCredentialType),
raw_id_(raw_id),
response_(response) {}
response_(response),
extension_outputs_(extension_outputs) {}
ScriptPromise
PublicKeyCredential::isUserVerifyingPlatformAuthenticatorAvailable(
......@@ -39,6 +43,11 @@ PublicKeyCredential::isUserVerifyingPlatformAuthenticatorAvailable(
DOMException::Create(kNotSupportedError, "Operation not implemented."));
}
void PublicKeyCredential::getClientExtensionResults(
AuthenticationExtensionsClientOutputs& result) const {
result = extension_outputs_;
}
void PublicKeyCredential::Trace(blink::Visitor* visitor) {
visitor->Trace(raw_id_);
visitor->Trace(response_);
......
......@@ -6,9 +6,10 @@
#define PublicKeyCredential_h
#include "core/typed_arrays/DOMArrayBuffer.h"
#include "modules/ModulesExport.h"
#include "modules/credentialmanager/AuthenticationExtensionsClientOutputs.h"
#include "modules/credentialmanager/AuthenticatorResponse.h"
#include "modules/credentialmanager/Credential.h"
#include "modules/ModulesExport.h"
#include "platform/heap/Handle.h"
namespace blink {
......@@ -21,26 +22,32 @@ class MODULES_EXPORT PublicKeyCredential final : public Credential {
DEFINE_WRAPPERTYPEINFO();
public:
static PublicKeyCredential* Create(const String& id,
DOMArrayBuffer* raw_id,
AuthenticatorResponse*);
static PublicKeyCredential* Create(
const String& id,
DOMArrayBuffer* raw_id,
AuthenticatorResponse*,
const AuthenticationExtensionsClientOutputs&);
DOMArrayBuffer* rawId() const { return raw_id_.Get(); }
AuthenticatorResponse* response() const { return response_.Get(); }
static ScriptPromise isUserVerifyingPlatformAuthenticatorAvailable(
ScriptState*);
void getClientExtensionResults(AuthenticationExtensionsClientOutputs&) const;
// Credential:
void Trace(blink::Visitor*) override;
bool IsPublicKeyCredential() const override;
private:
explicit PublicKeyCredential(const String& id,
DOMArrayBuffer* raw_id,
AuthenticatorResponse*);
explicit PublicKeyCredential(
const String& id,
DOMArrayBuffer* raw_id,
AuthenticatorResponse*,
const AuthenticationExtensionsClientOutputs& extension_outputs);
const Member<DOMArrayBuffer> raw_id_;
const Member<AuthenticatorResponse> response_;
AuthenticationExtensionsClientOutputs extension_outputs_;
};
} // namespace blink
......
......@@ -12,5 +12,5 @@
[SameObject] readonly attribute ArrayBuffer rawId;
[SameObject] readonly attribute AuthenticatorResponse response;
[CallWith=ScriptState] static Promise <boolean> isUserVerifyingPlatformAuthenticatorAvailable();
// TODO(crbug.com/733033): Add extension support
AuthenticationExtensionsClientOutputs getClientExtensionResults();
};
......@@ -464,6 +464,7 @@ modules_dictionary_idl_files =
"cookie_store/CookieStoreGetOptions.idl",
"cookie_store/CookieStoreSetOptions.idl",
"credentialmanager/AuthenticationExtensionsClientInputs.idl",
"credentialmanager/AuthenticationExtensionsClientOutputs.idl",
"credentialmanager/AuthenticatorSelectionCriteria.idl",
"credentialmanager/CollectedClientData.idl",
"credentialmanager/CredentialCreationOptions.idl",
......
......@@ -53,10 +53,17 @@ struct GetAssertionAuthenticatorResponse {
// Cryptographic signature proving possession of the credential private key.
array<uint8> signature;
// Only supported by CTAP devices, not by U2F devices.
// Equivalent of the `user.id` passed into create().
// Maximum 64 bytes.
array<uint8>? user_handle;
// Only supported by CTAP devices, not by U2F devices.
// Equivalent of the `user.id` passed into create().
// Maximum 64 bytes.
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.
......
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