From 84f1838e6063722082bd4674ec6d28459870ab9b Mon Sep 17 00:00:00 2001 From: Matt Kiazyk Date: Fri, 14 Feb 2025 11:27:09 -0600 Subject: [PATCH] feat: switch login to use xcodesLoginKit --- Xcodes.xcodeproj/project.pbxproj | 69 +++--- .../xcshareddata/swiftpm/Package.resolved | 17 +- Xcodes/Backend/AppState+Install.swift | 1 + Xcodes/Backend/AppState+Runtimes.swift | 3 +- Xcodes/Backend/AppState+Update.swift | 2 +- Xcodes/Backend/AppState.swift | 220 +++++++----------- Xcodes/Backend/Environment.swift | 1 + Xcodes/Frontend/Common/XcodesSheet.swift | 2 +- Xcodes/Frontend/MainWindow.swift | 2 +- .../Preferences/AdvancedPreferencePane.swift | 2 +- .../Preferences/DownloadPreferencePane.swift | 2 +- .../ExperiementsPreferencePane.swift | 2 +- .../Preferences/GeneralPreferencePane.swift | 4 +- .../Preferences/UpdatesPreferencePane.swift | 2 +- Xcodes/Frontend/SignIn/SignIn2FAView.swift | 2 +- .../Frontend/SignIn/SignInPhoneListView.swift | 2 +- Xcodes/Frontend/SignIn/SignInSMSView.swift | 2 +- .../SignIn/SignInSecurityKeyPinView.swift | 2 +- .../SignIn/SignInSecurityKeyTouchView.swift | 2 +- Xcodes/Resources/Licenses.rtf | 2 +- Xcodes/Resources/Localizable.xcstrings | 10 + Xcodes/XcodesKit/Package.swift | 2 +- XcodesTests/AppStateTests.swift | 2 +- 23 files changed, 163 insertions(+), 192 deletions(-) diff --git a/Xcodes.xcodeproj/project.pbxproj b/Xcodes.xcodeproj/project.pbxproj index 4b79c55..f42d8c2 100644 --- a/Xcodes.xcodeproj/project.pbxproj +++ b/Xcodes.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 60; objects = { /* Begin PBXBuildFile section */ @@ -60,7 +60,6 @@ CA9FF8E025959BAA00E47BAF /* ConnectionVerifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF8DF25959BAA00E47BAF /* ConnectionVerifier.swift */; }; CA9FF8E625959BB800E47BAF /* AuditTokenHack.m in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF8E525959BB800E47BAF /* AuditTokenHack.m */; }; CA9FF9362595B44700E47BAF /* HelperClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9FF9352595B44700E47BAF /* HelperClient.swift */; }; - CAA1CB2D255A5262003FD669 /* AppleAPI in Frameworks */ = {isa = PBXBuildFile; productRef = CAA1CB2C255A5262003FD669 /* AppleAPI */; }; CAA1CB35255A5AD5003FD669 /* SignInCredentialsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAA1CB34255A5AD5003FD669 /* SignInCredentialsView.swift */; }; CAA1CB45255A5B60003FD669 /* SignIn2FAView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAA1CB44255A5B60003FD669 /* SignIn2FAView.swift */; }; CAA1CB49255A5C97003FD669 /* SignInSMSView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAA1CB48255A5C97003FD669 /* SignInSMSView.swift */; }; @@ -129,6 +128,8 @@ E87DD6EB25D053FA00D86808 /* Progress+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E87DD6EA25D053FA00D86808 /* Progress+.swift */; }; E89342FA25EDCC17007CF557 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E89342F925EDCC17007CF557 /* NotificationManager.swift */; }; E8977EA325C11E1500835F80 /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8977EA225C11E1500835F80 /* PreferencesView.swift */; }; + E89CBD382D5FAB950037ED95 /* XcodesLoginKit in Frameworks */ = {isa = PBXBuildFile; productRef = E89CBD372D5FAB950037ED95 /* XcodesLoginKit */; }; + E89CBD3D2D5FB25A0037ED95 /* AppleAPI in Frameworks */ = {isa = PBXBuildFile; productRef = E89CBD3C2D5FB25A0037ED95 /* AppleAPI */; }; E8B20CBF2A2EDEC20057D816 /* SDKs+Xcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8B20CBE2A2EDEC20057D816 /* SDKs+Xcode.swift */; }; E8C0EB1A291EF43E0081528A /* XcodesKit in Frameworks */ = {isa = PBXBuildFile; productRef = E8C0EB19291EF43E0081528A /* XcodesKit */; }; E8C0EB1C291EF9A10081528A /* AppState+Runtimes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8C0EB1B291EF9A10081528A /* AppState+Runtimes.swift */; }; @@ -141,7 +142,6 @@ E8E98A9625D863D700EC89A0 /* InstallationStepDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */; }; E8F44A1E296B4CD7002D6592 /* Path in Frameworks */ = {isa = PBXBuildFile; productRef = E8F44A1D296B4CD7002D6592 /* Path */; }; E8FA00542B5B109800769CE0 /* com.xcodesorg.xcodesapp.Helper in Copy Helper */ = {isa = PBXBuildFile; fileRef = CA9FF8AE2595967A00E47BAF /* com.xcodesorg.xcodesapp.Helper */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - E8FD5727291EE4AC001E004C /* AsyncNetworkService in Frameworks */ = {isa = PBXBuildFile; productRef = E8FD5726291EE4AC001E004C /* AsyncNetworkService */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -229,7 +229,6 @@ CA452BAF259FD9770072DFA4 /* ProgressIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressIndicator.swift; sourceTree = ""; }; CA452BBF259FDDFE0072DFA4 /* Stub-version.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Stub-version.plist"; sourceTree = ""; }; CA452BEA25A236500072DFA4 /* Stub-0.0.0.Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Stub-0.0.0.Info.plist"; sourceTree = ""; }; - CA538A0C255A4F1A00E64DD7 /* AppleAPI */ = {isa = PBXFileReference; lastKnownFileType = folder; name = AppleAPI; path = Xcodes/AppleAPI; sourceTree = ""; }; CA5D781D257365D6008EDE9D /* PinCodeTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinCodeTextView.swift; sourceTree = ""; }; CA61A6DF259835580008926E /* Xcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xcode.swift; sourceTree = ""; }; CA735108257BF96D00EA9CF8 /* AttributedText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedText.swift; sourceTree = ""; }; @@ -358,15 +357,15 @@ 33027E342CA8C18800CB387C /* LibFido2Swift in Frameworks */, CABFA9E42592F08E00380FEE /* Version in Frameworks */, CABFA9FD2592F13300380FEE /* LegibleError in Frameworks */, + E89CBD3D2D5FB25A0037ED95 /* AppleAPI in Frameworks */, E689540325BE8C64000EBCEA /* DockProgress in Frameworks */, CA9FF86D25951C6E00E47BAF /* XCModel in Frameworks */, CABFA9F82592F0F900380FEE /* KeychainAccess in Frameworks */, E83FDC442CBB649100679C6B /* Sparkle in Frameworks */, E862D43B2CC8B26F00BAA376 /* SRP in Frameworks */, CAA858CD25A3D8BC00ACF8C0 /* ErrorHandling in Frameworks */, + E89CBD382D5FAB950037ED95 /* XcodesLoginKit in Frameworks */, E8C0EB1A291EF43E0081528A /* XcodesKit in Frameworks */, - E8FD5727291EE4AC001E004C /* AsyncNetworkService in Frameworks */, - CAA1CB2D255A5262003FD669 /* AppleAPI in Frameworks */, CABFA9EE2592F0CC00380FEE /* SwiftSoup in Frameworks */, E84E4F572B335094003F3959 /* OrderedCollections in Frameworks */, E8F44A1E296B4CD7002D6592 /* Path in Frameworks */, @@ -585,7 +584,6 @@ CA8FB61C256E115700469DA5 /* .github */, CA9FF9252595A7EB00E47BAF /* Scripts */, CA9FF8242594F10700E47BAF /* AcknowledgementsGenerator */, - CA538A0C255A4F1A00E64DD7 /* AppleAPI */, CAD2E7A02449574E00113D76 /* Xcodes */, CAD2E7B62449575100113D76 /* XcodesTests */, CA9FF8AF2595967A00E47BAF /* com.xcodesorg.xcodesapp.Helper */, @@ -715,7 +713,6 @@ ); name = Xcodes; packageProductDependencies = ( - CAA1CB2C255A5262003FD669 /* AppleAPI */, CABFA9E32592F08E00380FEE /* Version */, CABFA9ED2592F0CC00380FEE /* SwiftSoup */, CABFA9F72592F0F900380FEE /* KeychainAccess */, @@ -723,13 +720,14 @@ CA9FF86C25951C6E00E47BAF /* XCModel */, CAA858CC25A3D8BC00ACF8C0 /* ErrorHandling */, E689540225BE8C64000EBCEA /* DockProgress */, - E8FD5726291EE4AC001E004C /* AsyncNetworkService */, E8C0EB19291EF43E0081528A /* XcodesKit */, E8F44A1D296B4CD7002D6592 /* Path */, E84E4F562B335094003F3959 /* OrderedCollections */, E83FDC432CBB649100679C6B /* Sparkle */, 334A932B2CA885A400A5E079 /* LibFido2Swift */, E862D43A2CC8B26F00BAA376 /* SRP */, + E89CBD372D5FAB950037ED95 /* XcodesLoginKit */, + E89CBD3C2D5FB25A0037ED95 /* AppleAPI */, ); productName = XcodesMac; productReference = CAD2E79E2449574E00113D76 /* Xcodes.app */; @@ -814,11 +812,11 @@ CAA858CB25A3D8BC00ACF8C0 /* XCRemoteSwiftPackageReference "ErrorHandling" */, CAC28186259EE27200B8AB0B /* XCRemoteSwiftPackageReference "CombineExpectations" */, E689540125BE8C64000EBCEA /* XCRemoteSwiftPackageReference "DockProgress" */, - E8FD5725291EE4AC001E004C /* XCRemoteSwiftPackageReference "AsyncHTTPNetworkService" */, E8F44A1C296B4CD7002D6592 /* XCRemoteSwiftPackageReference "Path" */, E84E4F552B335094003F3959 /* XCRemoteSwiftPackageReference "swift-collections" */, E83FDC422CBB649100679C6B /* XCRemoteSwiftPackageReference "Sparkle" */, - 33027E282CA8BB5800CB387C /* XCRemoteSwiftPackageReference "LibFido2Swift" */, + E89CBD362D5FAB950037ED95 /* XCRemoteSwiftPackageReference "XcodesLoginKit" */, + E89CBD3B2D5FB25A0037ED95 /* XCLocalSwiftPackageReference "Xcodes/AppleAPI" */, ); productRefGroup = CAD2E79F2449574E00113D76 /* Products */; projectDirPath = ""; @@ -1487,15 +1485,14 @@ }; /* End XCConfigurationList section */ -/* Begin XCRemoteSwiftPackageReference section */ - 33027E282CA8BB5800CB387C /* XCRemoteSwiftPackageReference "LibFido2Swift" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/kinoroy/LibFido2Swift.git"; - requirement = { - kind = upToNextMinorVersion; - minimumVersion = 0.1.0; - }; +/* Begin XCLocalSwiftPackageReference section */ + E89CBD3B2D5FB25A0037ED95 /* XCLocalSwiftPackageReference "Xcodes/AppleAPI" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Xcodes/AppleAPI; }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCRemoteSwiftPackageReference section */ CA9FF86B25951C6E00E47BAF /* XCRemoteSwiftPackageReference "data" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/xcodereleases/data"; @@ -1576,6 +1573,14 @@ minimumVersion = 1.0.5; }; }; + E89CBD362D5FAB950037ED95 /* XCRemoteSwiftPackageReference "XcodesLoginKit" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/XcodesOrg/XcodesLoginKit"; + requirement = { + branch = main; + kind = branch; + }; + }; E8F44A1C296B4CD7002D6592 /* XCRemoteSwiftPackageReference "Path" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/mxcl/Path.swift"; @@ -1584,14 +1589,6 @@ minimumVersion = 1.0.0; }; }; - E8FD5725291EE4AC001E004C /* XCRemoteSwiftPackageReference "AsyncHTTPNetworkService" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/RobotsAndPencils/AsyncHTTPNetworkService"; - requirement = { - branch = main; - kind = branch; - }; - }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -1604,10 +1601,6 @@ package = CA9FF86B25951C6E00E47BAF /* XCRemoteSwiftPackageReference "data" */; productName = XCModel; }; - CAA1CB2C255A5262003FD669 /* AppleAPI */ = { - isa = XCSwiftPackageProductDependency; - productName = AppleAPI; - }; CAA858CC25A3D8BC00ACF8C0 /* ErrorHandling */ = { isa = XCSwiftPackageProductDependency; package = CAA858CB25A3D8BC00ACF8C0 /* XCRemoteSwiftPackageReference "ErrorHandling" */; @@ -1657,6 +1650,15 @@ isa = XCSwiftPackageProductDependency; productName = SRP; }; + E89CBD372D5FAB950037ED95 /* XcodesLoginKit */ = { + isa = XCSwiftPackageProductDependency; + package = E89CBD362D5FAB950037ED95 /* XCRemoteSwiftPackageReference "XcodesLoginKit" */; + productName = XcodesLoginKit; + }; + E89CBD3C2D5FB25A0037ED95 /* AppleAPI */ = { + isa = XCSwiftPackageProductDependency; + productName = AppleAPI; + }; E8C0EB19291EF43E0081528A /* XcodesKit */ = { isa = XCSwiftPackageProductDependency; productName = XcodesKit; @@ -1666,11 +1668,6 @@ package = E8F44A1C296B4CD7002D6592 /* XCRemoteSwiftPackageReference "Path" */; productName = Path; }; - E8FD5726291EE4AC001E004C /* AsyncNetworkService */ = { - isa = XCSwiftPackageProductDependency; - package = E8FD5725291EE4AC001E004C /* XCRemoteSwiftPackageReference "AsyncHTTPNetworkService" */; - productName = AsyncNetworkService; - }; /* End XCSwiftPackageProductDependency section */ }; rootObject = CAD2E7962449574E00113D76 /* Project object */; diff --git a/Xcodes.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Xcodes.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index f90bb44..37edb65 100644 --- a/Xcodes.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Xcodes.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -3,10 +3,10 @@ "pins": [ { "package": "AsyncNetworkService", - "repositoryURL": "https://github.com/RobotsAndPencils/AsyncHTTPNetworkService", + "repositoryURL": "https://github.com/XcodesOrg/AsyncHTTPNetworkService", "state": { "branch": "main", - "revision": "97770856c4e429f880d4b4dd68cfaf286dc00c30", + "revision": "12e225af8b5dc25afcfabfcf582a165b0581ab19", "version": null } }, @@ -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" } }, { @@ -144,6 +144,15 @@ "revision": "087c91fedc110f9f833b14ef4c32745dabca8913", "version": "1.0.3" } + }, + { + "package": "XcodesLoginKit", + "repositoryURL": "https://github.com/XcodesOrg/XcodesLoginKit", + "state": { + "branch": "main", + "revision": "3a6f2b82c47aea17c510f0326849734a08a74eae", + "version": null + } } ] }, diff --git a/Xcodes/Backend/AppState+Install.swift b/Xcodes/Backend/AppState+Install.swift index 2c9fc84..d428ca8 100644 --- a/Xcodes/Backend/AppState+Install.swift +++ b/Xcodes/Backend/AppState+Install.swift @@ -2,6 +2,7 @@ import Combine import Foundation import Path import AppleAPI +import XcodesLoginKit import Version import LegibleError import os.log diff --git a/Xcodes/Backend/AppState+Runtimes.swift b/Xcodes/Backend/AppState+Runtimes.swift index e2b1149..4867cbf 100644 --- a/Xcodes/Backend/AppState+Runtimes.swift +++ b/Xcodes/Backend/AppState+Runtimes.swift @@ -1,9 +1,10 @@ import Foundation import XcodesKit +import AppleAPI import OSLog import Combine import Path -import AppleAPI +import XcodesLoginKit import Version extension AppState { diff --git a/Xcodes/Backend/AppState+Update.swift b/Xcodes/Backend/AppState+Update.swift index 90f531f..fed3edf 100644 --- a/Xcodes/Backend/AppState+Update.swift +++ b/Xcodes/Backend/AppState+Update.swift @@ -4,7 +4,7 @@ import Path import Version import SwiftSoup import struct XCModel.Xcode -import AppleAPI +import XcodesLoginKit import XcodesKit extension AppState { diff --git a/Xcodes/Backend/AppState.swift b/Xcodes/Backend/AppState.swift index 475602f..180a594 100644 --- a/Xcodes/Backend/AppState.swift +++ b/Xcodes/Backend/AppState.swift @@ -1,5 +1,5 @@ import AppKit -import AppleAPI +import XcodesLoginKit import Combine import Path import LegibleError @@ -9,7 +9,6 @@ import Version import os.log import DockProgress import XcodesKit -import LibFido2Swift enum PreferenceKey: String { case installPath @@ -31,7 +30,7 @@ enum PreferenceKey: String { } class AppState: ObservableObject { - private let client = AppleAPI.Client() + private let client = XcodesLoginKit.Client() internal let runtimeService = RuntimeService() // MARK: - Published Properties @@ -258,50 +257,40 @@ class AppState: ObservableObject { } func signInIfNeeded() -> AnyPublisher { - validateSession() - .catch { (error) -> AnyPublisher in - guard - let username = self.savedUsername, - let password = try? Current.keychain.getString(username) - else { - return Fail(error: error) - .eraseToAnyPublisher() - } - - return self.signIn(username: username, password: password) - .map { _ in Void() } - .eraseToAnyPublisher() - } - .eraseToAnyPublisher() + return validateSession() +// .catch { (error) -> AnyPublisher in +// guard +// let username = self.savedUsername, +// let password = try? Current.keychain.getString(username) +// else { +// return Fail(error: error) +// .eraseToAnyPublisher() +// } +// +// return self.signIn(username: username, password: password) +// //.map { _ in Void() } +// .eraseToAnyPublisher() +// } +// .eraseToAnyPublisher() } func signIn(username: String, password: String) { authError = nil - signIn(username: username, password: password) - .sink( - receiveCompletion: { _ in }, - receiveValue: { _ in } - ) - .store(in: &cancellables) - } - - func signIn(username: String, password: String) -> AnyPublisher { try? Current.keychain.set(password, key: username) Current.defaults.set(username, forKey: "username") - isProcessingAuthRequest = true - return client.srpLogin(accountName: username, password: password) - .receive(on: DispatchQueue.main) - .handleEvents( - receiveOutput: { authenticationState in - self.authenticationState = authenticationState - }, - receiveCompletion: { completion in - self.handleAuthenticationFlowCompletion(completion) - self.isProcessingAuthRequest = false - } - ) - .eraseToAnyPublisher() + + Task { @MainActor in + do { + let authenticationState = try await client.srpLogin(accountName: username, password: password) + handleAuthenticationFlowCompletion(authenticationState) + isProcessingAuthRequest = false + } catch { + Logger.appState.error("Error signing in: \(error, privacy: .public)") + authError = error + self.isProcessingAuthRequest = false + } + } } func handleTwoFactorOption(_ option: TwoFactorOption, authOptions: AuthOptionsResponse, serviceKey: String, sessionID: String, scnt: String) { @@ -314,21 +303,18 @@ class AppState: ObservableObject { func requestSMS(to trustedPhoneNumber: AuthOptionsResponse.TrustedPhoneNumber, authOptions: AuthOptionsResponse, sessionData: AppleSessionData) { isProcessingAuthRequest = true - client.requestSMSSecurityCode(to: trustedPhoneNumber, authOptions: authOptions, sessionData: sessionData) - .receive(on: DispatchQueue.main) - .sink( - receiveCompletion: { completion in - self.handleAuthenticationFlowCompletion(completion) - self.isProcessingAuthRequest = false - }, - receiveValue: { authenticationState in - self.authenticationState = authenticationState - if case let AuthenticationState.waitingForSecondFactor(option, authOptions, sessionData) = authenticationState { - self.handleTwoFactorOption(option, authOptions: authOptions, serviceKey: sessionData.serviceKey, sessionID: sessionData.sessionID, scnt: sessionData.scnt) - } - } - ) - .store(in: &cancellables) + Task { + do { + let authenticationState = try await client.requestSMSSecurityCode(to: trustedPhoneNumber, authOptions: authOptions, sessionData: sessionData) + self.handleAuthenticationFlowCompletion(authenticationState) + self.isProcessingAuthRequest = false + + } catch { + Logger.appState.error("Error requesting SMS: \(error, privacy: .public)") + authError = error + self.isProcessingAuthRequest = false + } + } } func choosePhoneNumberForSMS(authOptions: AuthOptionsResponse, sessionData: AppleSessionData) { @@ -341,101 +327,67 @@ class AppState: ObservableObject { func submitSecurityCode(_ code: SecurityCode, sessionData: AppleSessionData) { isProcessingAuthRequest = true - client.submitSecurityCode(code, sessionData: sessionData) - .receive(on: DispatchQueue.main) - .sink( - receiveCompletion: { completion in - self.handleAuthenticationFlowCompletion(completion) - self.isProcessingAuthRequest = false - }, - receiveValue: { authenticationState in - self.authenticationState = authenticationState - } - ) - .store(in: &cancellables) - } - - var fido2: FIDO2? - - func createAndSubmitSecurityKeyAssertationWithPinCode(_ pinCode: String, sessionData: AppleSessionData, authOptions: AuthOptionsResponse) { - self.presentedSheet = .securityKeyTouchToConfirm - guard let fsaChallenge = authOptions.fsaChallenge else { - // This shouldn't happen - // we shouldn't have called this method without setting the fsaChallenge - // so this is an assertionFailure - assertionFailure() - self.authError = "Something went wrong. Please file a bug report" - return - } - - // The challenge is encoded in Base64URL encoding - let challengeUrl = fsaChallenge.challenge - let challenge = FIDO2.base64urlToBase64(base64url: challengeUrl) - let origin = "https://idmsa.apple.com" - let rpId = "apple.com" - // Allowed creds is sent as a comma separated string - let validCreds = fsaChallenge.allowedCredentials.split(separator: ",").map(String.init) - 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 - self.isProcessingAuthRequest = true - } - - let respData = try JSONEncoder().encode(response) - client.submitChallenge(response: respData, sessionData: AppleSessionData(serviceKey: sessionData.serviceKey, sessionID: sessionData.sessionID, scnt: sessionData.scnt)) - .receive(on: DispatchQueue.main) - .handleEvents( - receiveOutput: { authenticationState in - self.authenticationState = authenticationState - }, - receiveCompletion: { completion in - self.handleAuthenticationFlowCompletion(completion) - self.isProcessingAuthRequest = false - } - ).sink( - receiveCompletion: { _ in }, - receiveValue: { _ in } - ).store(in: &cancellables) - } catch FIDO2Error.canceledByUser { - // User cancelled the auth flow - // we don't have to show an error - // because the sheet will already be dismissed + let authenticationState = try await client.submitSecurityCode(code, sessionData: sessionData) + self.handleAuthenticationFlowCompletion(authenticationState) + self.isProcessingAuthRequest = false } catch { + Logger.appState.error("ERROR SUBMITTING SECURITYCODE: \(error, privacy: .public)") authError = error + self.isProcessingAuthRequest = false + } + } + } + + func createAndSubmitSecurityKeyAssertationWithPinCode(_ pinCode: String, sessionData: AppleSessionData, authOptions: AuthOptionsResponse) { + isProcessingAuthRequest = true + self.presentedSheet = .securityKeyTouchToConfirm + + Task { + do { + let authenticationState = try await client.submitSecurityKeyPinCode(pinCode, sessionData: sessionData, authOptions: authOptions) + self.handleAuthenticationFlowCompletion(authenticationState) + self.isProcessingAuthRequest = false + + } catch { + print("ERROR Requesting SMS: \(error)") + authError = error + self.isProcessingAuthRequest = false } } } func cancelSecurityKeyAssertationRequest() { - self.fido2?.cancel() + self.client.cancelSecurityKeyAssertationRequest() } - - private func handleAuthenticationFlowCompletion(_ completion: Subscribers.Completion) { - switch completion { - case let .failure(error): - // remove saved username and any stored keychain password if authentication fails so it doesn't try again. - clearLoginCredentials() - Logger.appState.error("Authentication error: \(error.legibleDescription)") - self.authError = error - case .finished: - switch self.authenticationState { - case .authenticated, .unauthenticated, .notAppleDeveloper: - self.presentedSheet = nil - case let .waitingForSecondFactor(option, authOptions, sessionData): - self.handleTwoFactorOption(option, authOptions: authOptions, serviceKey: sessionData.serviceKey, sessionID: sessionData.sessionID, scnt: sessionData.scnt) - } + + private func handleAuthenticationFlowCompletion(_ authenticationState: AuthenticationState) { + self.authenticationState = authenticationState + + switch authenticationState { + case .unauthenticated: + authError = AuthenticationError.notAuthorized + case let .waitingForSecondFactor(twoFactorOption, authOptionsResponse, appleSessionData): + self.presentedSheet = .twoFactor(.init( + option: twoFactorOption, + authOptions: authOptionsResponse, + sessionData: AppleSessionData(serviceKey: appleSessionData.serviceKey, sessionID: appleSessionData.sessionID, scnt: appleSessionData.scnt) + )) + case .authenticated(let appleSession): + Logger.appState.info("SUCCESSFULLY LOGGED IN - WELCOME: \(appleSession.user.fullName ?? "")") + self.presentedSheet = nil + break + case .notAppleDeveloper: + authError = AuthenticationError.notDeveloperAppleId } } func signOut() { clearLoginCredentials() - AppleAPI.Current.network.session.configuration.httpCookieStorage?.removeCookies(since: .distantPast) + // TODO FIX ME + //client.signout() authenticationState = .unauthenticated } diff --git a/Xcodes/Backend/Environment.swift b/Xcodes/Backend/Environment.swift index 61574ce..cff8080 100644 --- a/Xcodes/Backend/Environment.swift +++ b/Xcodes/Backend/Environment.swift @@ -2,6 +2,7 @@ import Combine import Foundation import Path import AppleAPI +import XcodesLoginKit import KeychainAccess import XcodesKit /** diff --git a/Xcodes/Frontend/Common/XcodesSheet.swift b/Xcodes/Frontend/Common/XcodesSheet.swift index 2aa8f2c..5d60fc9 100644 --- a/Xcodes/Frontend/Common/XcodesSheet.swift +++ b/Xcodes/Frontend/Common/XcodesSheet.swift @@ -1,5 +1,5 @@ import Foundation -import AppleAPI +import XcodesLoginKit enum XcodesSheet: Identifiable { case signIn diff --git a/Xcodes/Frontend/MainWindow.swift b/Xcodes/Frontend/MainWindow.swift index 698ba96..1caed60 100644 --- a/Xcodes/Frontend/MainWindow.swift +++ b/Xcodes/Frontend/MainWindow.swift @@ -117,7 +117,7 @@ struct MainWindow: View { @ViewBuilder private func signInView() -> some View { - if appState.authenticationState == .authenticated { + if case .authenticated(_) = appState.authenticationState { VStack { SignedInView() .padding(32) diff --git a/Xcodes/Frontend/Preferences/AdvancedPreferencePane.swift b/Xcodes/Frontend/Preferences/AdvancedPreferencePane.swift index 13ab718..ecf5bf8 100644 --- a/Xcodes/Frontend/Preferences/AdvancedPreferencePane.swift +++ b/Xcodes/Frontend/Preferences/AdvancedPreferencePane.swift @@ -1,4 +1,4 @@ -import AppleAPI +import XcodesLoginKit import SwiftUI import Path diff --git a/Xcodes/Frontend/Preferences/DownloadPreferencePane.swift b/Xcodes/Frontend/Preferences/DownloadPreferencePane.swift index 854c615..995aee9 100644 --- a/Xcodes/Frontend/Preferences/DownloadPreferencePane.swift +++ b/Xcodes/Frontend/Preferences/DownloadPreferencePane.swift @@ -1,4 +1,4 @@ -import AppleAPI +import XcodesLoginKit import SwiftUI struct DownloadPreferencePane: View { diff --git a/Xcodes/Frontend/Preferences/ExperiementsPreferencePane.swift b/Xcodes/Frontend/Preferences/ExperiementsPreferencePane.swift index 68dad51..69b595e 100644 --- a/Xcodes/Frontend/Preferences/ExperiementsPreferencePane.swift +++ b/Xcodes/Frontend/Preferences/ExperiementsPreferencePane.swift @@ -1,4 +1,4 @@ -import AppleAPI +import XcodesLoginKit import Path import SwiftUI diff --git a/Xcodes/Frontend/Preferences/GeneralPreferencePane.swift b/Xcodes/Frontend/Preferences/GeneralPreferencePane.swift index b15f5c6..98ac0e1 100644 --- a/Xcodes/Frontend/Preferences/GeneralPreferencePane.swift +++ b/Xcodes/Frontend/Preferences/GeneralPreferencePane.swift @@ -1,4 +1,4 @@ -import AppleAPI +import XcodesLoginKit import SwiftUI struct GeneralPreferencePane: View { @@ -7,7 +7,7 @@ struct GeneralPreferencePane: View { var body: some View { VStack(alignment: .leading) { GroupBox(label: Text("AppleID")) { - if appState.authenticationState == .authenticated { + if case .authenticated(_) = appState.authenticationState { SignedInView() } else { Button("SignIn", action: { self.appState.presentedSheet = .signIn }) diff --git a/Xcodes/Frontend/Preferences/UpdatesPreferencePane.swift b/Xcodes/Frontend/Preferences/UpdatesPreferencePane.swift index edad69b..14d3821 100644 --- a/Xcodes/Frontend/Preferences/UpdatesPreferencePane.swift +++ b/Xcodes/Frontend/Preferences/UpdatesPreferencePane.swift @@ -1,4 +1,4 @@ -import AppleAPI +import XcodesLoginKit import Sparkle import SwiftUI diff --git a/Xcodes/Frontend/SignIn/SignIn2FAView.swift b/Xcodes/Frontend/SignIn/SignIn2FAView.swift index a8dfa98..46952a3 100644 --- a/Xcodes/Frontend/SignIn/SignIn2FAView.swift +++ b/Xcodes/Frontend/SignIn/SignIn2FAView.swift @@ -1,5 +1,5 @@ import SwiftUI -import AppleAPI +import XcodesLoginKit struct SignIn2FAView: View { @EnvironmentObject var appState: AppState diff --git a/Xcodes/Frontend/SignIn/SignInPhoneListView.swift b/Xcodes/Frontend/SignIn/SignInPhoneListView.swift index fc31d97..35f88da 100644 --- a/Xcodes/Frontend/SignIn/SignInPhoneListView.swift +++ b/Xcodes/Frontend/SignIn/SignInPhoneListView.swift @@ -1,4 +1,4 @@ -import AppleAPI +import XcodesLoginKit import SwiftUI struct SignInPhoneListView: View { diff --git a/Xcodes/Frontend/SignIn/SignInSMSView.swift b/Xcodes/Frontend/SignIn/SignInSMSView.swift index 6d42f69..3c428c4 100644 --- a/Xcodes/Frontend/SignIn/SignInSMSView.swift +++ b/Xcodes/Frontend/SignIn/SignInSMSView.swift @@ -1,5 +1,5 @@ import SwiftUI -import AppleAPI +import XcodesLoginKit struct SignInSMSView: View { @EnvironmentObject var appState: AppState diff --git a/Xcodes/Frontend/SignIn/SignInSecurityKeyPinView.swift b/Xcodes/Frontend/SignIn/SignInSecurityKeyPinView.swift index 32b6028..355dc1c 100644 --- a/Xcodes/Frontend/SignIn/SignInSecurityKeyPinView.swift +++ b/Xcodes/Frontend/SignIn/SignInSecurityKeyPinView.swift @@ -7,7 +7,7 @@ // import SwiftUI -import AppleAPI +import XcodesLoginKit struct SignInSecurityKeyPinView: View { @EnvironmentObject var appState: AppState diff --git a/Xcodes/Frontend/SignIn/SignInSecurityKeyTouchView.swift b/Xcodes/Frontend/SignIn/SignInSecurityKeyTouchView.swift index 362a54f..c3643db 100644 --- a/Xcodes/Frontend/SignIn/SignInSecurityKeyTouchView.swift +++ b/Xcodes/Frontend/SignIn/SignInSecurityKeyTouchView.swift @@ -7,7 +7,7 @@ // import SwiftUI -import AppleAPI +import XcodesLoginKit struct SignInSecurityKeyTouchView: View { @EnvironmentObject var appState: AppState diff --git a/Xcodes/Resources/Licenses.rtf b/Xcodes/Resources/Licenses.rtf index a10eda3..25ff4d4 100644 --- a/Xcodes/Resources/Licenses.rtf +++ b/Xcodes/Resources/Licenses.rtf @@ -1,4 +1,4 @@ -{\rtf1\ansi\ansicpg1252\cocoartf2818 +{\rtf1\ansi\ansicpg1252\cocoartf2821 \cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 .SFNS-Regular;} {\colortbl;\red255\green255\blue255;} {\*\expandedcolortbl;;} diff --git a/Xcodes/Resources/Localizable.xcstrings b/Xcodes/Resources/Localizable.xcstrings index 24fc217..2265e02 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" : { diff --git a/Xcodes/XcodesKit/Package.swift b/Xcodes/XcodesKit/Package.swift index 81447ab..a3f6896 100644 --- a/Xcodes/XcodesKit/Package.swift +++ b/Xcodes/XcodesKit/Package.swift @@ -14,7 +14,7 @@ let package = Package( ], dependencies: [ // Dependencies declare other packages that this package depends on. - .package(url: "https://github.com/RobotsAndPencils/AsyncHTTPNetworkService", branch: "main"), + .package(url: "https://github.com/XcodesOrg/AsyncHTTPNetworkService", branch: "main"), .package(url: "https://github.com/mxcl/Path.swift", from: "1.0.0"), ], targets: [ diff --git a/XcodesTests/AppStateTests.swift b/XcodesTests/AppStateTests.swift index 4be9ca3..ead4935 100644 --- a/XcodesTests/AppStateTests.swift +++ b/XcodesTests/AppStateTests.swift @@ -1,4 +1,4 @@ -import AppleAPI +import XcodesLoginKit import Combine import CombineExpectations import Path