Index: third_party/WebKit/Source/modules/webauth/WebAuthentication.cpp |
diff --git a/third_party/WebKit/Source/modules/webauth/WebAuthentication.cpp b/third_party/WebKit/Source/modules/webauth/WebAuthentication.cpp |
index 06cba2c2be2f6b4296dedf8ac97fc645435b2f85..b2f17e4469c8efebfaf4a79936e87f8556f3a3f6 100644 |
--- a/third_party/WebKit/Source/modules/webauth/WebAuthentication.cpp |
+++ b/third_party/WebKit/Source/modules/webauth/WebAuthentication.cpp |
@@ -1,4 +1,4 @@ |
-// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Copyright 2017 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. |
@@ -20,23 +20,26 @@ |
namespace { |
const char kNoAuthenticatorError[] = "Authenticator unavailable."; |
+// Time to wait for an authenticator to successfully complete an operation. |
+const int kAdjustedTimeoutLowerInSeconds = 60; |
+const int kAdjustedTimeoutUpperInSeconds = 120; |
} // anonymous namespace |
namespace mojo { |
- |
using webauth::mojom::blink::RelyingPartyAccount; |
using webauth::mojom::blink::RelyingPartyAccountPtr; |
+using webauth::mojom::blink::AuthenticatorStatus; |
+using webauth::mojom::blink::ScopedCredentialDescriptor; |
using webauth::mojom::blink::ScopedCredentialOptions; |
using webauth::mojom::blink::ScopedCredentialOptionsPtr; |
using webauth::mojom::blink::ScopedCredentialParameters; |
using webauth::mojom::blink::ScopedCredentialParametersPtr; |
-using webauth::mojom::blink::ScopedCredentialDescriptor; |
using webauth::mojom::blink::ScopedCredentialType; |
using webauth::mojom::blink::Transport; |
// TODO(kpaulhamus): Make this a TypeConverter |
Vector<uint8_t> ConvertBufferSource(const blink::BufferSource& buffer) { |
- DCHECK(buffer.isNull()); |
+ DCHECK(!buffer.isNull()); |
Vector<uint8_t> vector; |
if (buffer.isArrayBuffer()) { |
vector.Append(static_cast<uint8_t*>(buffer.getAsArrayBuffer()->Data()), |
@@ -69,6 +72,7 @@ Transport ConvertTransport(const String& transport) { |
return Transport::USB; |
} |
+// TODO(kpaulhamus): Make this a TypeConverter |
RelyingPartyAccountPtr ConvertRelyingPartyAccount( |
const blink::RelyingPartyAccount& account_information, |
blink::ScriptPromiseResolver* resolver) { |
@@ -88,17 +92,32 @@ ScopedCredentialOptionsPtr ConvertScopedCredentialOptions( |
const blink::ScopedCredentialOptions options, |
blink::ScriptPromiseResolver* resolver) { |
auto mojo_options = ScopedCredentialOptions::New(); |
- mojo_options->timeout_seconds = options.timeoutSeconds(); |
- mojo_options->relying_party_id = options.rpId(); |
- |
- // Adds the excludeList members (which are ScopedCredentialDescriptors) |
- for (const auto& descriptor : options.excludeList()) { |
- auto mojo_descriptor = ScopedCredentialDescriptor::New(); |
- mojo_descriptor->type = ConvertScopedCredentialType(descriptor.type()); |
- mojo_descriptor->id = ConvertBufferSource(descriptor.id()); |
- for (const auto& transport : descriptor.transports()) |
- mojo_descriptor->transports.push_back(ConvertTransport(transport)); |
- mojo_options->exclude_list.push_back(std::move(mojo_descriptor)); |
+ if (options.hasRpId()) { |
+ // if rpID is missing, it will later be set to the origin of the page |
+ // in the secure browser process. |
+ mojo_options->relying_party_id = options.rpId(); |
+ } |
+ |
+ // Step 4 of https://w3c.github.io/webauthn/#createCredential |
+ int predicted_timeout = kAdjustedTimeoutLowerInSeconds; |
+ if (options.hasTimeoutSeconds()) { |
+ predicted_timeout = static_cast<int>(options.timeoutSeconds()); |
+ } |
+ |
+ mojo_options->adjusted_timeout = static_cast<double>( |
+ std::max(kAdjustedTimeoutLowerInSeconds, |
+ std::min(kAdjustedTimeoutUpperInSeconds, predicted_timeout))); |
+ |
+ if (options.hasExcludeList()) { |
+ // Adds the excludeList members (which are ScopedCredentialDescriptors) |
+ for (const auto& descriptor : options.excludeList()) { |
+ auto mojo_descriptor = ScopedCredentialDescriptor::New(); |
+ mojo_descriptor->type = ConvertScopedCredentialType(descriptor.type()); |
+ mojo_descriptor->id = ConvertBufferSource(descriptor.id()); |
+ for (const auto& transport : descriptor.transports()) |
+ mojo_descriptor->transports.push_back(ConvertTransport(transport)); |
+ mojo_options->exclude_list.push_back(std::move(mojo_descriptor)); |
+ } |
} |
// TODO(kpaulhamus): add AuthenticationExtensions; |
return mojo_options; |
@@ -113,18 +132,40 @@ ScopedCredentialParametersPtr ConvertScopedCredentialParameter( |
// TODO(kpaulhamus): add AlgorithmIdentifier |
return mojo_parameter; |
} |
+ |
+blink::DOMException* CreateExceptionFromStatus(AuthenticatorStatus status) { |
+ switch (status) { |
+ case AuthenticatorStatus::NOT_IMPLEMENTED: |
+ return blink::DOMException::Create(blink::kNotSupportedError, |
+ "Not implemented."); |
+ case AuthenticatorStatus::NOT_ALLOWED_ERROR: |
+ return blink::DOMException::Create(blink::kNotAllowedError, |
+ "Not allowed."); |
+ case AuthenticatorStatus::NOT_SUPPORTED_ERROR: |
+ return blink::DOMException::Create( |
+ blink::kNotSupportedError, |
+ "Parameters for this operation are not supported."); |
+ case AuthenticatorStatus::SECURITY_ERROR: |
+ return blink::DOMException::Create(blink::kSecurityError, |
+ "The operation was not allowed."); |
+ case AuthenticatorStatus::UNKNOWN_ERROR: |
+ return blink::DOMException::Create(blink::kUnknownError, |
+ "Request failed."); |
+ case AuthenticatorStatus::CANCELLED: |
+ return blink::DOMException::Create(blink::kNotAllowedError, |
+ "User canceled the operation."); |
+ case AuthenticatorStatus::SUCCESS: |
+ return nullptr; |
+ default: |
+ NOTREACHED(); |
+ return nullptr; |
+ } |
+} |
} // namespace mojo |
namespace blink { |
- |
WebAuthentication::WebAuthentication(LocalFrame& frame) |
- : ContextLifecycleObserver(frame.GetDocument()) { |
- frame.GetInterfaceProvider()->GetInterface( |
- mojo::MakeRequest(&authenticator_)); |
- authenticator_.set_connection_error_handler(ConvertToBaseCallback( |
- WTF::Bind(&WebAuthentication::OnAuthenticatorConnectionError, |
- WrapWeakPersistent(this)))); |
-} |
+ : ContextLifecycleObserver(frame.GetDocument()) {} |
WebAuthentication::~WebAuthentication() { |
// |authenticator_| may still be valid but there should be no more |
@@ -132,40 +173,36 @@ WebAuthentication::~WebAuthentication() { |
DCHECK(authenticator_requests_.IsEmpty()); |
} |
-void WebAuthentication::Dispose() {} |
- |
ScriptPromise WebAuthentication::makeCredential( |
ScriptState* script_state, |
const RelyingPartyAccount& account_information, |
const HeapVector<ScopedCredentialParameters> crypto_parameters, |
const BufferSource& attestation_challenge, |
ScopedCredentialOptions& options) { |
- if (!authenticator_) { |
- return ScriptPromise::RejectWithDOMException( |
- script_state, DOMException::Create(kNotSupportedError)); |
- } |
+ ScriptPromise promise = RejectIfNotSupported(script_state); |
+ if (!promise.IsEmpty()) |
+ return promise; |
ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); |
- ScriptPromise promise = resolver->Promise(); |
- // TODO(kpaulhamus) validate parameters according to spec |
- auto account = |
- mojo::ConvertRelyingPartyAccount(account_information, resolver); |
Vector<uint8_t> buffer = mojo::ConvertBufferSource(attestation_challenge); |
auto opts = mojo::ConvertScopedCredentialOptions(options, resolver); |
Vector<webauth::mojom::blink::ScopedCredentialParametersPtr> parameters; |
for (const auto& parameter : crypto_parameters) { |
- parameters.push_back( |
- mojo::ConvertScopedCredentialParameter(parameter, resolver)); |
+ if (parameter.hasType()) { |
+ parameters.push_back( |
+ mojo::ConvertScopedCredentialParameter(parameter, resolver)); |
+ } |
} |
- |
+ auto account = |
+ mojo::ConvertRelyingPartyAccount(account_information, resolver); |
authenticator_requests_.insert(resolver); |
authenticator_->MakeCredential( |
std::move(account), std::move(parameters), buffer, std::move(opts), |
- ConvertToBaseCallback(Bind(&WebAuthentication::OnMakeCredential, |
- WrapPersistent(this), |
- WrapPersistent(resolver)))); |
- return promise; |
+ ConvertToBaseCallback(WTF::Bind(&WebAuthentication::OnMakeCredential, |
+ WrapPersistent(this), |
+ WrapPersistent(resolver)))); |
+ return resolver->Promise(); |
} |
ScriptPromise WebAuthentication::getAssertion( |
@@ -177,44 +214,66 @@ ScriptPromise WebAuthentication::getAssertion( |
} |
void WebAuthentication::ContextDestroyed(ExecutionContext*) { |
- authenticator_.reset(); |
- authenticator_requests_.clear(); |
-} |
- |
-void WebAuthentication::OnAuthenticatorConnectionError() { |
- authenticator_.reset(); |
- for (ScriptPromiseResolver* resolver : authenticator_requests_) { |
- resolver->Reject( |
- DOMException::Create(kNotFoundError, kNoAuthenticatorError)); |
- } |
- authenticator_requests_.clear(); |
+ Cleanup(); |
} |
void WebAuthentication::OnMakeCredential( |
ScriptPromiseResolver* resolver, |
- Vector<webauth::mojom::blink::ScopedCredentialInfoPtr> credentials) { |
+ webauth::mojom::blink::AuthenticatorStatus status, |
+ webauth::mojom::blink::ScopedCredentialInfoPtr credential) { |
if (!MarkRequestComplete(resolver)) |
return; |
- HeapVector<Member<ScopedCredentialInfo>> scoped_credentials; |
- for (auto& credential : credentials) { |
- if (credential->client_data.IsEmpty() || |
- credential->attestation.IsEmpty()) { |
- resolver->Reject( |
- DOMException::Create(kNotFoundError, "No credentials returned.")); |
+ DOMException* error = mojo::CreateExceptionFromStatus(status); |
+ if (error) { |
+ DCHECK(!credential); |
+ resolver->Reject(error); |
+ Cleanup(); |
+ return; |
+ } |
+ |
+ if (credential->client_data.IsEmpty() || credential->attestation.IsEmpty()) { |
+ resolver->Reject( |
+ DOMException::Create(kNotFoundError, "No credential returned.")); |
+ return; |
+ } |
+ |
+ DOMArrayBuffer* clientDataBuffer = DOMArrayBuffer::Create( |
+ static_cast<void*>(&credential->client_data.front()), |
+ credential->client_data.size()); |
+ |
+ DOMArrayBuffer* attestationBuffer = DOMArrayBuffer::Create( |
+ static_cast<void*>(&credential->attestation.front()), |
+ credential->attestation.size()); |
+ |
+ ScopedCredentialInfo* scopedCredential = |
+ ScopedCredentialInfo::Create(clientDataBuffer, attestationBuffer); |
+ resolver->Resolve(scopedCredential); |
+} |
+ |
+ScriptPromise WebAuthentication::RejectIfNotSupported( |
+ ScriptState* script_state) { |
+ if (!authenticator_) { |
+ if (!GetFrame()) { |
+ return ScriptPromise::RejectWithDOMException( |
+ script_state, DOMException::Create(kNotSupportedError)); |
} |
- DOMArrayBuffer* client_data_buffer = DOMArrayBuffer::Create( |
- static_cast<void*>(&credential->client_data.front()), |
- credential->client_data.size()); |
+ GetFrame()->GetInterfaceProvider()->GetInterface( |
+ mojo::MakeRequest(&authenticator_)); |
- DOMArrayBuffer* attestation_buffer = DOMArrayBuffer::Create( |
- static_cast<void*>(&credential->attestation.front()), |
- credential->attestation.size()); |
+ authenticator_.set_connection_error_handler(ConvertToBaseCallback( |
+ WTF::Bind(&WebAuthentication::OnAuthenticatorConnectionError, |
+ WrapWeakPersistent(this)))); |
+ } |
+ return ScriptPromise(); |
+} |
- scoped_credentials.push_back( |
- ScopedCredentialInfo::Create(client_data_buffer, attestation_buffer)); |
+void WebAuthentication::OnAuthenticatorConnectionError() { |
+ for (ScriptPromiseResolver* resolver : authenticator_requests_) { |
+ resolver->Reject( |
+ DOMException::Create(kNotFoundError, kNoAuthenticatorError)); |
} |
- resolver->Resolve(); |
+ Cleanup(); |
} |
bool WebAuthentication::MarkRequestComplete(ScriptPromiseResolver* resolver) { |
@@ -230,4 +289,10 @@ DEFINE_TRACE(WebAuthentication) { |
ContextLifecycleObserver::Trace(visitor); |
} |
+// Clears the promise resolver, timer, and closes the Mojo connection. |
+void WebAuthentication::Cleanup() { |
+ authenticator_.reset(); |
+ authenticator_requests_.clear(); |
+} |
+ |
} // namespace blink |