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

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: Removed dispose method 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(static_cast<uint8_t*>( 42 vector.Append(static_cast<uint8_t*>(
40 buffer.getAsArrayBufferView().View()->BaseAddress()), 43 buffer.getAsArrayBufferView().View()->BaseAddress()),
41 buffer.getAsArrayBufferView().View()->byteLength()); 44 buffer.getAsArrayBufferView().View()->byteLength());
42 } 45 }
43 return vector; 46 return vector;
(...skipping 14 matching lines...) Expand all
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
foolip 2017/05/12 08:00:06 This link doesn't work, should it be https://w3c.g
kpaulhamus 2017/05/24 21:08:44 Updated the link.
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.GetDocument()) { 142 : ContextLifecycleObserver(frame.GetDocument()) {}
98 frame.GetInterfaceProvider()->GetInterface(
99 mojo::MakeRequest(&m_authenticator));
100 m_authenticator.set_connection_error_handler(ConvertToBaseCallback(
101 WTF::Bind(&WebAuthentication::onAuthenticatorConnectionError,
102 WrapWeakPersistent(this))));
103 }
104 143
105 WebAuthentication::~WebAuthentication() { 144 WebAuthentication::~WebAuthentication() {
106 // |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
107 // outstanding requests because each holds a persistent handle to this object. 146 // outstanding requests because each holds a persistent handle to this object.
108 DCHECK(m_authenticatorRequests.IsEmpty()); 147 DCHECK(m_authenticatorRequests.IsEmpty());
109 } 148 }
110 149
111 void WebAuthentication::Dispose() {}
112
113 ScriptPromise WebAuthentication::makeCredential( 150 ScriptPromise WebAuthentication::makeCredential(
114 ScriptState* script_state, 151 ScriptState* script_state,
115 const RelyingPartyAccount& account_information, 152 const RelyingPartyAccount& account_information,
116 const HeapVector<ScopedCredentialParameters> crypto_parameters, 153 const HeapVector<ScopedCredentialParameters> crypto_parameters,
117 const BufferSource& attestation_challenge, 154 const BufferSource& attestation_challenge,
118 ScopedCredentialOptions& options) { 155 ScopedCredentialOptions& options) {
119 ExecutionContext* executionContext = script_state->GetExecutionContext(); 156 ScriptPromise promise = rejectIfNotSupported(script_state);
120 157 if (!promise.IsEmpty())
121 if (!m_authenticator) { 158 return promise;
122 return ScriptPromise::RejectWithDOMException(
123 script_state, DOMException::Create(kNotSupportedError));
124 }
125
126 String errorMessage;
127 if (!executionContext->IsSecureContext(errorMessage)) {
128 return ScriptPromise::RejectWithDOMException(
129 script_state, DOMException::Create(kSecurityError, errorMessage));
130 }
131 159
132 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); 160 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
133 ScriptPromise promise = resolver->Promise();
134 161
135 // TODO(kpaulhamus) validate parameters according to spec
136 Vector<uint8_t> buffer = mojo::convertBufferSource(attestation_challenge); 162 Vector<uint8_t> buffer = mojo::convertBufferSource(attestation_challenge);
137 auto opts = mojo::convertScopedCredentialOptions(options, resolver); 163 auto opts = mojo::convertScopedCredentialOptions(options, resolver);
138 Vector<webauth::mojom::blink::ScopedCredentialParametersPtr> parameters; 164 Vector<webauth::mojom::blink::ScopedCredentialParametersPtr> parameters;
139 for (const auto& parameter : crypto_parameters) { 165 for (const auto& parameter : crypto_parameters) {
140 parameters.push_back( 166 if (parameter.hasType()) { // TODO add algorithm
141 mojo::convertScopedCredentialParameter(parameter, resolver)); 167 parameters.push_back(
168 mojo::convertScopedCredentialParameter(parameter, resolver));
169 }
142 } 170 }
143 171
144 m_authenticatorRequests.insert(resolver); 172 m_authenticatorRequests.insert(resolver);
145 m_authenticator->makeCredential( 173 m_authenticator->MakeCredential(
146 account_information, std::move(parameters), buffer, std::move(opts), 174 account_information, std::move(parameters), buffer, std::move(opts),
147 ConvertToBaseCallback(WTF::Bind(&WebAuthentication::onMakeCredential, 175 ConvertToBaseCallback(WTF::Bind(&WebAuthentication::onMakeCredential,
148 WrapPersistent(this), 176 WrapPersistent(this),
149 WrapPersistent(resolver)))); 177 WrapPersistent(resolver))));
150 return promise; 178 return resolver->Promise();
151 } 179 }
152 180
153 ScriptPromise WebAuthentication::getAssertion( 181 ScriptPromise WebAuthentication::getAssertion(
154 ScriptState* script_state, 182 ScriptState* script_state,
155 const BufferSource& assertion_challenge, 183 const BufferSource& assertion_challenge,
156 const AuthenticationAssertionOptions& options) { 184 const AuthenticationAssertionOptions& options) {
157 NOTREACHED(); 185 NOTREACHED();
158 return ScriptPromise(); 186 return ScriptPromise();
159 } 187 }
160 188
161 void WebAuthentication::ContextDestroyed(ExecutionContext*) { 189 void WebAuthentication::ContextDestroyed(ExecutionContext*) {
162 m_authenticator.reset(); 190 cleanup();
163 m_authenticatorRequests.Clear(); 191 }
192
193 // Step 11 of https://w3c.github.io/webauthn/#makeCredential
194 void WebAuthentication::onMakeCredential(
195 ScriptPromiseResolver* resolver,
196 webauth::mojom::blink::AuthenticatorStatus status,
197 webauth::mojom::blink::ScopedCredentialInfoPtr credential) {
198 if (!markRequestComplete(resolver))
199 return;
200
201 DOMException* error = mojo::createExceptionFromStatus(status);
202 if (error) {
203 resolver->Reject(error);
204 cleanup();
205 return;
206 }
207
208 if (credential->client_data.IsEmpty() || credential->attestation.IsEmpty()) {
209 resolver->Reject(
210 DOMException::Create(kNotFoundError, "No credential returned."));
211 return;
212 }
213
214 DOMArrayBuffer* clientDataBuffer = DOMArrayBuffer::Create(
215 static_cast<void*>(&credential->client_data.front()),
216 credential->client_data.size());
217
218 DOMArrayBuffer* attestationBuffer = DOMArrayBuffer::Create(
219 static_cast<void*>(&credential->attestation.front()),
220 credential->attestation.size());
221
222 ScopedCredentialInfo* scopedCredential =
223 ScopedCredentialInfo::Create(clientDataBuffer, attestationBuffer);
224 resolver->Resolve(scopedCredential);
225 }
226
227 ScriptPromise WebAuthentication::rejectIfNotSupported(
228 ScriptState* script_state) {
229 ExecutionContext* executionContext = script_state->GetExecutionContext();
230
231 if (!m_authenticator) {
232 if (!GetFrame()) {
233 return ScriptPromise::RejectWithDOMException(
234 script_state, DOMException::Create(kNotSupportedError));
foolip 2017/05/12 08:00:06 The only NotSupportedError I see in the spec doesn
kpaulhamus 2017/05/24 21:08:44 This particular error is not specific to the spec
235 }
236 GetFrame()->GetInterfaceProvider()->GetInterface(
237 mojo::MakeRequest(&m_authenticator));
238
239 m_authenticator.set_connection_error_handler(ConvertToBaseCallback(
240 WTF::Bind(&WebAuthentication::onAuthenticatorConnectionError,
241 WrapWeakPersistent(this))));
242 }
243
244 String errorMessage;
245 if (!executionContext->IsSecureContext(errorMessage)) {
246 return ScriptPromise::RejectWithDOMException(
247 script_state, DOMException::Create(kSecurityError, errorMessage));
248 }
249
250 return ScriptPromise();
164 } 251 }
165 252
166 void WebAuthentication::onAuthenticatorConnectionError() { 253 void WebAuthentication::onAuthenticatorConnectionError() {
167 m_authenticator.reset();
168 for (ScriptPromiseResolver* resolver : m_authenticatorRequests) { 254 for (ScriptPromiseResolver* resolver : m_authenticatorRequests) {
169 resolver->Reject( 255 resolver->Reject(
170 DOMException::Create(kNotFoundError, kNoAuthenticatorError)); 256 DOMException::Create(kNotFoundError, kNoAuthenticatorError));
171 } 257 }
172 m_authenticatorRequests.Clear(); 258 cleanup();
173 }
174
175 void WebAuthentication::onMakeCredential(
176 ScriptPromiseResolver* resolver,
177 Vector<webauth::mojom::blink::ScopedCredentialInfoPtr> credentials) {
178 if (!markRequestComplete(resolver))
179 return;
180
181 HeapVector<Member<ScopedCredentialInfo>> scopedCredentials;
182 for (auto& credential : credentials) {
183 if (credential->client_data.IsEmpty() ||
184 credential->attestation.IsEmpty()) {
185 resolver->Reject(
186 DOMException::Create(kNotFoundError, "No credentials returned."));
187 }
188 DOMArrayBuffer* clientDataBuffer = DOMArrayBuffer::Create(
189 static_cast<void*>(&credential->client_data.front()),
190 credential->client_data.size());
191
192 DOMArrayBuffer* attestationBuffer = DOMArrayBuffer::Create(
193 static_cast<void*>(&credential->attestation.front()),
194 credential->attestation.size());
195
196 scopedCredentials.push_back(
197 ScopedCredentialInfo::Create(clientDataBuffer, attestationBuffer));
198 }
199 resolver->Resolve(scopedCredentials);
200 m_authenticatorRequests.erase(resolver);
201 } 259 }
202 260
203 bool WebAuthentication::markRequestComplete(ScriptPromiseResolver* resolver) { 261 bool WebAuthentication::markRequestComplete(ScriptPromiseResolver* resolver) {
204 auto requestEntry = m_authenticatorRequests.Find(resolver); 262 auto requestEntry = m_authenticatorRequests.Find(resolver);
205 if (requestEntry == m_authenticatorRequests.end()) 263 if (requestEntry == m_authenticatorRequests.end())
206 return false; 264 return false;
207 m_authenticatorRequests.erase(requestEntry); 265 m_authenticatorRequests.erase(requestEntry);
208 return true; 266 return true;
209 } 267 }
210 268
269 // Clears the promise resolver and closes the Mojo connection.
270 void WebAuthentication::cleanup() {
271 m_authenticator.reset();
272 m_authenticatorRequests.Clear();
273 }
274
211 DEFINE_TRACE(WebAuthentication) { 275 DEFINE_TRACE(WebAuthentication) {
212 visitor->Trace(m_authenticatorRequests); 276 visitor->Trace(m_authenticatorRequests);
213 ContextLifecycleObserver::Trace(visitor); 277 ContextLifecycleObserver::Trace(visitor);
214 } 278 }
215 279
216 } // namespace blink 280 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698