diff --git a/Xcodes.xcodeproj/project.pbxproj b/Xcodes.xcodeproj/project.pbxproj index 4b79c55c..dc39b3af 100644 --- a/Xcodes.xcodeproj/project.pbxproj +++ b/Xcodes.xcodeproj/project.pbxproj @@ -1493,7 +1493,7 @@ repositoryURL = "https://github.com/kinoroy/LibFido2Swift.git"; requirement = { kind = upToNextMinorVersion; - minimumVersion = 0.1.0; + minimumVersion = 0.1.4; }; }; CA9FF86B25951C6E00E47BAF /* XCRemoteSwiftPackageReference "data" */ = { diff --git a/Xcodes.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Xcodes.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index f90bb44b..84e33365 100644 --- a/Xcodes.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Xcodes.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -78,8 +78,8 @@ "repositoryURL": "https://github.com/kinoroy/LibFido2Swift.git", "state": { "branch": null, - "revision": "b77e5c6451bea69d15615d6578936b11777d9a6c", - "version": "0.1.2" + "revision": "94d496d6f850dcbb3e8c4a27cd7eeabfad9f14e3", + "version": "0.1.4" } }, { diff --git a/Xcodes/Backend/AppState.swift b/Xcodes/Backend/AppState.swift index 475602fe..e3997a11 100644 --- a/Xcodes/Backend/AppState.swift +++ b/Xcodes/Backend/AppState.swift @@ -305,11 +305,17 @@ class AppState: ObservableObject { } func handleTwoFactorOption(_ option: TwoFactorOption, authOptions: AuthOptionsResponse, serviceKey: String, sessionID: String, scnt: String) { - self.presentedSheet = .twoFactor(.init( - option: option, - authOptions: authOptions, - sessionData: AppleSessionData(serviceKey: serviceKey, sessionID: sessionID, scnt: scnt) - )) + let sessionData = AppleSessionData(serviceKey: serviceKey, sessionID: sessionID, scnt: scnt) + + if option == .securityKey, fido2DeviceIsPresent() && !fido2DeviceNeedsPin() { + createAndSubmitSecurityKeyAssertationWithPinCode(nil, sessionData: sessionData, authOptions: authOptions) + } else { + self.presentedSheet = .twoFactor(.init( + option: option, + authOptions: authOptions, + sessionData: sessionData + )) + } } func requestSMS(to trustedPhoneNumber: AuthOptionsResponse.TrustedPhoneNumber, authOptions: AuthOptionsResponse, sessionData: AppleSessionData) { @@ -355,9 +361,9 @@ class AppState: ObservableObject { .store(in: &cancellables) } - var fido2: FIDO2? - - func createAndSubmitSecurityKeyAssertationWithPinCode(_ pinCode: String, sessionData: AppleSessionData, authOptions: AuthOptionsResponse) { + private lazy var fido2 = FIDO2() + + func createAndSubmitSecurityKeyAssertationWithPinCode(_ pinCode: String?, sessionData: AppleSessionData, authOptions: AuthOptionsResponse) { self.presentedSheet = .securityKeyTouchToConfirm guard let fsaChallenge = authOptions.fsaChallenge else { @@ -379,8 +385,6 @@ class AppState: ObservableObject { Task { do { - let fido2 = FIDO2() - self.fido2 = fido2 let response = try fido2.respondToChallenge(args: ChallengeArgs(rpId: rpId, validCredentials: validCreds, devPin: pinCode, challenge: challenge, origin: origin)) Task { @MainActor in @@ -407,13 +411,31 @@ class AppState: ObservableObject { // we don't have to show an error // because the sheet will already be dismissed } catch { + Task { @MainActor in + authError = error + } + } + } + } + + func fido2DeviceIsPresent() -> Bool { + fido2.hasDeviceAttached() + } + + func fido2DeviceNeedsPin() -> Bool { + do { + return try fido2.deviceHasPin() + } catch { + Task { @MainActor in authError = error } + + return true } } func cancelSecurityKeyAssertationRequest() { - self.fido2?.cancel() + self.fido2.cancel() } private func handleAuthenticationFlowCompletion(_ completion: Subscribers.Completion) { diff --git a/Xcodes/Frontend/SignIn/SignInSecurityKeyPinView.swift b/Xcodes/Frontend/SignIn/SignInSecurityKeyPinView.swift index 32b6028c..d2b16465 100644 --- a/Xcodes/Frontend/SignIn/SignInSecurityKeyPinView.swift +++ b/Xcodes/Frontend/SignIn/SignInSecurityKeyPinView.swift @@ -32,6 +32,9 @@ struct SignInSecurityKeyPinView: View { Button("Cancel", action: { isPresented = false }) .keyboardShortcut(.cancelAction) Spacer() + + Button("PIN not set", action: submitWithoutPinCode) + ProgressButton(isInProgress: appState.isProcessingAuthRequest, action: submitPinCode) { Text("Continue") @@ -50,6 +53,10 @@ struct SignInSecurityKeyPinView: View { func submitPinCode() { appState.createAndSubmitSecurityKeyAssertationWithPinCode(pin, sessionData: sessionData, authOptions: authOptions) } + + func submitWithoutPinCode() { + appState.createAndSubmitSecurityKeyAssertationWithPinCode(nil, sessionData: sessionData, authOptions: authOptions) + } } #Preview { diff --git a/Xcodes/Resources/Localizable.xcstrings b/Xcodes/Resources/Localizable.xcstrings index 24fc217b..e80c58bd 100644 --- a/Xcodes/Resources/Localizable.xcstrings +++ b/Xcodes/Resources/Localizable.xcstrings @@ -237,6 +237,16 @@ } } }, + "%@ (%@)" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$@ (%2$@)" + } + } + } + }, "%@ %@ %@" : { "localizations" : { "ar" : { @@ -17907,6 +17917,9 @@ } } } + }, + "PIN not set" : { + }, "Platforms" : { "localizations" : {