@@ -15,6 +15,7 @@ enum SiteCredentialLoginError: LocalizedError {
1515 case inaccessibleLoginPage
1616 case inaccessibleAdminPage
1717 case unacceptableStatusCode( code: Int )
18+ case failedAuthenticationChallenge
1819 case genericFailure( underlyingError: Error )
1920
2021 /// Used for tracking error code
@@ -26,7 +27,8 @@ enum SiteCredentialLoginError: LocalizedError {
2627 . invalidLoginResponse,
2728 . invalidCredentials,
2829 . loginFailed,
29- . unacceptableStatusCode:
30+ . unacceptableStatusCode,
31+ . failedAuthenticationChallenge:
3032 return NSError ( domain: Self . errorDomain, code: errorCode, userInfo: [ NSLocalizedDescriptionKey: errorMessage] )
3133 case . genericFailure( let underlyingError) :
3234 return underlyingError as NSError
@@ -45,6 +47,8 @@ enum SiteCredentialLoginError: LocalizedError {
4547 return 401
4648 case . unacceptableStatusCode( let code) :
4749 return code
50+ case . failedAuthenticationChallenge:
51+ return - 2
4852 case . genericFailure( let underlyingError) :
4953 return ( underlyingError as NSError ) . code
5054 }
@@ -64,6 +68,8 @@ enum SiteCredentialLoginError: LocalizedError {
6468 return message
6569 case . unacceptableStatusCode( let code) :
6670 return String ( format: Localization . unacceptableStatusCode, code)
71+ case . failedAuthenticationChallenge:
72+ return Localization . failedAuthenticationChallenge
6773 case . genericFailure:
6874 return " "
6975 }
@@ -83,7 +89,8 @@ enum SiteCredentialLoginError: LocalizedError {
8389 comment: " Error message explaining login failure due to blocked WP Admin page "
8490 )
8591 static let invalidLoginResponse = NSLocalizedString (
86- " Unable to login due to an unexpected response from your site. We are working on fixing this issue. " ,
92+ " siteCredentialLoginError.invalidLoginResponse.message " ,
93+ value: " Unable to login due to an unexpected response from your site. " ,
8794 comment: " Error message explaining login failure due to unexpected response. "
8895 )
8996 static let unacceptableStatusCode = NSLocalizedString (
@@ -94,6 +101,11 @@ enum SiteCredentialLoginError: LocalizedError {
94101 " It seems the username or password you entered doesn't quite match. Double-check your credentials and try again. " ,
95102 comment: " Error message explaining login failure due to invalid credentials. "
96103 )
104+ static let failedAuthenticationChallenge = NSLocalizedString (
105+ " siteCredentialLoginError.failedAuthenticationChallenge.message " ,
106+ value: " Unable to log in due to an unexpected security measure on your store. Please contact support for troubleshooting. " ,
107+ comment: " Error message explaining login failure due to an unexpected authentication challenge. "
108+ )
97109 }
98110}
99111
@@ -104,12 +116,24 @@ enum SiteCredentialLoginError: LocalizedError {
104116/// - If the request does not redirect or the redirect fails, login fails.
105117/// Ref: pe5sF9-1iQ-p2
106118///
107- final class SiteCredentialLoginUseCase : NSObject , SiteCredentialLoginProtocol {
119+ final class SiteCredentialLoginUseCase : NSObject , SiteCredentialLoginProtocol , URLSessionTaskDelegate {
108120 private let siteURL : String
109121 private let cookieJar : HTTPCookieStorage
110122 private var successHandler : ( ( ) -> Void ) ?
111123 private var errorHandler : ( ( SiteCredentialLoginError ) -> Void ) ?
112- private lazy var session = URLSession ( configuration: . default)
124+
125+ private var receivedAuthChallengeMethod : String ?
126+
127+ private lazy var session = {
128+ var configuration = URLSessionConfiguration . default
129+ configuration. timeoutIntervalForResource = 60
130+ return URLSession ( configuration: configuration, delegate: self , delegateQueue: nil )
131+ } ( )
132+
133+ static let supportedAuthChallengeMethods = [
134+ NSURLAuthenticationMethodServerTrust,
135+ NSURLAuthenticationMethodHTTPBasic
136+ ]
113137
114138 init ( siteURL: String ,
115139 cookieJar: HTTPCookieStorage = HTTPCookieStorage . shared) {
@@ -128,18 +152,26 @@ final class SiteCredentialLoginUseCase: NSObject, SiteCredentialLoginProtocol {
128152 // Old cookies can make the login succeeds even with incorrect credentials
129153 // So we need to clear all cookies before login.
130154 clearAllCookies ( )
155+
156+ receivedAuthChallengeMethod = nil
157+
131158 guard let loginRequest = buildLoginRequest ( username: username, password: password) else {
132159 DDLogError ( " ⛔️ Error constructing login requests " )
133160 return
134161 }
162+
135163 Task { @MainActor in
136164 do {
137165 try await startLogin ( with: loginRequest)
138166 successHandler ? ( )
139167 } catch let error as SiteCredentialLoginError {
140168 errorHandler ? ( error)
141169 } catch {
142- errorHandler ? ( . genericFailure( underlyingError: error as NSError ) )
170+ if receivedAuthChallengeMethod != nil {
171+ errorHandler ? ( . failedAuthenticationChallenge)
172+ } else {
173+ errorHandler ? ( . genericFailure( underlyingError: error as NSError ) )
174+ }
143175 }
144176 }
145177 }
@@ -293,3 +325,14 @@ private extension String {
293325 return wholeMatch ( of: regex) != nil
294326 }
295327}
328+
329+ // MARK: - URLSessionTaskDelegate
330+ extension SiteCredentialLoginUseCase {
331+ func urlSession( _ session: URLSession ,
332+ didReceive challenge: URLAuthenticationChallenge ,
333+ completionHandler: @escaping ( URLSession . AuthChallengeDisposition , URLCredential ? ) -> Void ) {
334+ let authMethod = challenge. protectionSpace. authenticationMethod
335+ receivedAuthChallengeMethod = authMethod
336+ completionHandler ( . performDefaultHandling, nil )
337+ }
338+ }
0 commit comments