Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(106)

Side by Side Diff: third_party/WebKit/Source/modules/webauth/WebAuthentication.cpp

Issue 2788823002: Add the Mojo implementation of authenticator.mojom's MakeCredential. (Closed)
Patch Set: Addressing comments Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "modules/webauth/WebAuthentication.h" 5 #include "modules/webauth/WebAuthentication.h"
6 6
7 #include <stdint.h> 7 #include <stdint.h>
8 8
9 #include "bindings/core/v8/ScriptPromise.h" 9 #include "bindings/core/v8/ScriptPromise.h"
10 #include "bindings/core/v8/ScriptPromiseResolver.h" 10 #include "bindings/core/v8/ScriptPromiseResolver.h"
11 #include "core/dom/DOMException.h" 11 #include "core/dom/DOMException.h"
12 #include "core/dom/Document.h" 12 #include "core/dom/Document.h"
13 #include "core/dom/ExceptionCode.h" 13 #include "core/dom/ExceptionCode.h"
14 #include "core/frame/LocalFrame.h" 14 #include "core/frame/LocalFrame.h"
15 #include "modules/webauth/RelyingPartyAccount.h" 15 #include "modules/webauth/RelyingPartyAccount.h"
16 #include "modules/webauth/ScopedCredential.h" 16 #include "modules/webauth/ScopedCredential.h"
17 #include "modules/webauth/ScopedCredentialOptions.h" 17 #include "modules/webauth/ScopedCredentialOptions.h"
18 #include "modules/webauth/ScopedCredentialParameters.h" 18 #include "modules/webauth/ScopedCredentialParameters.h"
19 #include "public/platform/InterfaceProvider.h" 19 #include "public/platform/InterfaceProvider.h"
20 20
21 namespace { 21 namespace {
22 const char kNoAuthenticatorError[] = "Authenticator unavailable."; 22 const char kNoAuthenticatorError[] = "Authenticator unavailable.";
23 // Time to wait for an authenticator to successfully complete an operation.
24 static const double adjustedTimeoutLower = 60;
25 static const double adjustedTimeoutUpper = 120;
Mike West 2017/06/13 21:11:23 Nit: s/double/int/ (It's not clear why these need
kpaulhamus 2017/06/15 01:27:13 It is now, but it previously was seconds. Is it ok
23 } // anonymous namespace 26 } // anonymous namespace
24 27
25 namespace mojo { 28 namespace mojo {
26
27 using webauth::mojom::blink::RelyingPartyAccount; 29 using webauth::mojom::blink::RelyingPartyAccount;
28 using webauth::mojom::blink::RelyingPartyAccountPtr; 30 using webauth::mojom::blink::RelyingPartyAccountPtr;
31 using webauth::mojom::blink::AuthenticatorStatus;
32 using webauth::mojom::blink::ScopedCredentialDescriptor;
29 using webauth::mojom::blink::ScopedCredentialOptions; 33 using webauth::mojom::blink::ScopedCredentialOptions;
30 using webauth::mojom::blink::ScopedCredentialOptionsPtr; 34 using webauth::mojom::blink::ScopedCredentialOptionsPtr;
31 using webauth::mojom::blink::ScopedCredentialParameters; 35 using webauth::mojom::blink::ScopedCredentialParameters;
32 using webauth::mojom::blink::ScopedCredentialParametersPtr; 36 using webauth::mojom::blink::ScopedCredentialParametersPtr;
33 using webauth::mojom::blink::ScopedCredentialDescriptor;
34 using webauth::mojom::blink::ScopedCredentialType; 37 using webauth::mojom::blink::ScopedCredentialType;
35 using webauth::mojom::blink::Transport; 38 using webauth::mojom::blink::Transport;
36 39
37 // TODO(kpaulhamus): Make this a TypeConverter 40 // TODO(kpaulhamus): Make this a TypeConverter
38 Vector<uint8_t> ConvertBufferSource(const blink::BufferSource& buffer) { 41 Vector<uint8_t> ConvertBufferSource(const blink::BufferSource& buffer) {
39 DCHECK(buffer.isNull()); 42 DCHECK(!buffer.isNull());
40 Vector<uint8_t> vector; 43 Vector<uint8_t> vector;
41 if (buffer.isArrayBuffer()) { 44 if (buffer.isArrayBuffer()) {
42 vector.Append(static_cast<uint8_t*>(buffer.getAsArrayBuffer()->Data()), 45 vector.Append(static_cast<uint8_t*>(buffer.getAsArrayBuffer()->Data()),
43 buffer.getAsArrayBuffer()->ByteLength()); 46 buffer.getAsArrayBuffer()->ByteLength());
44 } else { 47 } else {
45 vector.Append(static_cast<uint8_t*>( 48 vector.Append(static_cast<uint8_t*>(
46 buffer.getAsArrayBufferView().View()->BaseAddress()), 49 buffer.getAsArrayBufferView().View()->BaseAddress()),
47 buffer.getAsArrayBufferView().View()->byteLength()); 50 buffer.getAsArrayBufferView().View()->byteLength());
48 } 51 }
49 return vector; 52 return vector;
(...skipping 12 matching lines...) Expand all
62 if (transport == "usb") 65 if (transport == "usb")
63 return Transport::USB; 66 return Transport::USB;
64 if (transport == "nfc") 67 if (transport == "nfc")
65 return Transport::NFC; 68 return Transport::NFC;
66 if (transport == "ble") 69 if (transport == "ble")
67 return Transport::BLE; 70 return Transport::BLE;
68 NOTREACHED(); 71 NOTREACHED();
69 return Transport::USB; 72 return Transport::USB;
70 } 73 }
71 74
75 // TODO(kpaulhamus): Make this a TypeConverter
72 RelyingPartyAccountPtr ConvertRelyingPartyAccount( 76 RelyingPartyAccountPtr ConvertRelyingPartyAccount(
73 const blink::RelyingPartyAccount& account_information, 77 const blink::RelyingPartyAccount& account_information,
74 blink::ScriptPromiseResolver* resolver) { 78 blink::ScriptPromiseResolver* resolver) {
75 auto mojo_account = RelyingPartyAccount::New(); 79 auto mojo_account = RelyingPartyAccount::New();
76 80
77 mojo_account->relying_party_display_name = 81 mojo_account->relying_party_display_name =
78 account_information.rpDisplayName(); 82 account_information.rpDisplayName();
79 mojo_account->display_name = account_information.displayName(); 83 mojo_account->display_name = account_information.displayName();
80 mojo_account->id = account_information.id(); 84 mojo_account->id = account_information.id();
81 mojo_account->name = account_information.name(); 85 mojo_account->name = account_information.name();
82 mojo_account->image_url = account_information.imageURL(); 86 mojo_account->image_url = account_information.imageURL();
83 return mojo_account; 87 return mojo_account;
84 } 88 }
85 89
86 // TODO(kpaulhamus): Make this a TypeConverter 90 // TODO(kpaulhamus): Make this a TypeConverter
87 ScopedCredentialOptionsPtr ConvertScopedCredentialOptions( 91 ScopedCredentialOptionsPtr ConvertScopedCredentialOptions(
88 const blink::ScopedCredentialOptions options, 92 const blink::ScopedCredentialOptions options,
89 blink::ScriptPromiseResolver* resolver) { 93 blink::ScriptPromiseResolver* resolver) {
90 auto mojo_options = ScopedCredentialOptions::New(); 94 auto mojo_options = ScopedCredentialOptions::New();
91 mojo_options->timeout_seconds = options.timeoutSeconds(); 95 if (options.hasRpId()) {
92 mojo_options->relying_party_id = options.rpId(); 96 mojo_options->relying_party_id = options.rpId();
Mike West 2017/06/13 21:11:23 Shouldn't we set this to the origin of the page if
kpaulhamus 2017/06/15 01:27:13 I do that setting in authenticator_impl, in the br
Mike West 2017/06/19 12:19:23 Hrm. Yeah, that makes sense. Can you add a comment
kpaulhamus 2017/06/26 15:37:40 Done.
97 }
93 98
94 // Adds the excludeList members (which are ScopedCredentialDescriptors) 99 // Step 4 of https://w3c.github.io/webauthn/#createCredential
95 for (const auto& descriptor : options.excludeList()) { 100 if (options.hasTimeoutSeconds()) {
96 auto mojo_descriptor = ScopedCredentialDescriptor::New(); 101 mojo_options->adjusted_timeout =
97 mojo_descriptor->type = ConvertScopedCredentialType(descriptor.type()); 102 static_cast<double>(options.timeoutSeconds());
Mike West 2017/06/13 21:11:23 See milliseconds vs seconds question above.
kpaulhamus 2017/06/15 01:27:14 Acknowledged.
98 mojo_descriptor->id = ConvertBufferSource(descriptor.id()); 103 if (mojo_options->adjusted_timeout > adjustedTimeoutUpper) {
99 for (const auto& transport : descriptor.transports()) 104 mojo_options->adjusted_timeout = adjustedTimeoutUpper;
100 mojo_descriptor->transports.push_back(ConvertTransport(transport)); 105 } else if (mojo_options->adjusted_timeout < adjustedTimeoutLower) {
101 mojo_options->exclude_list.push_back(std::move(mojo_descriptor)); 106 mojo_options->adjusted_timeout = adjustedTimeoutLower;
107 }
108 } else {
109 mojo_options->adjusted_timeout = adjustedTimeoutLower;
110 }
111
112 if (options.hasExcludeList()) {
113 // Adds the excludeList members (which are ScopedCredentialDescriptors)
114 for (const auto& descriptor : options.excludeList()) {
115 auto mojo_descriptor = ScopedCredentialDescriptor::New();
116 mojo_descriptor->type = ConvertScopedCredentialType(descriptor.type());
117 mojo_descriptor->id = ConvertBufferSource(descriptor.id());
118 for (const auto& transport : descriptor.transports())
119 mojo_descriptor->transports.push_back(ConvertTransport(transport));
120 mojo_options->exclude_list.push_back(std::move(mojo_descriptor));
121 }
102 } 122 }
103 // TODO(kpaulhamus): add AuthenticationExtensions; 123 // TODO(kpaulhamus): add AuthenticationExtensions;
104 return mojo_options; 124 return mojo_options;
105 } 125 }
106 126
107 // TODO(kpaulhamus): Make this a TypeConverter 127 // TODO(kpaulhamus): Make this a TypeConverter
108 ScopedCredentialParametersPtr ConvertScopedCredentialParameter( 128 ScopedCredentialParametersPtr ConvertScopedCredentialParameter(
109 const blink::ScopedCredentialParameters parameter, 129 const blink::ScopedCredentialParameters parameter,
110 blink::ScriptPromiseResolver* resolver) { 130 blink::ScriptPromiseResolver* resolver) {
111 auto mojo_parameter = ScopedCredentialParameters::New(); 131 auto mojo_parameter = ScopedCredentialParameters::New();
112 mojo_parameter->type = ConvertScopedCredentialType(parameter.type()); 132 mojo_parameter->type = ConvertScopedCredentialType(parameter.type());
113 // TODO(kpaulhamus): add AlgorithmIdentifier 133 // TODO(kpaulhamus): add AlgorithmIdentifier
114 return mojo_parameter; 134 return mojo_parameter;
115 } 135 }
136
137 blink::DOMException* CreateExceptionFromStatus(AuthenticatorStatus status) {
138 switch (status) {
139 case AuthenticatorStatus::NOT_ALLOWED_ERROR:
140 return blink::DOMException::Create(blink::kNotAllowedError,
141 "Not allowed.");
142 case AuthenticatorStatus::NOT_SUPPORTED_ERROR:
143 return blink::DOMException::Create(
144 blink::kNotSupportedError,
145 "Parameters for this operation are not supported.");
146 case AuthenticatorStatus::SECURITY_ERROR:
147 return blink::DOMException::Create(blink::kSecurityError,
148 "The operation was not allowed.");
149 case AuthenticatorStatus::UNKNOWN_ERROR:
150 return blink::DOMException::Create(blink::kUnknownError,
151 "Request failed.");
152 case AuthenticatorStatus::CANCELLED:
153 return blink::DOMException::Create(blink::kNotAllowedError,
154 "User canceled the operation.");
155 case AuthenticatorStatus::SUCCESS:
156 return nullptr;
157 default:
158 NOTREACHED();
159 return nullptr;
160 }
161 }
116 } // namespace mojo 162 } // namespace mojo
117 163
118 namespace blink { 164 namespace blink {
119
120 WebAuthentication::WebAuthentication(LocalFrame& frame) 165 WebAuthentication::WebAuthentication(LocalFrame& frame)
121 : ContextLifecycleObserver(frame.GetDocument()) { 166 : ContextLifecycleObserver(frame.GetDocument()) {}
122 frame.GetInterfaceProvider()->GetInterface(
123 mojo::MakeRequest(&authenticator_));
124 authenticator_.set_connection_error_handler(ConvertToBaseCallback(
125 WTF::Bind(&WebAuthentication::OnAuthenticatorConnectionError,
126 WrapWeakPersistent(this))));
127 }
128 167
129 WebAuthentication::~WebAuthentication() { 168 WebAuthentication::~WebAuthentication() {
130 // |authenticator_| may still be valid but there should be no more 169 // |authenticator_| may still be valid but there should be no more
131 // outstanding requests because each holds a persistent handle to this object. 170 // outstanding requests because each holds a persistent handle to this object.
132 DCHECK(authenticator_requests_.IsEmpty()); 171 DCHECK(authenticator_requests_.IsEmpty());
133 } 172 }
134 173
135 void WebAuthentication::Dispose() {}
136
137 ScriptPromise WebAuthentication::makeCredential( 174 ScriptPromise WebAuthentication::makeCredential(
138 ScriptState* script_state, 175 ScriptState* script_state,
139 const RelyingPartyAccount& account_information, 176 const RelyingPartyAccount& account_information,
140 const HeapVector<ScopedCredentialParameters> crypto_parameters, 177 const HeapVector<ScopedCredentialParameters> crypto_parameters,
141 const BufferSource& attestation_challenge, 178 const BufferSource& attestation_challenge,
142 ScopedCredentialOptions& options) { 179 ScopedCredentialOptions& options) {
143 if (!authenticator_) { 180 ScriptPromise promise = RejectIfNotSupported(script_state);
144 return ScriptPromise::RejectWithDOMException( 181 if (!promise.IsEmpty())
145 script_state, DOMException::Create(kNotSupportedError)); 182 return promise;
146 }
147 183
148 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); 184 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
149 ScriptPromise promise = resolver->Promise();
150 185
151 // TODO(kpaulhamus) validate parameters according to spec
152 auto account =
153 mojo::ConvertRelyingPartyAccount(account_information, resolver);
154 Vector<uint8_t> buffer = mojo::ConvertBufferSource(attestation_challenge); 186 Vector<uint8_t> buffer = mojo::ConvertBufferSource(attestation_challenge);
155 auto opts = mojo::ConvertScopedCredentialOptions(options, resolver); 187 auto opts = mojo::ConvertScopedCredentialOptions(options, resolver);
156 Vector<webauth::mojom::blink::ScopedCredentialParametersPtr> parameters; 188 Vector<webauth::mojom::blink::ScopedCredentialParametersPtr> parameters;
157 for (const auto& parameter : crypto_parameters) { 189 for (const auto& parameter : crypto_parameters) {
158 parameters.push_back( 190 if (parameter.hasType()) {
159 mojo::ConvertScopedCredentialParameter(parameter, resolver)); 191 parameters.push_back(
192 mojo::ConvertScopedCredentialParameter(parameter, resolver));
193 }
160 } 194 }
161 195 auto account =
196 mojo::ConvertRelyingPartyAccount(account_information, resolver);
162 authenticator_requests_.insert(resolver); 197 authenticator_requests_.insert(resolver);
163 authenticator_->MakeCredential( 198 authenticator_->MakeCredential(
164 std::move(account), std::move(parameters), buffer, std::move(opts), 199 std::move(account), std::move(parameters), buffer, std::move(opts),
165 ConvertToBaseCallback(Bind(&WebAuthentication::OnMakeCredential, 200 ConvertToBaseCallback(WTF::Bind(&WebAuthentication::OnMakeCredential,
166 WrapPersistent(this), 201 WrapPersistent(this),
167 WrapPersistent(resolver)))); 202 WrapPersistent(resolver))));
168 return promise; 203 return resolver->Promise();
169 } 204 }
170 205
171 ScriptPromise WebAuthentication::getAssertion( 206 ScriptPromise WebAuthentication::getAssertion(
172 ScriptState* script_state, 207 ScriptState* script_state,
173 const BufferSource& assertion_challenge, 208 const BufferSource& assertion_challenge,
174 const AuthenticationAssertionOptions& options) { 209 const AuthenticationAssertionOptions& options) {
175 NOTREACHED(); 210 NOTREACHED();
176 return ScriptPromise(); 211 return ScriptPromise();
177 } 212 }
178 213
179 void WebAuthentication::ContextDestroyed(ExecutionContext*) { 214 void WebAuthentication::ContextDestroyed(ExecutionContext*) {
180 authenticator_.reset(); 215 Cleanup();
181 authenticator_requests_.clear(); 216 }
217
218 void WebAuthentication::OnMakeCredential(
219 ScriptPromiseResolver* resolver,
220 webauth::mojom::blink::AuthenticatorStatus status,
221 webauth::mojom::blink::ScopedCredentialInfoPtr credential) {
222 if (!MarkRequestComplete(resolver))
223 return;
224
225 DOMException* error = mojo::CreateExceptionFromStatus(status);
226 if (error) {
Mike West 2017/06/13 21:11:23 Perhaps `DCHECK(!credential)`?
kpaulhamus 2017/06/15 01:27:13 The spec is pretty specific on what types of error
Mike West 2017/06/19 12:19:23 I'm assuming that in any case where an error exist
kpaulhamus 2017/06/26 15:37:40 Done.
227 resolver->Reject(error);
228 Cleanup();
229 return;
230 }
231
232 if (credential->client_data.IsEmpty() || credential->attestation.IsEmpty()) {
233 resolver->Reject(
234 DOMException::Create(kNotFoundError, "No credential returned."));
235 return;
236 }
237
238 DOMArrayBuffer* clientDataBuffer = DOMArrayBuffer::Create(
239 static_cast<void*>(&credential->client_data.front()),
240 credential->client_data.size());
241
242 DOMArrayBuffer* attestationBuffer = DOMArrayBuffer::Create(
243 static_cast<void*>(&credential->attestation.front()),
244 credential->attestation.size());
245
246 ScopedCredentialInfo* scopedCredential =
247 ScopedCredentialInfo::Create(clientDataBuffer, attestationBuffer);
248 resolver->Resolve(scopedCredential);
249 }
250
251 ScriptPromise WebAuthentication::RejectIfNotSupported(
252 ScriptState* script_state) {
253 if (!authenticator_) {
254 if (!GetFrame()) {
255 return ScriptPromise::RejectWithDOMException(
256 script_state, DOMException::Create(kNotSupportedError));
257 }
258 GetFrame()->GetInterfaceProvider()->GetInterface(
259 mojo::MakeRequest(&authenticator_));
260
261 authenticator_.set_connection_error_handler(ConvertToBaseCallback(
262 WTF::Bind(&WebAuthentication::OnAuthenticatorConnectionError,
263 WrapWeakPersistent(this))));
264 }
265 return ScriptPromise();
182 } 266 }
183 267
184 void WebAuthentication::OnAuthenticatorConnectionError() { 268 void WebAuthentication::OnAuthenticatorConnectionError() {
185 authenticator_.reset();
186 for (ScriptPromiseResolver* resolver : authenticator_requests_) { 269 for (ScriptPromiseResolver* resolver : authenticator_requests_) {
187 resolver->Reject( 270 resolver->Reject(
188 DOMException::Create(kNotFoundError, kNoAuthenticatorError)); 271 DOMException::Create(kNotFoundError, kNoAuthenticatorError));
189 } 272 }
190 authenticator_requests_.clear(); 273 Cleanup();
191 }
192
193 void WebAuthentication::OnMakeCredential(
194 ScriptPromiseResolver* resolver,
195 Vector<webauth::mojom::blink::ScopedCredentialInfoPtr> credentials) {
196 if (!MarkRequestComplete(resolver))
197 return;
198
199 HeapVector<Member<ScopedCredentialInfo>> scoped_credentials;
200 for (auto& credential : credentials) {
201 if (credential->client_data.IsEmpty() ||
202 credential->attestation.IsEmpty()) {
203 resolver->Reject(
204 DOMException::Create(kNotFoundError, "No credentials returned."));
205 }
206 DOMArrayBuffer* client_data_buffer = DOMArrayBuffer::Create(
207 static_cast<void*>(&credential->client_data.front()),
208 credential->client_data.size());
209
210 DOMArrayBuffer* attestation_buffer = DOMArrayBuffer::Create(
211 static_cast<void*>(&credential->attestation.front()),
212 credential->attestation.size());
213
214 scoped_credentials.push_back(
215 ScopedCredentialInfo::Create(client_data_buffer, attestation_buffer));
216 }
217 resolver->Resolve();
218 } 274 }
219 275
220 bool WebAuthentication::MarkRequestComplete(ScriptPromiseResolver* resolver) { 276 bool WebAuthentication::MarkRequestComplete(ScriptPromiseResolver* resolver) {
221 auto request_entry = authenticator_requests_.find(resolver); 277 auto request_entry = authenticator_requests_.find(resolver);
222 if (request_entry == authenticator_requests_.end()) 278 if (request_entry == authenticator_requests_.end())
223 return false; 279 return false;
224 authenticator_requests_.erase(request_entry); 280 authenticator_requests_.erase(request_entry);
225 return true; 281 return true;
226 } 282 }
227 283
228 DEFINE_TRACE(WebAuthentication) { 284 DEFINE_TRACE(WebAuthentication) {
229 visitor->Trace(authenticator_requests_); 285 visitor->Trace(authenticator_requests_);
230 ContextLifecycleObserver::Trace(visitor); 286 ContextLifecycleObserver::Trace(visitor);
231 } 287 }
232 288
289 // Clears the promise resolver, timer, and closes the Mojo connection.
290 void WebAuthentication::Cleanup() {
291 authenticator_.reset();
292 authenticator_requests_.clear();
293 }
294
233 } // namespace blink 295 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698