Skip to content

Commit

Permalink
Merge pull request #294 from garrettmoon/addErrorForHardwareKeys
Browse files Browse the repository at this point in the history
Fix error for accounts using hardware keys.
  • Loading branch information
MattKiazyk authored Feb 16, 2024
2 parents 7340c9b + 6a4ab11 commit bbe71da
Showing 1 changed file with 27 additions and 6 deletions.
33 changes: 27 additions & 6 deletions Sources/AppleAPI/Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public class Client {
case noTrustedPhoneNumbers
case notAuthenticated
case invalidHashcash
case missingSecurityCodeInfo
case accountUsesHardwareKey

public var errorDescription: String? {
switch self {
Expand All @@ -33,6 +35,10 @@ public class Client {
return "You are already signed out"
case .invalidHashcash:
return "Could not create a hashcash for the session."
case .missingSecurityCodeInfo:
return "Expected security code info but didn't receive any."
case .accountUsesHardwareKey:
return "Account uses a hardware key for authentication but this is not supported yet."
default:
return String(describing: self)
}
Expand All @@ -58,7 +64,7 @@ public class Client {
}
.then { (data, _) -> Promise<(serviceKey: String, hashcash: String)> in
struct ServiceKeyResponse: Decodable {
let authServiceKey: String
let authServiceKey: String?
}

let response = try JSONDecoder().decode(ServiceKeyResponse.self, from: data)
Expand Down Expand Up @@ -120,6 +126,8 @@ public class Client {
return Promise.value(())
case .twoFactor:
return self.handleTwoFactor(serviceKey: serviceKey, sessionID: sessionID, scnt: scnt, authOptions: authOptions)
case .hardwareKey:
throw Error.accountUsesHardwareKey
case .unknown:
Current.logging.log("Received a response from Apple that indicates this account has two-step or two-factor authentication enabled, but xcodes is unsure how to handle this response:".red)
String(data: data, encoding: .utf8).map { Current.logging.log($0) }
Expand All @@ -134,7 +142,8 @@ public class Client {
// SMS was sent automatically
if authOptions.smsAutomaticallySent {
return firstly { () throws -> Promise<(data: Data, response: URLResponse)> in
let code = self.promptForSMSSecurityCode(length: authOptions.securityCode.length, for: authOptions.trustedPhoneNumbers!.first!)
guard let securityCode = authOptions.securityCode else { throw Error.missingSecurityCodeInfo }
let code = self.promptForSMSSecurityCode(length: securityCode.length, for: authOptions.trustedPhoneNumbers!.first!)
return Current.network.dataTask(with: try URLRequest.submitSecurityCode(serviceKey: serviceKey, sessionID: sessionID, scnt: scnt, code: code))
.validateSecurityCodeResponse()
}
Expand All @@ -146,9 +155,10 @@ public class Client {
return handleWithPhoneNumberSelection(authOptions: authOptions, serviceKey: serviceKey, sessionID: sessionID, scnt: scnt)
// Code is shown on trusted devices
} else {
let securityCodeLength: Int = authOptions.securityCode?.length ?? 0
let code = Current.shell.readLine("""
Enter "sms" without quotes to exit this prompt and choose a phone number to send an SMS security code to.
Enter the \(authOptions.securityCode.length) digit code from one of your trusted devices:
Enter the \(securityCodeLength) digit code from one of your trusted devices:
""") ?? ""

if code == "sms" {
Expand Down Expand Up @@ -216,7 +226,8 @@ public class Client {
.then { trustedPhoneNumber in
Current.network.dataTask(with: try URLRequest.requestSecurityCode(serviceKey: serviceKey, sessionID: sessionID, scnt: scnt, trustedPhoneID: trustedPhoneNumber.id))
.map { _ in
self.promptForSMSSecurityCode(length: authOptions.securityCode.length, for: trustedPhoneNumber)
guard let securityCodeLength = authOptions.securityCode?.length else { throw Error.missingSecurityCodeInfo }
return self.promptForSMSSecurityCode(length: securityCodeLength, for: trustedPhoneNumber)
}
}
.then { code in
Expand Down Expand Up @@ -276,15 +287,18 @@ public extension Promise where T == (data: Data, response: URLResponse) {
struct AuthOptionsResponse: Decodable {
let trustedPhoneNumbers: [TrustedPhoneNumber]?
let trustedDevices: [TrustedDevice]?
let securityCode: SecurityCodeInfo
let securityCode: SecurityCodeInfo?
let noTrustedDevices: Bool?
let serviceErrors: [ServiceError]?
let fsaChallenge: FSAChallenge?

var kind: Kind {
if trustedDevices != nil {
return .twoStep
} else if trustedPhoneNumbers != nil {
return .twoFactor
} else if fsaChallenge != nil {
return .hardwareKey
} else {
return .unknown
}
Expand Down Expand Up @@ -321,8 +335,15 @@ struct AuthOptionsResponse: Decodable {
let securityCodeCooldown: Bool
}

struct FSAChallenge: Decodable {
let challenge: String
let keyHandles: [String]
let rpId: String
let allowedCredentials: String
}

enum Kind {
case twoStep, twoFactor, unknown
case twoStep, twoFactor, hardwareKey, unknown
}
}

Expand Down

0 comments on commit bbe71da

Please sign in to comment.