Use Optional::valueOr() instead of Optional::value_or()
[WebKit-https.git] / Source / WebKit / UIProcess / WebAuthentication / AuthenticatorManager.cpp
1 /*
2  * Copyright (C) 2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "AuthenticatorManager.h"
28
29 #if ENABLE(WEB_AUTHN)
30
31 #include <WebCore/AuthenticatorTransport.h>
32 #include <WebCore/PublicKeyCredentialCreationOptions.h>
33
34 namespace WebKit {
35 using namespace WebCore;
36
37 namespace AuthenticatorManagerInternal {
38
39 #if PLATFORM(MAC)
40 const size_t maxTransportNumber = 2;
41 #else
42 const size_t maxTransportNumber = 1;
43 #endif
44
45 // Suggested by WebAuthN spec as of 7 August 2018.
46 const unsigned maxTimeOutValue = 120000;
47
48 // FIXME(188624, 188625): Support NFC and BLE authenticators.
49 static AuthenticatorManager::TransportSet collectTransports(const Optional<PublicKeyCredentialCreationOptions::AuthenticatorSelectionCriteria>& authenticatorSelection)
50 {
51     AuthenticatorManager::TransportSet result;
52     if (!authenticatorSelection || !authenticatorSelection->authenticatorAttachment) {
53         auto addResult = result.add(AuthenticatorTransport::Internal);
54         ASSERT_UNUSED(addResult, addResult.isNewEntry);
55 #if PLATFORM(MAC)
56         addResult = result.add(AuthenticatorTransport::Usb);
57         ASSERT_UNUSED(addResult, addResult.isNewEntry);
58 #endif
59         return result;
60     }
61
62     if (authenticatorSelection->authenticatorAttachment == PublicKeyCredentialCreationOptions::AuthenticatorAttachment::Platform) {
63         auto addResult = result.add(AuthenticatorTransport::Internal);
64         ASSERT_UNUSED(addResult, addResult.isNewEntry);
65         return result;
66     }
67     if (authenticatorSelection->authenticatorAttachment == PublicKeyCredentialCreationOptions::AuthenticatorAttachment::CrossPlatform) {
68 #if PLATFORM(MAC)
69         auto addResult = result.add(AuthenticatorTransport::Usb);
70         ASSERT_UNUSED(addResult, addResult.isNewEntry);
71 #endif
72         return result;
73     }
74
75     ASSERT_NOT_REACHED();
76     return result;
77 }
78
79 // FIXME(188624, 188625): Support NFC and BLE authenticators.
80 // The goal is to find a union of different transports from allowCredentials.
81 // If it is not specified or any of its credentials doesn't specify its own. We should discover all.
82 // This is a variant of Step. 18.*.4 from https://www.w3.org/TR/webauthn/#discover-from-external-source
83 // as of 7 August 2018.
84 static AuthenticatorManager::TransportSet collectTransports(const Vector<PublicKeyCredentialDescriptor>& allowCredentials)
85 {
86     AuthenticatorManager::TransportSet result;
87     if (allowCredentials.isEmpty()) {
88         auto addResult = result.add(AuthenticatorTransport::Internal);
89         ASSERT_UNUSED(addResult, addResult.isNewEntry);
90 #if PLATFORM(MAC)
91         addResult = result.add(AuthenticatorTransport::Usb);
92         ASSERT_UNUSED(addResult, addResult.isNewEntry);
93 #endif
94         return result;
95     }
96
97     for (auto& allowCredential : allowCredentials) {
98         if (allowCredential.transports.isEmpty()) {
99             result.add(AuthenticatorTransport::Internal);
100 #if PLATFORM(MAC)
101             result.add(AuthenticatorTransport::Usb);
102             return result;
103 #endif
104         }
105         if (!result.contains(AuthenticatorTransport::Internal) && allowCredential.transports.contains(AuthenticatorTransport::Internal))
106             result.add(AuthenticatorTransport::Internal);
107 #if PLATFORM(MAC)
108         if (!result.contains(AuthenticatorTransport::Usb) && allowCredential.transports.contains(AuthenticatorTransport::Usb))
109             result.add(AuthenticatorTransport::Usb);
110 #endif
111         if (result.size() >= maxTransportNumber)
112             return result;
113     }
114
115     ASSERT(result.size() < maxTransportNumber);
116     return result;
117 }
118
119 } // namespace AuthenticatorManagerInternal
120
121 AuthenticatorManager::AuthenticatorManager()
122     : m_requestTimeOutTimer(RunLoop::main(), this, &AuthenticatorManager::timeOutTimerFired)
123 {
124 }
125
126 void AuthenticatorManager::makeCredential(const Vector<uint8_t>& hash, const PublicKeyCredentialCreationOptions& options, Callback&& callback)
127 {
128     using namespace AuthenticatorManagerInternal;
129
130     if (m_pendingCompletionHandler) {
131         callback(ExceptionData { NotAllowedError, "A request is pending."_s });
132         return;
133     }
134
135     // 1. Save request for async operations.
136     m_pendingRequestData = { hash, true, options, { } };
137     m_pendingCompletionHandler = WTFMove(callback);
138     initTimeOutTimer(options.timeout);
139
140     // 2. Get available transports and start discovering authenticators on them.
141     startDiscovery(collectTransports(options.authenticatorSelection));
142 }
143
144 void AuthenticatorManager::getAssertion(const Vector<uint8_t>& hash, const PublicKeyCredentialRequestOptions& options, Callback&& callback)
145 {
146     using namespace AuthenticatorManagerInternal;
147
148     if (m_pendingCompletionHandler) {
149         callback(ExceptionData { NotAllowedError, "A request is pending."_s });
150         return;
151     }
152
153     // 1. Save request for async operations.
154     m_pendingRequestData = { hash, false, { }, options };
155     m_pendingCompletionHandler = WTFMove(callback);
156     initTimeOutTimer(options.timeout);
157
158     // 2. Get available transports and start discovering authenticators on them.
159     ASSERT(m_services.isEmpty());
160     startDiscovery(collectTransports(options.allowCredentials));
161 }
162
163 void AuthenticatorManager::clearStateAsync()
164 {
165     RunLoop::main().dispatch([weakThis = makeWeakPtr(*this)] {
166         if (!weakThis)
167             return;
168         weakThis->m_pendingRequestData = { };
169         ASSERT(!weakThis->m_pendingCompletionHandler);
170         weakThis->m_services.clear();
171         weakThis->m_authenticators.clear();
172     });
173 }
174
175 void AuthenticatorManager::authenticatorAdded(Ref<Authenticator>&& authenticator)
176 {
177     ASSERT(RunLoop::isMain());
178     authenticator->setObserver(*this);
179     authenticator->handleRequest(m_pendingRequestData);
180     auto addResult = m_authenticators.add(WTFMove(authenticator));
181     ASSERT_UNUSED(addResult, addResult.isNewEntry);
182 }
183
184 void AuthenticatorManager::respondReceived(Respond&& respond)
185 {
186     ASSERT(RunLoop::isMain());
187     if (!m_requestTimeOutTimer.isActive())
188         return;
189
190     ASSERT(m_pendingCompletionHandler);
191     if (WTF::holds_alternative<PublicKeyCredentialData>(respond)) {
192         m_pendingCompletionHandler(WTFMove(respond));
193         clearStateAsync();
194         // FIXME(192061)
195         LOG_ERROR("Stop timer.");
196         m_requestTimeOutTimer.stop();
197         return;
198     }
199     respondReceivedInternal(WTFMove(respond));
200 }
201
202 UniqueRef<AuthenticatorTransportService> AuthenticatorManager::createService(WebCore::AuthenticatorTransport transport, AuthenticatorTransportService::Observer& observer) const
203 {
204     return AuthenticatorTransportService::create(transport, observer);
205 }
206
207 void AuthenticatorManager::respondReceivedInternal(Respond&&)
208 {
209 }
210
211 void AuthenticatorManager::startDiscovery(const TransportSet& transports)
212 {
213     using namespace AuthenticatorManagerInternal;
214
215     ASSERT(m_services.isEmpty() && transports.size() <= maxTransportNumber);
216     for (auto& transport : transports) {
217         auto service = createService(transport, *this);
218         service->startDiscovery();
219         m_services.append(WTFMove(service));
220     }
221 }
222
223 void AuthenticatorManager::initTimeOutTimer(const Optional<unsigned>& timeOutInMs)
224 {
225     using namespace AuthenticatorManagerInternal;
226
227     unsigned timeOutInMsValue = std::min(maxTimeOutValue, timeOutInMs.valueOr(maxTimeOutValue));
228     // FIXME(192061)
229     LOG_ERROR("Start timer. %d", timeOutInMsValue);
230     m_requestTimeOutTimer.startOneShot(Seconds::fromMilliseconds(timeOutInMsValue));
231 }
232
233 void AuthenticatorManager::timeOutTimerFired()
234 {
235     ASSERT(m_requestTimeOutTimer.isActive());
236     // FIXME(192061)
237     LOG_ERROR("Timer fired.");
238     m_pendingCompletionHandler((ExceptionData { NotAllowedError, "Operation timed out."_s }));
239     clearStateAsync();
240 }
241
242 } // namespace WebKit
243
244 #endif // ENABLE(WEB_AUTHN)