feat: switch login to use xcodesLoginKit

This commit is contained in:
Matt Kiazyk 2025-02-14 11:27:09 -06:00
parent 17f3d365b8
commit 84f1838e60
23 changed files with 163 additions and 192 deletions

View file

@ -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 = "<group>"; };
CA452BBF259FDDFE0072DFA4 /* Stub-version.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Stub-version.plist"; sourceTree = "<group>"; };
CA452BEA25A236500072DFA4 /* Stub-0.0.0.Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Stub-0.0.0.Info.plist"; sourceTree = "<group>"; };
CA538A0C255A4F1A00E64DD7 /* AppleAPI */ = {isa = PBXFileReference; lastKnownFileType = folder; name = AppleAPI; path = Xcodes/AppleAPI; sourceTree = "<group>"; };
CA5D781D257365D6008EDE9D /* PinCodeTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinCodeTextView.swift; sourceTree = "<group>"; };
CA61A6DF259835580008926E /* Xcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xcode.swift; sourceTree = "<group>"; };
CA735108257BF96D00EA9CF8 /* AttributedText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedText.swift; sourceTree = "<group>"; };
@ -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 */;

View file

@ -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
}
}
]
},

View file

@ -2,6 +2,7 @@ import Combine
import Foundation
import Path
import AppleAPI
import XcodesLoginKit
import Version
import LegibleError
import os.log

View file

@ -1,9 +1,10 @@
import Foundation
import XcodesKit
import AppleAPI
import OSLog
import Combine
import Path
import AppleAPI
import XcodesLoginKit
import Version
extension AppState {

View file

@ -4,7 +4,7 @@ import Path
import Version
import SwiftSoup
import struct XCModel.Xcode
import AppleAPI
import XcodesLoginKit
import XcodesKit
extension AppState {

View file

@ -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<Void, Error> {
validateSession()
.catch { (error) -> AnyPublisher<Void, Error> 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<Void, Error> 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<AuthenticationState, Error> {
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<Error>) {
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
}

View file

@ -2,6 +2,7 @@ import Combine
import Foundation
import Path
import AppleAPI
import XcodesLoginKit
import KeychainAccess
import XcodesKit
/**

View file

@ -1,5 +1,5 @@
import Foundation
import AppleAPI
import XcodesLoginKit
enum XcodesSheet: Identifiable {
case signIn

View file

@ -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)

View file

@ -1,4 +1,4 @@
import AppleAPI
import XcodesLoginKit
import SwiftUI
import Path

View file

@ -1,4 +1,4 @@
import AppleAPI
import XcodesLoginKit
import SwiftUI
struct DownloadPreferencePane: View {

View file

@ -1,4 +1,4 @@
import AppleAPI
import XcodesLoginKit
import Path
import SwiftUI

View file

@ -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 })

View file

@ -1,4 +1,4 @@
import AppleAPI
import XcodesLoginKit
import Sparkle
import SwiftUI

View file

@ -1,5 +1,5 @@
import SwiftUI
import AppleAPI
import XcodesLoginKit
struct SignIn2FAView: View {
@EnvironmentObject var appState: AppState

View file

@ -1,4 +1,4 @@
import AppleAPI
import XcodesLoginKit
import SwiftUI
struct SignInPhoneListView: View {

View file

@ -1,5 +1,5 @@
import SwiftUI
import AppleAPI
import XcodesLoginKit
struct SignInSMSView: View {
@EnvironmentObject var appState: AppState

View file

@ -7,7 +7,7 @@
//
import SwiftUI
import AppleAPI
import XcodesLoginKit
struct SignInSecurityKeyPinView: View {
@EnvironmentObject var appState: AppState

View file

@ -7,7 +7,7 @@
//
import SwiftUI
import AppleAPI
import XcodesLoginKit
struct SignInSecurityKeyTouchView: View {
@EnvironmentObject var appState: AppState

View file

@ -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;;}

View file

@ -237,6 +237,16 @@
}
}
},
"%@ (%@)" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "%1$@ (%2$@)"
}
}
}
},
"%@ %@ %@" : {
"localizations" : {
"ar" : {

View file

@ -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: [

View file

@ -1,4 +1,4 @@
import AppleAPI
import XcodesLoginKit
import Combine
import CombineExpectations
import Path