1 // Copyright 2018 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef CHROME_BROWSER_WEBAUTHN_AUTHENTICATOR_REQUEST_DIALOG_MODEL_H_ 6 #define CHROME_BROWSER_WEBAUTHN_AUTHENTICATOR_REQUEST_DIALOG_MODEL_H_ 7 8 #include <memory> 9 #include <string> 10 #include <vector> 11 12 #include "base/containers/span.h" 13 #include "base/memory/weak_ptr.h" 14 #include "base/observer_list.h" 15 #include "base/optional.h" 16 #include "base/strings/string16.h" 17 #include "base/strings/string_piece.h" 18 #include "base/values.h" 19 #include "build/build_config.h" 20 #include "chrome/browser/webauthn/authenticator_reference.h" 21 #include "chrome/browser/webauthn/authenticator_transport.h" 22 #include "chrome/browser/webauthn/observable_authenticator_list.h" 23 #include "device/fido/fido_request_handler_base.h" 24 #include "device/fido/fido_transport_protocol.h" 25 26 namespace device { 27 class AuthenticatorGetAssertionResponse; 28 } 29 30 // Encapsulates the model behind the Web Authentication request dialog's UX 31 // flow. This is essentially a state machine going through the states defined in 32 // the `Step` enumeration. 33 // 34 // Ultimately, this will become an observer of the AuthenticatorRequest, and 35 // contain the logic to figure out which steps the user needs to take, in which 36 // order, to complete the authentication flow. 37 class AuthenticatorRequestDialogModel { 38 public: 39 using RequestCallback = device::FidoRequestHandlerBase::RequestCallback; 40 using BlePairingCallback = device::FidoRequestHandlerBase::BlePairingCallback; 41 using BleDevicePairedCallback = base::RepeatingCallback<void(std::string)>; 42 using TransportAvailabilityInfo = 43 device::FidoRequestHandlerBase::TransportAvailabilityInfo; 44 45 // Defines the potential steps of the Web Authentication API request UX flow. 46 enum class Step { 47 // The UX flow has not started yet, the dialog should still be hidden. 48 kNotStarted, 49 50 kTransportSelection, 51 52 // The request errored out before completing. Error will only be sent 53 // after user interaction. 54 kErrorNoAvailableTransports, 55 kErrorInternalUnrecognized, 56 57 // The request is already complete, but the error dialog should wait 58 // until user acknowledgement. 59 kTimedOut, 60 kKeyNotRegistered, 61 kKeyAlreadyRegistered, 62 kMissingCapability, 63 kStorageFull, 64 65 // The request is completed, and the dialog should be closed. 66 kClosed, 67 68 // Universal Serial Bus (USB). 69 kUsbInsertAndActivate, 70 71 // Bluetooth Low Energy (BLE). 72 kBlePowerOnAutomatic, 73 kBlePowerOnManual, 74 75 // Touch ID. 76 kTouchIdIncognitoSpeedBump, 77 78 // Phone as a security key. 79 kCableActivate, 80 kCableV2Activate, 81 kCableV2QRCode, 82 83 // Authenticator Client PIN. 84 kClientPinEntry, 85 kClientPinSetup, 86 kClientPinTapAgain, 87 kClientPinErrorSoftBlock, 88 kClientPinErrorHardBlock, 89 kClientPinErrorAuthenticatorRemoved, 90 91 // Authenticator Internal User Verification 92 kInlineBioEnrollment, 93 kRetryInternalUserVerification, 94 95 // Confirm user consent to create a resident credential. Used prior to 96 // triggering Windows-native APIs when Windows itself won't show any 97 // notice about resident credentials. 98 kResidentCredentialConfirmation, 99 100 // Account selection, 101 kSelectAccount, 102 103 // Attestation permission request. 104 kAttestationPermissionRequest, 105 }; 106 107 // Implemented by the dialog to observe this model and show the UI panels 108 // appropriate for the current step. 109 class Observer { 110 public: 111 // Called when the user clicks "Try Again" to restart the user flow. OnStartOver()112 virtual void OnStartOver() {} 113 114 // Called just before the model is destructed. 115 virtual void OnModelDestroyed() = 0; 116 117 // Called when the UX flow has navigated to a different step, so the UI 118 // should update. OnStepTransition()119 virtual void OnStepTransition() {} 120 121 // Called when the model corresponding to the current sheet of the UX flow 122 // was updated, so UI should update. OnSheetModelChanged()123 virtual void OnSheetModelChanged() {} 124 125 // Called when the power state of the Bluetooth adapter has changed. OnBluetoothPoweredStateChanged()126 virtual void OnBluetoothPoweredStateChanged() {} 127 128 // Called when the user cancelled WebAuthN request by clicking the 129 // "cancel" button or the back arrow in the UI dialog. OnCancelRequest()130 virtual void OnCancelRequest() {} 131 }; 132 133 explicit AuthenticatorRequestDialogModel(const std::string& relying_party_id); 134 ~AuthenticatorRequestDialogModel(); 135 136 void SetCurrentStep(Step step); current_step()137 Step current_step() const { return current_step_; } 138 139 // Hides the dialog. A subsequent call to SetCurrentStep() will unhide it. 140 void HideDialog(); 141 142 // Returns whether the UI is in a state at which the |request_| member of 143 // AuthenticatorImpl has completed processing. Note that the request callback 144 // is only resolved after the UI is dismissed. is_request_complete()145 bool is_request_complete() const { 146 return current_step() == Step::kTimedOut || 147 current_step() == Step::kKeyNotRegistered || 148 current_step() == Step::kKeyAlreadyRegistered || 149 current_step() == Step::kMissingCapability || 150 current_step() == Step::kClosed; 151 } 152 should_dialog_be_closed()153 bool should_dialog_be_closed() const { 154 return current_step() == Step::kClosed; 155 } should_dialog_be_hidden()156 bool should_dialog_be_hidden() const { 157 return current_step() == Step::kNotStarted; 158 } 159 transport_availability()160 const TransportAvailabilityInfo* transport_availability() const { 161 return &transport_availability_; 162 } 163 ble_adapter_is_powered()164 bool ble_adapter_is_powered() const { 165 return transport_availability()->is_ble_powered; 166 } 167 selected_authenticator_id()168 const base::Optional<std::string>& selected_authenticator_id() const { 169 return ephemeral_state_.selected_authenticator_id_; 170 } 171 172 // Starts the UX flow, by either showing the transport selection screen or 173 // the guided flow for them most likely transport. 174 // 175 // Valid action when at step: kNotStarted. 176 void StartFlow( 177 TransportAvailabilityInfo transport_availability, 178 base::Optional<device::FidoTransportProtocol> last_used_transport); 179 180 // Restarts the UX flow. 181 void StartOver(); 182 183 // Starts the UX flow. Tries to figure out the most likely transport to be 184 // used, and starts the guided flow for that transport; or shows the manual 185 // transport selection screen if the transport could not be uniquely 186 // identified. 187 // 188 // Valid action when at step: kNotStarted. 189 void StartGuidedFlowForMostLikelyTransportOrShowTransportSelection(); 190 191 // Requests that the step-by-step wizard flow commence, guiding the user 192 // through using the Secutity Key with the given |transport|. 193 // 194 // Valid action when at step: kNotStarted. 195 // kTransportSelection, and steps where the other transports menu is shown, 196 // namely, kUsbInsertAndActivate, kCableActivate. 197 void StartGuidedFlowForTransport(AuthenticatorTransport transport); 198 199 // Hides the modal Chrome UI dialog and shows the native Windows WebAuthn 200 // UI instead. 201 void HideDialogAndDispatchToNativeWindowsApi(); 202 203 // Displays a resident-key warning if needed and then calls 204 // |HideDialogAndDispatchToNativeWindowsApi|. 205 void StartWinNativeApi(); 206 207 // StartPhonePairing triggers the display of a QR code for pairing a new 208 // phone. 209 void StartPhonePairing(); 210 211 // Ensures that the Bluetooth adapter is powered before proceeding to |step|. 212 // -- If the adapter is powered, advanced directly to |step|. 213 // -- If the adapter is not powered, but Chrome can turn it automatically, 214 // then advanced to the flow to turn on Bluetooth automatically. 215 // -- Otherwise advanced to the manual Bluetooth power on flow. 216 // 217 // Valid action when at step: kNotStarted, kTransportSelection, and steps 218 // where the other transports menu is shown, namely, kUsbInsertAndActivate, 219 // kCableActivate. 220 void EnsureBleAdapterIsPoweredAndContinueWithCable(); 221 222 // Continues with the BLE/caBLE flow now that the Bluetooth adapter is 223 // powered. 224 // 225 // Valid action when at step: kBlePowerOnManual, kBlePowerOnAutomatic. 226 void ContinueWithFlowAfterBleAdapterPowered(); 227 228 // Turns on the BLE adapter automatically. 229 // 230 // Valid action when at step: kBlePowerOnAutomatic. 231 void PowerOnBleAdapter(); 232 233 // Tries if a USB device is present -- the user claims they plugged it in. 234 // 235 // Valid action when at step: kUsbInsert. 236 void TryUsbDevice(); 237 238 // Tries to use Touch ID -- either because the request requires it or because 239 // the user told us to. May show an error for unrecognized credential, or an 240 // Incognito mode interstitial, or proceed straight to the Touch ID prompt. 241 // 242 // Valid action when at all steps. 243 void StartTouchIdFlow(); 244 245 // Proceeds straight to the Touch ID prompt. 246 // 247 // Valid action when at all steps. 248 void HideDialogAndTryTouchId(); 249 250 // Cancels the flow as a result of the user clicking `Cancel` on the UI. 251 // 252 // Valid action at all steps. 253 void Cancel(); 254 255 // Called by the AuthenticatorRequestSheetModel subclasses when their state 256 // changes, which will trigger notifying observers of OnSheetModelChanged. 257 void OnSheetModelDidChange(); 258 259 // The |observer| must either outlive the object, or unregister itself on its 260 // destruction. 261 void AddObserver(Observer* observer); 262 void RemoveObserver(Observer* observer); 263 264 // To be called when the Web Authentication request is complete. 265 void OnRequestComplete(); 266 267 // To be called when Web Authentication request times-out. 268 void OnRequestTimeout(); 269 270 // To be called when the user activates a security key that does not recognize 271 // any of the allowed credentials (during a GetAssertion request). 272 void OnActivatedKeyNotRegistered(); 273 274 // To be called when the user activates a security key that does recognize 275 // one of excluded credentials (during a MakeCredential request). 276 void OnActivatedKeyAlreadyRegistered(); 277 278 // To be called when the selected authenticator cannot currently handle PIN 279 // requests because it needs a power-cycle due to too many failures. 280 void OnSoftPINBlock(); 281 282 // To be called when the selected authenticator must be reset before 283 // performing any PIN operations because of too many failures. 284 void OnHardPINBlock(); 285 286 // To be called when the selected authenticator was removed while 287 // waiting for a PIN to be entered. 288 void OnAuthenticatorRemovedDuringPINEntry(); 289 290 // To be called when the selected authenticator doesn't have the requested 291 // resident key capability. 292 void OnAuthenticatorMissingResidentKeys(); 293 294 // To be called when the selected authenticator doesn't have the requested 295 // user verification capability. 296 void OnAuthenticatorMissingUserVerification(); 297 298 // To be called when the selected authenticator doesn't have the requested 299 // large blob capability. 300 void OnAuthenticatorMissingLargeBlob(); 301 302 // To be called when the selected authenticator doesn't support any of the 303 // COSEAlgorithmIdentifiers requested by the RP. 304 void OnNoCommonAlgorithms(); 305 306 // To be called when the selected authenticator cannot create a resident 307 // credential because of insufficient storage. 308 void OnAuthenticatorStorageFull(); 309 310 // To be called when the user denies consent, e.g. by clicking "Cancel" on the 311 // system Touch ID prompt. 312 void OnUserConsentDenied(); 313 314 // To be called when the user clicks "Cancel" in the native Windows UI. 315 // Returns true if the event was handled. 316 bool OnWinUserCancelled(); 317 318 // To be called when the Bluetooth adapter powered state changes. 319 void OnBluetoothPoweredStateChanged(bool powered); 320 321 void SetRequestCallback(RequestCallback request_callback); 322 323 void SetBluetoothAdapterPowerOnCallback( 324 base::RepeatingClosure bluetooth_adapter_power_on_callback); 325 326 void SetPINCallback(base::OnceCallback<void(std::string)> pin_callback); 327 328 // OnHavePIN is called when the user enters a PIN in the UI. 329 void OnHavePIN(const std::string& pin); 330 331 // Called when the user needs to retry user verification with the number of 332 // |attempts| remaining. 333 void OnRetryUserVerification(int attempts); 334 335 // OnResidentCredentialConfirmed is called when a user accepts a dialog 336 // confirming that they're happy to create a resident credential. 337 void OnResidentCredentialConfirmed(); 338 339 // OnAttestationPermissionResponse is called when the user either allows or 340 // disallows an attestation permission request. 341 void OnAttestationPermissionResponse(bool attestation_permission_granted); 342 343 void AddAuthenticator(const device::FidoAuthenticator& authenticator); 344 void RemoveAuthenticator(base::StringPiece authenticator_id); 345 346 // SelectAccount is called to trigger an account selection dialog. 347 void SelectAccount( 348 std::vector<device::AuthenticatorGetAssertionResponse> responses, 349 base::OnceCallback<void(device::AuthenticatorGetAssertionResponse)> 350 callback); 351 352 // OnAccountSelected is called when one of the accounts from |SelectAccount| 353 // has been picked. |index| is the index of the selected account in 354 // |responses()|. 355 void OnAccountSelected(size_t index); 356 357 void SetSelectedAuthenticatorForTesting(AuthenticatorReference authenticator); 358 saved_authenticators()359 ObservableAuthenticatorList& saved_authenticators() { 360 return ephemeral_state_.saved_authenticators_; 361 } 362 available_transports()363 const base::flat_set<AuthenticatorTransport>& available_transports() { 364 return transport_availability_.available_transports; 365 } 366 cable_qr_string()367 const std::string& cable_qr_string() const { return *cable_qr_string_; } 368 369 void CollectPIN(base::Optional<int> attempts, 370 base::OnceCallback<void(std::string)> provide_pin_cb); has_attempted_pin_entry()371 bool has_attempted_pin_entry() const { 372 return ephemeral_state_.has_attempted_pin_entry_; 373 } pin_attempts()374 base::Optional<int> pin_attempts() const { return pin_attempts_; } 375 376 void StartInlineBioEnrollment(base::OnceClosure next_callback); 377 void OnSampleCollected(int bio_samples_remaining); 378 void OnBioEnrollmentDone(); max_bio_samples()379 base::Optional<int> max_bio_samples() { return max_bio_samples_; } bio_samples_remaining()380 base::Optional<int> bio_samples_remaining() { return bio_samples_remaining_; } 381 382 // Flags the authenticator's internal user verification as locked. set_internal_uv_locked()383 void set_internal_uv_locked() { uv_attempts_ = 0; } uv_attempts()384 base::Optional<int> uv_attempts() const { return uv_attempts_; } 385 386 void RequestAttestationPermission(base::OnceCallback<void(bool)> callback); 387 responses()388 const std::vector<device::AuthenticatorGetAssertionResponse>& responses() { 389 return ephemeral_state_.responses_; 390 } 391 set_has_attempted_pin_entry_for_testing()392 void set_has_attempted_pin_entry_for_testing() { 393 ephemeral_state_.has_attempted_pin_entry_ = true; 394 } 395 set_incognito_mode(bool incognito_mode)396 void set_incognito_mode(bool incognito_mode) { 397 incognito_mode_ = incognito_mode; 398 } 399 might_create_resident_credential()400 bool might_create_resident_credential() const { 401 return might_create_resident_credential_; 402 } 403 set_might_create_resident_credential(bool v)404 void set_might_create_resident_credential(bool v) { 405 might_create_resident_credential_ = v; 406 } 407 408 void set_cable_transport_info( 409 bool cable_extension_provided, 410 bool has_paired_phones, 411 const base::Optional<std::string>& cable_qr_string); 412 win_native_api_enabled()413 bool win_native_api_enabled() const { 414 return transport_availability_.has_win_native_api_authenticator; 415 } 416 cable_extension_provided()417 bool cable_extension_provided() const { return cable_extension_provided_; } 418 relying_party_id()419 const std::string& relying_party_id() const { return relying_party_id_; } 420 offer_try_again_in_ui()421 bool offer_try_again_in_ui() const { return offer_try_again_in_ui_; } 422 423 base::WeakPtr<AuthenticatorRequestDialogModel> GetWeakPtr(); 424 425 private: 426 // Contains the state that will be reset when calling StartOver(). StartOver() 427 // might be called at an arbitrary point of execution. 428 struct EphemeralState { 429 EphemeralState(); 430 ~EphemeralState(); 431 432 void Reset(); 433 434 // Represents the id of the Bluetooth authenticator that the user is trying 435 // to connect to or conduct WebAuthN request to via the WebAuthN UI. 436 base::Optional<std::string> selected_authenticator_id_; 437 438 // Transport type and id of Mac TouchId and BLE authenticators are cached so 439 // that the WebAuthN request for the corresponding authenticators can be 440 // dispatched lazily after the user interacts with the UI element. 441 ObservableAuthenticatorList saved_authenticators_; 442 443 bool has_attempted_pin_entry_ = false; 444 445 // responses_ contains possible accounts to select between. 446 std::vector<device::AuthenticatorGetAssertionResponse> responses_; 447 }; 448 449 void DispatchRequestAsync(AuthenticatorReference* authenticator); 450 void DispatchRequestAsyncInternal(const std::string& authenticator_id); 451 452 EphemeralState ephemeral_state_; 453 454 // relying_party_id is the RP ID from Webauthn, essentially a domain name. 455 const std::string relying_party_id_; 456 457 // The current step of the request UX flow that is currently shown. 458 Step current_step_ = Step::kNotStarted; 459 460 // Determines which step to continue with once the Blueooth adapter is 461 // powered. Only set while the |current_step_| is either kBlePowerOnManual, 462 // kBlePowerOnAutomatic. 463 base::Optional<Step> next_step_once_ble_powered_; 464 465 base::ObserverList<Observer>::Unchecked observers_; 466 467 // These fields are only filled out when the UX flow is started. 468 TransportAvailabilityInfo transport_availability_; 469 base::Optional<device::FidoTransportProtocol> last_used_transport_; 470 471 RequestCallback request_callback_; 472 base::RepeatingClosure bluetooth_adapter_power_on_callback_; 473 474 base::Optional<int> max_bio_samples_; 475 base::Optional<int> bio_samples_remaining_; 476 base::OnceClosure bio_enrollment_callback_; 477 478 base::OnceCallback<void(std::string)> pin_callback_; 479 base::Optional<int> pin_attempts_; 480 base::Optional<int> uv_attempts_; 481 482 base::OnceCallback<void(bool)> attestation_callback_; 483 484 // might_create_resident_credential_ records whether activating an 485 // authenticator may cause a resident credential to be created. A resident 486 // credential may be discovered by someone with physical access to the 487 // authenticator and thus has privacy implications. 488 bool might_create_resident_credential_ = false; 489 490 base::OnceCallback<void(device::AuthenticatorGetAssertionResponse)> 491 selection_callback_; 492 493 bool incognito_mode_ = false; 494 495 // offer_try_again_in_ui_ indicates whether a button to retry the request 496 // should be included on the dialog sheet shown when encountering certain 497 // errors. 498 bool offer_try_again_in_ui_ = true; 499 500 // cable_extension_provided_ indicates whether the request included a caBLE 501 // extension. 502 bool cable_extension_provided_ = false; 503 // have_paired_phones_ indicates whether this profile knows of any paired 504 // phones. 505 bool have_paired_phones_ = false; 506 base::Optional<std::string> cable_qr_string_; 507 // win_native_api_already_tried_ is true if the Windows-native UI has been 508 // displayed already and the user cancelled it. In this case, we shouldn't 509 // jump straight to showing it again. 510 bool win_native_api_already_tried_ = false; 511 512 base::WeakPtrFactory<AuthenticatorRequestDialogModel> weak_factory_{this}; 513 514 DISALLOW_COPY_AND_ASSIGN(AuthenticatorRequestDialogModel); 515 }; 516 517 #endif // CHROME_BROWSER_WEBAUTHN_AUTHENTICATOR_REQUEST_DIALOG_MODEL_H_ 518