@@ -18,6 +18,8 @@ public class Client {
1818 case noTrustedPhoneNumbers
1919 case notAuthenticated
2020 case invalidHashcash
21+ case missingSecurityCodeInfo
22+ case accountUsesHardwareKey
2123
2224 public var errorDescription : String ? {
2325 switch self {
@@ -33,6 +35,10 @@ public class Client {
3335 return " You are already signed out "
3436 case . invalidHashcash:
3537 return " Could not create a hashcash for the session. "
38+ case . missingSecurityCodeInfo:
39+ return " Expected security code info but didn't receive any. "
40+ case . accountUsesHardwareKey:
41+ return " Account uses a hardware key for authentication but this is not supported yet. "
3642 default :
3743 return String ( describing: self )
3844 }
@@ -58,7 +64,7 @@ public class Client {
5864 }
5965 . then { ( data, _) -> Promise < ( serviceKey: String , hashcash: String ) > in
6066 struct ServiceKeyResponse : Decodable {
61- let authServiceKey : String
67+ let authServiceKey : String ?
6268 }
6369
6470 let response = try JSONDecoder ( ) . decode ( ServiceKeyResponse . self, from: data)
@@ -120,6 +126,8 @@ public class Client {
120126 return Promise . value ( ( ) )
121127 case . twoFactor:
122128 return self . handleTwoFactor ( serviceKey: serviceKey, sessionID: sessionID, scnt: scnt, authOptions: authOptions)
129+ case . hardwareKey:
130+ throw Error . accountUsesHardwareKey
123131 case . unknown:
124132 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)
125133 String ( data: data, encoding: . utf8) . map { Current . logging. log ( $0) }
@@ -134,7 +142,8 @@ public class Client {
134142 // SMS was sent automatically
135143 if authOptions. smsAutomaticallySent {
136144 return firstly { ( ) throws -> Promise < ( data: Data , response: URLResponse ) > in
137- let code = self . promptForSMSSecurityCode ( length: authOptions. securityCode. length, for: authOptions. trustedPhoneNumbers!. first!)
145+ guard let securityCode = authOptions. securityCode else { throw Error . missingSecurityCodeInfo }
146+ let code = self . promptForSMSSecurityCode ( length: securityCode. length, for: authOptions. trustedPhoneNumbers!. first!)
138147 return Current . network. dataTask ( with: try URLRequest . submitSecurityCode ( serviceKey: serviceKey, sessionID: sessionID, scnt: scnt, code: code) )
139148 . validateSecurityCodeResponse ( )
140149 }
@@ -146,9 +155,10 @@ public class Client {
146155 return handleWithPhoneNumberSelection ( authOptions: authOptions, serviceKey: serviceKey, sessionID: sessionID, scnt: scnt)
147156 // Code is shown on trusted devices
148157 } else {
158+ let securityCodeLength : Int = authOptions. securityCode? . length ?? 0
149159 let code = Current . shell. readLine ( """
150160 Enter " sms " without quotes to exit this prompt and choose a phone number to send an SMS security code to.
151- Enter the \( authOptions . securityCode . length ) digit code from one of your trusted devices:
161+ Enter the \( securityCodeLength ) digit code from one of your trusted devices:
152162 """ ) ?? " "
153163
154164 if code == " sms " {
@@ -216,7 +226,8 @@ public class Client {
216226 . then { trustedPhoneNumber in
217227 Current . network. dataTask ( with: try URLRequest . requestSecurityCode ( serviceKey: serviceKey, sessionID: sessionID, scnt: scnt, trustedPhoneID: trustedPhoneNumber. id) )
218228 . map { _ in
219- self . promptForSMSSecurityCode ( length: authOptions. securityCode. length, for: trustedPhoneNumber)
229+ guard let securityCodeLength = authOptions. securityCode? . length else { throw Error . missingSecurityCodeInfo }
230+ return self . promptForSMSSecurityCode ( length: securityCodeLength, for: trustedPhoneNumber)
220231 }
221232 }
222233 . then { code in
@@ -276,15 +287,18 @@ public extension Promise where T == (data: Data, response: URLResponse) {
276287struct AuthOptionsResponse : Decodable {
277288 let trustedPhoneNumbers : [ TrustedPhoneNumber ] ?
278289 let trustedDevices : [ TrustedDevice ] ?
279- let securityCode : SecurityCodeInfo
290+ let securityCode : SecurityCodeInfo ?
280291 let noTrustedDevices : Bool ?
281292 let serviceErrors : [ ServiceError ] ?
293+ let fsaChallenge : FSAChallenge ?
282294
283295 var kind : Kind {
284296 if trustedDevices != nil {
285297 return . twoStep
286298 } else if trustedPhoneNumbers != nil {
287299 return . twoFactor
300+ } else if fsaChallenge != nil {
301+ return . hardwareKey
288302 } else {
289303 return . unknown
290304 }
@@ -321,8 +335,15 @@ struct AuthOptionsResponse: Decodable {
321335 let securityCodeCooldown : Bool
322336 }
323337
338+ struct FSAChallenge : Decodable {
339+ let challenge : String
340+ let keyHandles : [ String ]
341+ let rpId : String
342+ let allowedCredentials : String
343+ }
344+
324345 enum Kind {
325- case twoStep, twoFactor, unknown
346+ case twoStep, twoFactor, hardwareKey , unknown
326347 }
327348}
328349
0 commit comments