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

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: Stripping out unnecessary comments 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
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() {} 150 void WebAuthentication::Dispose() {}
112 151
113 ScriptPromise WebAuthentication::makeCredential( 152 ScriptPromise WebAuthentication::makeCredential(
114 ScriptState* script_state, 153 ScriptState* script_state,
115 const RelyingPartyAccount& account_information, 154 const RelyingPartyAccount& account_information,
116 const HeapVector<ScopedCredentialParameters> crypto_parameters, 155 const HeapVector<ScopedCredentialParameters> crypto_parameters,
117 const BufferSource& attestation_challenge, 156 const BufferSource& attestation_challenge,
118 ScopedCredentialOptions& options) { 157 ScopedCredentialOptions& options) {
119 ExecutionContext* executionContext = script_state->GetExecutionContext(); 158 ScriptPromise promise = rejectIfNotSupported(script_state);
120 159 if (!promise.IsEmpty())
121 if (!m_authenticator) { 160 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 161
132 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); 162 ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
133 ScriptPromise promise = resolver->Promise();
134 163
135 // TODO(kpaulhamus) validate parameters according to spec
136 Vector<uint8_t> buffer = mojo::convertBufferSource(attestation_challenge); 164 Vector<uint8_t> buffer = mojo::convertBufferSource(attestation_challenge);
137 auto opts = mojo::convertScopedCredentialOptions(options, resolver); 165 auto opts = mojo::convertScopedCredentialOptions(options, resolver);
138 Vector<webauth::mojom::blink::ScopedCredentialParametersPtr> parameters; 166 Vector<webauth::mojom::blink::ScopedCredentialParametersPtr> parameters;
139 for (const auto& parameter : crypto_parameters) { 167 for (const auto& parameter : crypto_parameters) {
140 parameters.push_back( 168 if (parameter.hasType()) { // TODO add algorithm
141 mojo::convertScopedCredentialParameter(parameter, resolver)); 169 parameters.push_back(
170 mojo::convertScopedCredentialParameter(parameter, resolver));
171 }
142 } 172 }
143 173
144 m_authenticatorRequests.insert(resolver); 174 m_authenticatorRequests.insert(resolver);
145 m_authenticator->makeCredential( 175 m_authenticator->MakeCredential(
146 account_information, std::move(parameters), buffer, std::move(opts), 176 account_information, std::move(parameters), buffer, std::move(opts),
147 ConvertToBaseCallback(WTF::Bind(&WebAuthentication::onMakeCredential, 177 ConvertToBaseCallback(WTF::Bind(&WebAuthentication::onMakeCredential,
148 WrapPersistent(this), 178 WrapPersistent(this),
149 WrapPersistent(resolver)))); 179 WrapPersistent(resolver))));
150 return promise; 180 return resolver->Promise();
151 } 181 }
152 182
153 ScriptPromise WebAuthentication::getAssertion( 183 ScriptPromise WebAuthentication::getAssertion(
154 ScriptState* script_state, 184 ScriptState* script_state,
155 const BufferSource& assertion_challenge, 185 const BufferSource& assertion_challenge,
156 const AuthenticationAssertionOptions& options) { 186 const AuthenticationAssertionOptions& options) {
157 NOTREACHED(); 187 NOTREACHED();
158 return ScriptPromise(); 188 return ScriptPromise();
159 } 189 }
160 190
161 void WebAuthentication::ContextDestroyed(ExecutionContext*) { 191 void WebAuthentication::ContextDestroyed(ExecutionContext*) {
162 m_authenticator.reset(); 192 cleanup();
163 m_authenticatorRequests.Clear(); 193 }
194
195 // Step 11 of https://w3c.github.io/webauthn/#makeCredential
196 void WebAuthentication::onMakeCredential(
197 ScriptPromiseResolver* resolver,
198 webauth::mojom::blink::AuthenticatorStatus status,
199 webauth::mojom::blink::ScopedCredentialInfoPtr credential) {
200 if (!markRequestComplete(resolver))
201 return;
202
203 DOMException* error = mojo::createExceptionFromStatus(status);
204 if (error) {
205 resolver->Reject(error);
206 cleanup();
207 return;
208 }
209
210 if (credential->client_data.IsEmpty() || credential->attestation.IsEmpty()) {
211 resolver->Reject(
212 DOMException::Create(kNotFoundError, "No credential returned."));
213 return;
214 }
215
216 DOMArrayBuffer* clientDataBuffer = DOMArrayBuffer::Create(
217 static_cast<void*>(&credential->client_data.front()),
218 credential->client_data.size());
219
220 DOMArrayBuffer* attestationBuffer = DOMArrayBuffer::Create(
221 static_cast<void*>(&credential->attestation.front()),
222 credential->attestation.size());
223
224 ScopedCredentialInfo* scopedCredential =
225 ScopedCredentialInfo::Create(clientDataBuffer, attestationBuffer);
226 resolver->Resolve(scopedCredential);
227 }
228
229 ScriptPromise WebAuthentication::rejectIfNotSupported(
230 ScriptState* script_state) {
231 ExecutionContext* executionContext = script_state->GetExecutionContext();
232
233 if (!m_authenticator) {
234 if (!GetFrame()) {
235 return ScriptPromise::RejectWithDOMException(
236 script_state, DOMException::Create(kNotSupportedError));
237 }
238 GetFrame()->GetInterfaceProvider()->GetInterface(
239 mojo::MakeRequest(&m_authenticator));
240
241 m_authenticator.set_connection_error_handler(ConvertToBaseCallback(
242 WTF::Bind(&WebAuthentication::onAuthenticatorConnectionError,
243 WrapWeakPersistent(this))));
244 }
245
246 String errorMessage;
247 if (!executionContext->IsSecureContext(errorMessage)) {
248 return ScriptPromise::RejectWithDOMException(
249 script_state, DOMException::Create(kSecurityError, errorMessage));
250 }
251
252 return ScriptPromise();
164 } 253 }
165 254
166 void WebAuthentication::onAuthenticatorConnectionError() { 255 void WebAuthentication::onAuthenticatorConnectionError() {
167 m_authenticator.reset();
168 for (ScriptPromiseResolver* resolver : m_authenticatorRequests) { 256 for (ScriptPromiseResolver* resolver : m_authenticatorRequests) {
169 resolver->Reject( 257 resolver->Reject(
170 DOMException::Create(kNotFoundError, kNoAuthenticatorError)); 258 DOMException::Create(kNotFoundError, kNoAuthenticatorError));
171 } 259 }
172 m_authenticatorRequests.Clear(); 260 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 } 261 }
202 262
203 bool WebAuthentication::markRequestComplete(ScriptPromiseResolver* resolver) { 263 bool WebAuthentication::markRequestComplete(ScriptPromiseResolver* resolver) {
204 auto requestEntry = m_authenticatorRequests.Find(resolver); 264 auto requestEntry = m_authenticatorRequests.Find(resolver);
205 if (requestEntry == m_authenticatorRequests.end()) 265 if (requestEntry == m_authenticatorRequests.end())
206 return false; 266 return false;
207 m_authenticatorRequests.erase(requestEntry); 267 m_authenticatorRequests.erase(requestEntry);
208 return true; 268 return true;
209 } 269 }
210 270
271 // Clears the promise resolver and closes the Mojo connection.
272 void WebAuthentication::cleanup() {
273 m_authenticator.reset();
274 m_authenticatorRequests.Clear();
275 }
276
211 DEFINE_TRACE(WebAuthentication) { 277 DEFINE_TRACE(WebAuthentication) {
212 visitor->Trace(m_authenticatorRequests); 278 visitor->Trace(m_authenticatorRequests);
213 ContextLifecycleObserver::Trace(visitor); 279 ContextLifecycleObserver::Trace(visitor);
214 } 280 }
215 281
216 } // namespace blink 282 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698