| 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 140c96f245248264f0f1c16851c0683481d58528..993e936f898b1772372b20ed7e812df10aec842e 100644
|
| --- a/third_party/WebKit/Source/modules/webauth/WebAuthentication.cpp
|
| +++ b/third_party/WebKit/Source/modules/webauth/WebAuthentication.cpp
|
| @@ -17,20 +17,23 @@
|
|
|
| namespace {
|
| const char kNoAuthenticatorError[] = "Authenticator unavailable.";
|
| +// Time to wait for an authenticator to successfully complete an operation.
|
| +static const double adjustedTimeoutLower = 60;
|
| +static const double adjustedTimeoutUpper = 120;
|
| } // anonymous namespace
|
|
|
| namespace mojo {
|
| -
|
| +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;
|
|
|
| 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()),
|
| @@ -65,17 +68,33 @@ ScopedCredentialOptionsPtr convertScopedCredentialOptions(
|
| const blink::ScopedCredentialOptions options,
|
| blink::ScriptPromiseResolver* resolver) {
|
| auto mojoOptions = ScopedCredentialOptions::New();
|
| - mojoOptions->timeout_seconds = options.timeoutSeconds();
|
| - mojoOptions->rp_id = options.rpId();
|
| -
|
| - // Adds the excludeList members (which are ScopedCredentialDescriptors)
|
| - for (const auto& descriptor : options.excludeList()) {
|
| - auto mojoDescriptor = ScopedCredentialDescriptor::New();
|
| - mojoDescriptor->type = convertScopedCredentialType(descriptor.type());
|
| - mojoDescriptor->id = convertBufferSource(descriptor.id());
|
| - for (const auto& transport : descriptor.transports())
|
| - mojoDescriptor->transports.push_back(convertTransport(transport));
|
| - mojoOptions->exclude_list.push_back(std::move(mojoDescriptor));
|
| + if (options.hasRpId()) {
|
| + mojoOptions->rp_id = options.rpId();
|
| + }
|
| +
|
| + // Step 1 of https://w3c.github.io/webauthn/#makeCredential
|
| + if (options.hasTimeoutSeconds()) {
|
| + mojoOptions->adjusted_timeout =
|
| + static_cast<double>(options.timeoutSeconds());
|
| + if (mojoOptions->adjusted_timeout > adjustedTimeoutUpper) {
|
| + mojoOptions->adjusted_timeout = adjustedTimeoutUpper;
|
| + } else if (mojoOptions->adjusted_timeout < adjustedTimeoutLower) {
|
| + mojoOptions->adjusted_timeout = adjustedTimeoutLower;
|
| + }
|
| + } else {
|
| + mojoOptions->adjusted_timeout = adjustedTimeoutLower;
|
| + }
|
| +
|
| + if (options.hasExcludeList()) {
|
| + // Adds the excludeList members (which are ScopedCredentialDescriptors)
|
| + for (const auto& descriptor : options.excludeList()) {
|
| + auto mojoDescriptor = ScopedCredentialDescriptor::New();
|
| + mojoDescriptor->type = convertScopedCredentialType(descriptor.type());
|
| + mojoDescriptor->id = convertBufferSource(descriptor.id());
|
| + for (const auto& transport : descriptor.transports())
|
| + mojoDescriptor->transports.push_back(convertTransport(transport));
|
| + mojoOptions->exclude_list.push_back(std::move(mojoDescriptor));
|
| + }
|
| }
|
| // TODO (kpaulhamus) add AuthenticationExtensions;
|
| return mojoOptions;
|
| @@ -89,18 +108,38 @@ ScopedCredentialParametersPtr convertScopedCredentialParameter(
|
| // TODO (kpaulhamus) add AlgorithmIdentifier
|
| return mojoParameter;
|
| }
|
| +
|
| +blink::DOMException* createExceptionFromStatus(AuthenticatorStatus status) {
|
| + switch (status) {
|
| + 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(&m_authenticator));
|
| - m_authenticator.set_connection_error_handler(ConvertToBaseCallback(
|
| - WTF::Bind(&WebAuthentication::onAuthenticatorConnectionError,
|
| - WrapWeakPersistent(this))));
|
| -}
|
| + : ContextLifecycleObserver(frame.GetDocument()) {}
|
|
|
| WebAuthentication::~WebAuthentication() {
|
| // |m_authenticator| may still be valid but there should be no more
|
| @@ -116,38 +155,29 @@ ScriptPromise WebAuthentication::makeCredential(
|
| const HeapVector<ScopedCredentialParameters> crypto_parameters,
|
| const BufferSource& attestation_challenge,
|
| ScopedCredentialOptions& options) {
|
| - ExecutionContext* executionContext = script_state->GetExecutionContext();
|
| -
|
| - if (!m_authenticator) {
|
| - return ScriptPromise::RejectWithDOMException(
|
| - script_state, DOMException::Create(kNotSupportedError));
|
| - }
|
| -
|
| - String errorMessage;
|
| - if (!executionContext->IsSecureContext(errorMessage)) {
|
| - return ScriptPromise::RejectWithDOMException(
|
| - script_state, DOMException::Create(kSecurityError, errorMessage));
|
| - }
|
| + 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
|
| 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()) { // TODO add algorithm
|
| + parameters.push_back(
|
| + mojo::convertScopedCredentialParameter(parameter, resolver));
|
| + }
|
| }
|
|
|
| m_authenticatorRequests.insert(resolver);
|
| - m_authenticator->makeCredential(
|
| + m_authenticator->MakeCredential(
|
| account_information, std::move(parameters), buffer, std::move(opts),
|
| ConvertToBaseCallback(WTF::Bind(&WebAuthentication::onMakeCredential,
|
| WrapPersistent(this),
|
| WrapPersistent(resolver))));
|
| - return promise;
|
| + return resolver->Promise();
|
| }
|
|
|
| ScriptPromise WebAuthentication::getAssertion(
|
| @@ -159,45 +189,75 @@ ScriptPromise WebAuthentication::getAssertion(
|
| }
|
|
|
| void WebAuthentication::ContextDestroyed(ExecutionContext*) {
|
| - m_authenticator.reset();
|
| - m_authenticatorRequests.Clear();
|
| -}
|
| -
|
| -void WebAuthentication::onAuthenticatorConnectionError() {
|
| - m_authenticator.reset();
|
| - for (ScriptPromiseResolver* resolver : m_authenticatorRequests) {
|
| - resolver->Reject(
|
| - DOMException::Create(kNotFoundError, kNoAuthenticatorError));
|
| - }
|
| - m_authenticatorRequests.Clear();
|
| + cleanup();
|
| }
|
|
|
| +// Step 11 of https://w3c.github.io/webauthn/#makeCredential
|
| 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>> scopedCredentials;
|
| - 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) {
|
| + 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) {
|
| + ExecutionContext* executionContext = script_state->GetExecutionContext();
|
| +
|
| + if (!m_authenticator) {
|
| + if (!GetFrame()) {
|
| + return ScriptPromise::RejectWithDOMException(
|
| + script_state, DOMException::Create(kNotSupportedError));
|
| }
|
| - DOMArrayBuffer* clientDataBuffer = DOMArrayBuffer::Create(
|
| - static_cast<void*>(&credential->client_data.front()),
|
| - credential->client_data.size());
|
| + GetFrame()->GetInterfaceProvider()->GetInterface(
|
| + mojo::MakeRequest(&m_authenticator));
|
| +
|
| + m_authenticator.set_connection_error_handler(ConvertToBaseCallback(
|
| + WTF::Bind(&WebAuthentication::onAuthenticatorConnectionError,
|
| + WrapWeakPersistent(this))));
|
| + }
|
|
|
| - DOMArrayBuffer* attestationBuffer = DOMArrayBuffer::Create(
|
| - static_cast<void*>(&credential->attestation.front()),
|
| - credential->attestation.size());
|
| + String errorMessage;
|
| + if (!executionContext->IsSecureContext(errorMessage)) {
|
| + return ScriptPromise::RejectWithDOMException(
|
| + script_state, DOMException::Create(kSecurityError, errorMessage));
|
| + }
|
| +
|
| + return ScriptPromise();
|
| +}
|
|
|
| - scopedCredentials.push_back(
|
| - ScopedCredentialInfo::Create(clientDataBuffer, attestationBuffer));
|
| +void WebAuthentication::onAuthenticatorConnectionError() {
|
| + for (ScriptPromiseResolver* resolver : m_authenticatorRequests) {
|
| + resolver->Reject(
|
| + DOMException::Create(kNotFoundError, kNoAuthenticatorError));
|
| }
|
| - resolver->Resolve(scopedCredentials);
|
| - m_authenticatorRequests.erase(resolver);
|
| + cleanup();
|
| }
|
|
|
| bool WebAuthentication::markRequestComplete(ScriptPromiseResolver* resolver) {
|
| @@ -208,6 +268,12 @@ bool WebAuthentication::markRequestComplete(ScriptPromiseResolver* resolver) {
|
| return true;
|
| }
|
|
|
| +// Clears the promise resolver and closes the Mojo connection.
|
| +void WebAuthentication::cleanup() {
|
| + m_authenticator.reset();
|
| + m_authenticatorRequests.Clear();
|
| +}
|
| +
|
| DEFINE_TRACE(WebAuthentication) {
|
| visitor->Trace(m_authenticatorRequests);
|
| ContextLifecycleObserver::Trace(visitor);
|
|
|