Add WKBundlePage SPI to temporarily force light or dark appearance on a page.
[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 #include <wtf/MonotonicTime.h>
34
35 namespace WebKit {
36 using namespace WebCore;
37
38 namespace AuthenticatorManagerInternal {
39
40 #if PLATFORM(MAC)
41 const size_t maxTransportNumber = 2;
42 #else
43 const size_t maxTransportNumber = 1;
44 #endif
45
46 // Suggested by WebAuthN spec as of 7 August 2018.
47 const unsigned maxTimeOutValue = 120000;
48
49 // FIXME(188624, 188625): Support NFC and BLE authenticators.
50 static AuthenticatorManager::TransportSet collectTransports(const Optional<PublicKeyCredentialCreationOptions::AuthenticatorSelectionCriteria>& authenticatorSelection)
51 {
52     AuthenticatorManager::TransportSet result;
53     if (!authenticatorSelection || !authenticatorSelection->authenticatorAttachment) {
54         auto addResult = result.add(AuthenticatorTransport::Internal);
55         ASSERT_UNUSED(addResult, addResult.isNewEntry);
56 #if PLATFORM(MAC)
57         addResult = result.add(AuthenticatorTransport::Usb);
58         ASSERT_UNUSED(addResult, addResult.isNewEntry);
59 #endif
60         return result;
61     }
62
63     if (authenticatorSelection->authenticatorAttachment == PublicKeyCredentialCreationOptions::AuthenticatorAttachment::Platform) {
64         auto addResult = result.add(AuthenticatorTransport::Internal);
65         ASSERT_UNUSED(addResult, addResult.isNewEntry);
66         return result;
67     }
68     if (authenticatorSelection->authenticatorAttachment == PublicKeyCredentialCreationOptions::AuthenticatorAttachment::CrossPlatform) {
69 #if PLATFORM(MAC)
70         auto addResult = result.add(AuthenticatorTransport::Usb);
71         ASSERT_UNUSED(addResult, addResult.isNewEntry);
72 #endif
73         return result;
74     }
75
76     ASSERT_NOT_REACHED();
77     return result;
78 }
79
80 // FIXME(188624, 188625): Support NFC and BLE authenticators.
81 // The goal is to find a union of different transports from allowCredentials.
82 // If it is not specified or any of its credentials doesn't specify its own. We should discover all.
83 // This is a variant of Step. 18.*.4 from https://www.w3.org/TR/webauthn/#discover-from-external-source
84 // as of 7 August 2018.
85 static AuthenticatorManager::TransportSet collectTransports(const Vector<PublicKeyCredentialDescriptor>& allowCredentials)
86 {
87     AuthenticatorManager::TransportSet result;
88     if (allowCredentials.isEmpty()) {
89         auto addResult = result.add(AuthenticatorTransport::Internal);
90         ASSERT_UNUSED(addResult, addResult.isNewEntry);
91 #if PLATFORM(MAC)
92         addResult = result.add(AuthenticatorTransport::Usb);
93         ASSERT_UNUSED(addResult, addResult.isNewEntry);
94 #endif
95         return result;
96     }
97
98     for (auto& allowCredential : allowCredentials) {
99         if (allowCredential.transports.isEmpty()) {
100             result.add(AuthenticatorTransport::Internal);
101 #if PLATFORM(MAC)
102             result.add(AuthenticatorTransport::Usb);
103             return result;
104 #endif
105         }
106         if (!result.contains(AuthenticatorTransport::Internal) && allowCredential.transports.contains(AuthenticatorTransport::Internal))
107             result.add(AuthenticatorTransport::Internal);
108 #if PLATFORM(MAC)
109         if (!result.contains(AuthenticatorTransport::Usb) && allowCredential.transports.contains(AuthenticatorTransport::Usb))
110             result.add(AuthenticatorTransport::Usb);
111 #endif
112         if (result.size() >= maxTransportNumber)
113             return result;
114     }
115
116     ASSERT(result.size() < maxTransportNumber);
117     return result;
118 }
119
120 } // namespace AuthenticatorManagerInternal
121
122 AuthenticatorManager::AuthenticatorManager()
123     : m_requestTimeOutTimer(RunLoop::main(), this, &AuthenticatorManager::timeOutTimerFired)
124 {
125 }
126
127 void AuthenticatorManager::makeCredential(const Vector<uint8_t>& hash, const PublicKeyCredentialCreationOptions& options, Callback&& callback)
128 {
129     using namespace AuthenticatorManagerInternal;
130
131     if (m_pendingCompletionHandler) {
132         callback(ExceptionData { NotAllowedError, "A request is pending."_s });
133         return;
134     }
135
136     // 1. Save request for async operations.
137     m_pendingRequestData = { hash, true, options, { } };
138     m_pendingCompletionHandler = WTFMove(callback);
139     initTimeOutTimer(options.timeout);
140
141     // 2. Get available transports and start discovering authenticators on them.
142     startDiscovery(collectTransports(options.authenticatorSelection));
143 }
144
145 void AuthenticatorManager::getAssertion(const Vector<uint8_t>& hash, const PublicKeyCredentialRequestOptions& options, Callback&& callback)
146 {
147     using namespace AuthenticatorManagerInternal;
148
149     if (m_pendingCompletionHandler) {
150         callback(ExceptionData { NotAllowedError, "A request is pending."_s });
151         return;
152     }
153
154     // 1. Save request for async operations.
155     m_pendingRequestData = { hash, false, { }, options };
156     m_pendingCompletionHandler = WTFMove(callback);
157     initTimeOutTimer(options.timeout);
158
159     // 2. Get available transports and start discovering authenticators on them.
160     ASSERT(m_services.isEmpty());
161     startDiscovery(collectTransports(options.allowCredentials));
162 }
163
164 void AuthenticatorManager::clearStateAsync()
165 {
166     RunLoop::main().dispatch([weakThis = makeWeakPtr(*this)] {
167         if (!weakThis)
168             return;
169         weakThis->m_pendingRequestData = { };
170         ASSERT(!weakThis->m_pendingCompletionHandler);
171         weakThis->m_services.clear();
172         weakThis->m_authenticators.clear();
173     });
174 }
175
176 void AuthenticatorManager::authenticatorAdded(Ref<Authenticator>&& authenticator)
177 {
178     ASSERT(RunLoop::isMain());
179     authenticator->setObserver(*this);
180     authenticator->handleRequest(m_pendingRequestData);
181     auto addResult = m_authenticators.add(WTFMove(authenticator));
182     ASSERT_UNUSED(addResult, addResult.isNewEntry);
183 }
184
185 void AuthenticatorManager::respondReceived(Respond&& respond)
186 {
187     ASSERT(RunLoop::isMain());
188     if (!m_requestTimeOutTimer.isActive())
189         return;
190
191     ASSERT(m_pendingCompletionHandler);
192     if (WTF::holds_alternative<PublicKeyCredentialData>(respond)) {
193         m_pendingCompletionHandler(WTFMove(respond));
194         clearStateAsync();
195         // FIXME(192061)
196         LOG_ERROR("Stop timer.");
197         m_requestTimeOutTimer.stop();
198         return;
199     }
200     respondReceivedInternal(WTFMove(respond));
201 }
202
203 UniqueRef<AuthenticatorTransportService> AuthenticatorManager::createService(WebCore::AuthenticatorTransport transport, AuthenticatorTransportService::Observer& observer) const
204 {
205     return AuthenticatorTransportService::create(transport, observer);
206 }
207
208 void AuthenticatorManager::respondReceivedInternal(Respond&&)
209 {
210 }
211
212 void AuthenticatorManager::startDiscovery(const TransportSet& transports)
213 {
214     using namespace AuthenticatorManagerInternal;
215
216     ASSERT(m_services.isEmpty() && transports.size() <= maxTransportNumber);
217     for (auto& transport : transports) {
218         auto service = createService(transport, *this);
219         service->startDiscovery();
220         m_services.append(WTFMove(service));
221     }
222 }
223
224 void AuthenticatorManager::initTimeOutTimer(const Optional<unsigned>& timeOutInMs)
225 {
226     using namespace AuthenticatorManagerInternal;
227
228     unsigned timeOutInMsValue = std::min(maxTimeOutValue, timeOutInMs.valueOr(maxTimeOutValue));
229     // FIXME(192061)
230     LOG_ERROR("Start timer: %d. Current time: %f.", timeOutInMsValue, MonotonicTime::now().secondsSinceEpoch().value());
231     m_requestTimeOutTimer.startOneShot(Seconds::fromMilliseconds(timeOutInMsValue));
232     LOG_ERROR("Seconds until fire: %f", m_requestTimeOutTimer.secondsUntilFire().value());
233 }
234
235 void AuthenticatorManager::timeOutTimerFired()
236 {
237     ASSERT(m_requestTimeOutTimer.isActive());
238     // FIXME(192061)
239     LOG_ERROR("Timer fired: %f, Current time: %f", m_requestTimeOutTimer.secondsUntilFire().value(), MonotonicTime::now().secondsSinceEpoch().value());
240     m_pendingCompletionHandler((ExceptionData { NotAllowedError, "Operation timed out."_s }));
241     clearStateAsync();
242 }
243
244 } // namespace WebKit
245
246 #endif // ENABLE(WEB_AUTHN)