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

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: Added webauth browser feature. Corrected optional mojom struct params. Blink rename. Created 3 years, 8 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 2017 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 "bindings/core/v8/ScriptPromise.h" 7 #include "bindings/core/v8/ScriptPromise.h"
8 #include "bindings/core/v8/ScriptPromiseResolver.h" 8 #include "bindings/core/v8/ScriptPromiseResolver.h"
9 #include "core/dom/DOMException.h" 9 #include "core/dom/DOMException.h"
10 #include "core/dom/Document.h" 10 #include "core/dom/Document.h"
11 #include "core/dom/ExceptionCode.h" 11 #include "core/dom/ExceptionCode.h"
12 #include "core/frame/LocalFrame.h" 12 #include "core/frame/LocalFrame.h"
13 #include "modules/webauth/ScopedCredential.h" 13 #include "modules/webauth/ScopedCredential.h"
14 #include "modules/webauth/ScopedCredentialOptions.h" 14 #include "modules/webauth/ScopedCredentialOptions.h"
15 #include "modules/webauth/ScopedCredentialParameters.h" 15 #include "modules/webauth/ScopedCredentialParameters.h"
16 #include "public/platform/InterfaceProvider.h" 16 #include "public/platform/InterfaceProvider.h"
17 17
18 namespace { 18 namespace {
19 const char kNoAuthenticatorError[] = "Authenticator unavailable."; 19 const char kNoAuthenticatorError[] = "Authenticator unavailable.";
20 // Time to wait for an authenticator to successfully complete an operation.
21 static const double adjustedTimeoutLower = 60;
22 static const double adjustedTimeoutUpper = 120;
20 } // anonymous namespace 23 } // anonymous namespace
21 24
22 namespace mojo { 25 namespace mojo {
23 26 using webauth::mojom::blink::AuthenticatorStatus;
27 using webauth::mojom::blink::ScopedCredentialDescriptor;
24 using webauth::mojom::blink::ScopedCredentialOptions; 28 using webauth::mojom::blink::ScopedCredentialOptions;
25 using webauth::mojom::blink::ScopedCredentialOptionsPtr; 29 using webauth::mojom::blink::ScopedCredentialOptionsPtr;
26 using webauth::mojom::blink::ScopedCredentialParameters; 30 using webauth::mojom::blink::ScopedCredentialParameters;
27 using webauth::mojom::blink::ScopedCredentialParametersPtr; 31 using webauth::mojom::blink::ScopedCredentialParametersPtr;
28 using webauth::mojom::blink::ScopedCredentialDescriptor;
29 using webauth::mojom::blink::ScopedCredentialType; 32 using webauth::mojom::blink::ScopedCredentialType;
30 using webauth::mojom::blink::Transport; 33 using webauth::mojom::blink::Transport;
31 34
32 Vector<uint8_t> convertBufferSource(const blink::BufferSource& buffer) { 35 Vector<uint8_t> convertBufferSource(const blink::BufferSource& buffer) {
33 DCHECK(buffer.isNull()); 36 DCHECK(!buffer.isNull());
34 Vector<uint8_t> vector; 37 Vector<uint8_t> vector;
35 if (buffer.isArrayBuffer()) { 38 if (buffer.isArrayBuffer()) {
36 vector.append(static_cast<uint8_t*>(buffer.getAsArrayBuffer()->data()), 39 vector.Append(static_cast<uint8_t*>(buffer.getAsArrayBuffer()->Data()),
37 buffer.getAsArrayBuffer()->byteLength()); 40 buffer.getAsArrayBuffer()->ByteLength());
38 } else { 41 } else {
39 vector.append( 42 vector.Append(
40 static_cast<uint8_t*>(buffer.getAsArrayBufferView()->baseAddress()), 43 static_cast<uint8_t*>(buffer.getAsArrayBufferView()->BaseAddress()),
41 buffer.getAsArrayBufferView()->byteLength()); 44 buffer.getAsArrayBufferView()->byteLength());
42 } 45 }
43 return vector; 46 return vector;
44 } 47 }
45 48
46 ScopedCredentialType convertScopedCredentialType(const WTF::String& credType) { 49 ScopedCredentialType convertScopedCredentialType(const WTF::String& credType) {
47 if (credType == "ScopedCred") 50 if (credType == "ScopedCred")
48 return ScopedCredentialType::SCOPEDCRED; 51 return ScopedCredentialType::SCOPEDCRED;
49 NOTREACHED(); 52 NOTREACHED();
50 return ScopedCredentialType::SCOPEDCRED; 53 return ScopedCredentialType::SCOPEDCRED;
51 } 54 }
52 55
53 Transport convertTransport(const WTF::String& transport) { 56 Transport convertTransport(const WTF::String& transport) {
54 if (transport == "usb") 57 if (transport == "usb")
55 return Transport::USB; 58 return Transport::USB;
56 if (transport == "nfc") 59 if (transport == "nfc")
57 return Transport::NFC; 60 return Transport::NFC;
58 if (transport == "ble") 61 if (transport == "ble")
59 return Transport::BLE; 62 return Transport::BLE;
60 NOTREACHED(); 63 NOTREACHED();
61 return Transport::USB; 64 return Transport::USB;
62 } 65 }
63 66
64 ScopedCredentialOptionsPtr convertScopedCredentialOptions( 67 ScopedCredentialOptionsPtr convertScopedCredentialOptions(
65 const blink::ScopedCredentialOptions options, 68 const blink::ScopedCredentialOptions options,
66 blink::ScriptPromiseResolver* resolver) { 69 blink::ScriptPromiseResolver* resolver) {
67 auto mojoOptions = ScopedCredentialOptions::New(); 70 auto mojoOptions = ScopedCredentialOptions::New();
68 mojoOptions->timeout_seconds = options.timeoutSeconds(); 71 if (options.hasRpId()) {
69 mojoOptions->rp_id = options.rpId(); 72 mojoOptions->rp_id = options.rpId();
73 }
70 74
71 // Adds the excludeList members (which are ScopedCredentialDescriptors) 75 // Step 1 of https://w3c.github.io/webauthn/#makeCredential
72 for (const auto& descriptor : options.excludeList()) { 76 if (options.hasTimeoutSeconds()) {
73 auto mojoDescriptor = ScopedCredentialDescriptor::New(); 77 mojoOptions->adjusted_timeout =
74 mojoDescriptor->type = convertScopedCredentialType(descriptor.type()); 78 static_cast<double>(options.timeoutSeconds());
75 mojoDescriptor->id = convertBufferSource(descriptor.id()); 79 if (mojoOptions->adjusted_timeout > adjustedTimeoutUpper) {
76 for (const auto& transport : descriptor.transports()) 80 mojoOptions->adjusted_timeout = adjustedTimeoutUpper;
77 mojoDescriptor->transports.push_back(convertTransport(transport)); 81 } else if (mojoOptions->adjusted_timeout < adjustedTimeoutLower) {
78 mojoOptions->exclude_list.push_back(std::move(mojoDescriptor)); 82 mojoOptions->adjusted_timeout = adjustedTimeoutLower;
83 }
84 } else {
85 mojoOptions->adjusted_timeout = adjustedTimeoutLower;
86 }
87
88 if (options.hasExcludeList()) {
89 // Adds the excludeList members (which are ScopedCredentialDescriptors)
90 for (const auto& descriptor : options.excludeList()) {
91 auto mojoDescriptor = ScopedCredentialDescriptor::New();
92 mojoDescriptor->type = convertScopedCredentialType(descriptor.type());
93 mojoDescriptor->id = convertBufferSource(descriptor.id());
94 for (const auto& transport : descriptor.transports())
95 mojoDescriptor->transports.push_back(convertTransport(transport));
96 mojoOptions->exclude_list.push_back(std::move(mojoDescriptor));
97 }
79 } 98 }
80 // TODO (kpaulhamus) add AuthenticationExtensions; 99 // TODO (kpaulhamus) add AuthenticationExtensions;
81 return mojoOptions; 100 return mojoOptions;
82 } 101 }
83 102
84 ScopedCredentialParametersPtr convertScopedCredentialParameter( 103 ScopedCredentialParametersPtr convertScopedCredentialParameter(
85 const blink::ScopedCredentialParameters parameter, 104 const blink::ScopedCredentialParameters parameter,
86 blink::ScriptPromiseResolver* resolver) { 105 blink::ScriptPromiseResolver* resolver) {
87 auto mojoParameter = ScopedCredentialParameters::New(); 106 auto mojoParameter = ScopedCredentialParameters::New();
88 mojoParameter->type = convertScopedCredentialType(parameter.type()); 107 mojoParameter->type = convertScopedCredentialType(parameter.type());
89 // TODO (kpaulhamus) add AlgorithmIdentifier 108 // TODO (kpaulhamus) add AlgorithmIdentifier
90 return mojoParameter; 109 return mojoParameter;
91 } 110 }
111
112 blink::DOMException* createExceptionFromStatus(AuthenticatorStatus status) {
113 switch (status) {
114 case AuthenticatorStatus::NOT_ALLOWED_ERROR:
115 return blink::DOMException::Create(blink::kNotAllowedError,
116 "Not allowed.");
117 case AuthenticatorStatus::NOT_SUPPORTED_ERROR:
118 return blink::DOMException::Create(
119 blink::kNotSupportedError,
120 "Parameters for this operation are not supported.");
121 case AuthenticatorStatus::SECURITY_ERROR:
122 return blink::DOMException::Create(blink::kSecurityError,
123 "The operation was not allowed.");
124 case AuthenticatorStatus::UNKNOWN_ERROR:
125 return blink::DOMException::Create(blink::kUnknownError,
126 "Request failed.");
127 case AuthenticatorStatus::CANCELLED:
128 return blink::DOMException::Create(blink::kNotAllowedError,
129 "User canceled the operation.");
130 case AuthenticatorStatus::SUCCESS:
131 return nullptr;
132 default:
133 NOTREACHED();
134 return nullptr;
135 }
136 }
92 } // namespace mojo 137 } // namespace mojo
93 138
94 namespace blink { 139 namespace blink {
95 140
96 WebAuthentication::WebAuthentication(LocalFrame& frame) 141 WebAuthentication::WebAuthentication(LocalFrame& frame)
97 : ContextLifecycleObserver(frame.document()) { 142 : ContextLifecycleObserver(frame.GetDocument()) {}
98 frame.interfaceProvider()->getInterface(mojo::MakeRequest(&m_authenticator));
99 m_authenticator.set_connection_error_handler(convertToBaseCallback(
100 WTF::bind(&WebAuthentication::onAuthenticatorConnectionError,
101 wrapWeakPersistent(this))));
102 }
103 143
104 WebAuthentication::~WebAuthentication() { 144 WebAuthentication::~WebAuthentication() {
105 // |m_authenticator| may still be valid but there should be no more 145 // |m_authenticator| may still be valid but there should be no more
106 // outstanding requests because each holds a persistent handle to this object. 146 // outstanding requests because each holds a persistent handle to this object.
107 DCHECK(m_authenticatorRequests.isEmpty()); 147 DCHECK(m_authenticatorRequests.IsEmpty());
108 } 148 }
109 149
110 void WebAuthentication::Dispose() {} 150 void WebAuthentication::Dispose() {}
111 151
152 // With options
112 ScriptPromise WebAuthentication::makeCredential( 153 ScriptPromise WebAuthentication::makeCredential(
113 ScriptState* script_state, 154 ScriptState* script_state,
114 const RelyingPartyAccount& account_information, 155 const RelyingPartyAccount& account_information,
115 const HeapVector<ScopedCredentialParameters> crypto_parameters, 156 const HeapVector<ScopedCredentialParameters> crypto_parameters,
116 const BufferSource& attestation_challenge, 157 const BufferSource& attestation_challenge,
117 ScopedCredentialOptions& options) { 158 ScopedCredentialOptions& options) {
118 ExecutionContext* executionContext = scriptState->getExecutionContext(); 159 ScriptPromise promise = rejectIfNotSupported(script_state);
160 if (!promise.IsEmpty())
161 return promise;
119 162
120 if (!m_authenticator) { 163 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
121 return ScriptPromise::rejectWithDOMException(
122 scriptState, DOMException::create(NotSupportedError));
123 }
124 164
125 String errorMessage; 165 Vector<uint8_t> buffer = mojo::convertBufferSource(attestation_challenge);
126 if (!executionContext->isSecureContext(errorMessage)) {
127 return ScriptPromise::rejectWithDOMException(
128 scriptState, DOMException::create(SecurityError, errorMessage));
129 }
130
131 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
132 ScriptPromise promise = resolver->promise();
133
134 // TODO(kpaulhamus) validate parameters according to spec
135 Vector<uint8_t> buffer = mojo::convertBufferSource(attestationChallenge);
136 auto opts = mojo::convertScopedCredentialOptions(options, resolver); 166 auto opts = mojo::convertScopedCredentialOptions(options, resolver);
137 Vector<webauth::mojom::blink::ScopedCredentialParametersPtr> parameters; 167 Vector<webauth::mojom::blink::ScopedCredentialParametersPtr> parameters;
138 for (const auto& parameter : cryptoParameters) { 168 for (const auto& parameter : crypto_parameters) {
139 parameters.push_back( 169 if (parameter.hasType()) { // TODO add algorithm
140 mojo::convertScopedCredentialParameter(parameter, resolver)); 170 parameters.push_back(
171 mojo::convertScopedCredentialParameter(parameter, resolver));
172 }
141 } 173 }
142 174
143 m_authenticatorRequests.insert(resolver); 175 m_authenticatorRequests.insert(resolver);
144 m_authenticator->makeCredential( 176 m_authenticator->MakeCredential(
145 accountInformation, std::move(parameters), buffer, std::move(opts), 177 account_information, std::move(parameters), buffer, std::move(opts),
146 convertToBaseCallback(WTF::bind(&WebAuthentication::onMakeCredential, 178 ConvertToBaseCallback(WTF::Bind(&WebAuthentication::onMakeCredential,
147 wrapPersistent(this), 179 WrapPersistent(this),
148 wrapPersistent(resolver)))); 180 WrapPersistent(resolver))));
149 return promise; 181 return resolver->Promise();
150 } 182 }
151 183
152 ScriptPromise WebAuthentication::getAssertion( 184 ScriptPromise WebAuthentication::getAssertion(
153 ScriptState* script_state, 185 ScriptState* script_state,
154 const BufferSource& assertion_challenge, 186 const BufferSource& assertion_challenge,
155 const AuthenticationAssertionOptions& options) { 187 const AuthenticationAssertionOptions& options) {
156 NOTREACHED(); 188 NOTREACHED();
157 return ScriptPromise(); 189 return ScriptPromise();
158 } 190 }
159 191
160 void WebAuthentication::contextDestroyed(ExecutionContext*) { 192 void WebAuthentication::ContextDestroyed(ExecutionContext*) {
161 m_authenticator.reset(); 193 cleanup();
162 m_authenticatorRequests.clear(); 194 }
195
196 // Step 11 of https://w3c.github.io/webauthn/#makeCredential
197 void WebAuthentication::onMakeCredential(
198 ScriptPromiseResolver* resolver,
199 webauth::mojom::blink::AuthenticatorStatus status,
200 webauth::mojom::blink::ScopedCredentialInfoPtr credential) {
201 if (!markRequestComplete(resolver))
202 return;
203
204 DOMException* error = mojo::createExceptionFromStatus(status);
205 if (error) {
206 for (ScriptPromiseResolver* resolver : m_authenticatorRequests) {
207 resolver->Reject(error);
208 }
209 cleanup();
Zhiqiang Zhang (Slow) 2017/04/11 19:42:50 Actually, the issue is unrelated to mojo. I checke
210 return;
211 }
212
213 if (credential->client_data.IsEmpty() || credential->attestation.IsEmpty()) {
214 resolver->Reject(
215 DOMException::Create(kNotFoundError, "No credential returned."));
216 return;
217 }
218
219 DOMArrayBuffer* clientDataBuffer = DOMArrayBuffer::Create(
220 static_cast<void*>(&credential->client_data.front()),
221 credential->client_data.size());
222
223 DOMArrayBuffer* attestationBuffer = DOMArrayBuffer::Create(
224 static_cast<void*>(&credential->attestation.front()),
225 credential->attestation.size());
226
227 ScopedCredentialInfo* scopedCredential =
228 ScopedCredentialInfo::Create(clientDataBuffer, attestationBuffer);
229 resolver->Resolve(scopedCredential);
230 }
231
232 ScriptPromise WebAuthentication::rejectIfNotSupported(
233 ScriptState* script_state) {
234 ExecutionContext* executionContext = script_state->GetExecutionContext();
235
236 if (!m_authenticator) {
237 if (!GetFrame()) {
238 return ScriptPromise::RejectWithDOMException(
239 script_state, DOMException::Create(kNotSupportedError));
240 }
241 GetFrame()->GetInterfaceProvider()->GetInterface(
242 mojo::MakeRequest(&m_authenticator));
243
244 m_authenticator.set_connection_error_handler(ConvertToBaseCallback(
245 WTF::Bind(&WebAuthentication::onAuthenticatorConnectionError,
246 WrapWeakPersistent(this))));
247 }
248
249 String errorMessage;
250 if (!executionContext->IsSecureContext(errorMessage)) {
251 return ScriptPromise::RejectWithDOMException(
252 script_state, DOMException::Create(kSecurityError, errorMessage));
253 }
254
255 return ScriptPromise();
163 } 256 }
164 257
165 void WebAuthentication::onAuthenticatorConnectionError() { 258 void WebAuthentication::onAuthenticatorConnectionError() {
166 m_authenticator.reset();
167 for (ScriptPromiseResolver* resolver : m_authenticatorRequests) { 259 for (ScriptPromiseResolver* resolver : m_authenticatorRequests) {
168 resolver->reject( 260 resolver->Reject(
169 DOMException::create(NotFoundError, kNoAuthenticatorError)); 261 DOMException::Create(kNotFoundError, kNoAuthenticatorError));
170 } 262 }
171 m_authenticatorRequests.clear(); 263 cleanup();
172 }
173
174 void WebAuthentication::onMakeCredential(
175 ScriptPromiseResolver* resolver,
176 Vector<webauth::mojom::blink::ScopedCredentialInfoPtr> credentials) {
177 if (!markRequestComplete(resolver))
178 return;
179
180 HeapVector<Member<ScopedCredentialInfo>> scopedCredentials;
181 for (auto& credential : credentials) {
182 if (credential->client_data.isEmpty() ||
183 credential->attestation.isEmpty()) {
184 resolver->reject(
185 DOMException::create(NotFoundError, "No credentials returned."));
186 }
187 DOMArrayBuffer* clientDataBuffer = DOMArrayBuffer::create(
188 static_cast<void*>(&credential->client_data.front()),
189 credential->client_data.size());
190
191 DOMArrayBuffer* attestationBuffer = DOMArrayBuffer::create(
192 static_cast<void*>(&credential->attestation.front()),
193 credential->attestation.size());
194
195 scopedCredentials.push_back(
196 ScopedCredentialInfo::create(clientDataBuffer, attestationBuffer));
197 }
198 resolver->resolve(scopedCredentials);
199 m_authenticatorRequests.erase(resolver);
200 } 264 }
201 265
202 bool WebAuthentication::markRequestComplete(ScriptPromiseResolver* resolver) { 266 bool WebAuthentication::markRequestComplete(ScriptPromiseResolver* resolver) {
203 auto requestEntry = m_authenticatorRequests.find(resolver); 267 auto requestEntry = m_authenticatorRequests.Find(resolver);
204 if (requestEntry == m_authenticatorRequests.end()) 268 if (requestEntry == m_authenticatorRequests.end())
205 return false; 269 return false;
206 m_authenticatorRequests.erase(requestEntry); 270 m_authenticatorRequests.erase(requestEntry);
207 return true; 271 return true;
208 } 272 }
209 273
274 // Clears the promise resolver and closes the Mojo connection.
275 void WebAuthentication::cleanup() {
276 m_authenticator.reset();
277 m_authenticatorRequests.Clear();
278 }
279
210 DEFINE_TRACE(WebAuthentication) { 280 DEFINE_TRACE(WebAuthentication) {
211 visitor->trace(m_authenticatorRequests); 281 visitor->Trace(m_authenticatorRequests);
212 ContextLifecycleObserver::trace(visitor); 282 ContextLifecycleObserver::Trace(visitor);
213 } 283 }
214 284
215 } // namespace blink 285 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/modules/webauth/WebAuthentication.h ('k') | third_party/WebKit/Source/web/WebRuntimeFeatures.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698