Skip to content

Commit 34ef34d

Browse files
committed
Add new error type for failure to handle auth challenge
1 parent ac8c5c6 commit 34ef34d

File tree

3 files changed

+53
-6
lines changed

3 files changed

+53
-6
lines changed

WooCommerce/Classes/Authentication/Application Password/ApplicationPasswordTutorialViewModel.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ struct ApplicationPasswordTutorialViewModel {
2323
comment: "Reason for why the user could not login tin the application password tutorial screen")
2424
case .invalidCredentials:
2525
return error.localizedDescription
26+
case .failedAuthenticationChallenge:
27+
fatalError("Failure to handle authentication challenge is not eligible for application password flow.")
2628
}
2729
}
2830
}

WooCommerce/Classes/Authentication/AuthenticationManager.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,9 @@ extension AuthenticationManager: WordPressAuthenticatorDelegate {
363363

364364
let isAppPasswordAuthError = {
365365
switch error {
366-
case SiteCredentialLoginError.genericFailure, SiteCredentialLoginError.invalidCredentials:
366+
case SiteCredentialLoginError.genericFailure,
367+
SiteCredentialLoginError.invalidCredentials,
368+
SiteCredentialLoginError.failedAuthenticationChallenge:
367369
return false
368370
case is SiteCredentialLoginError:
369371
return true

WooCommerce/Classes/Authentication/SiteCredentialLoginUseCase.swift

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)