From 307733a9bf7f37b6fc9ec361d2fe4c7c028408c3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 May 2026 04:09:49 +0000 Subject: [PATCH] Fix QR Login: handle authenticate success(false) as authentication rejection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the server responds to the authenticate request but sets authenticated=false, the success callback fires with `false`. Previously this was ignored (using `_`) and the UI incorrectly moved to the done state with a qrLoginAuthenticated track. Now we check the Bool: false → show authentication failed error + track failure; true → proceed to done state as before. Also adds a test case for the server-rejected scenario. Agent-Logs-Url: https://github.com/wordpress-mobile/WordPress-iOS/sessions/378e64a4-563e-46cb-ad8b-017d726bc2ba Co-authored-by: mokagio <1218433+mokagio@users.noreply.github.com> --- .../Login/QRLoginVerifyCoordinatorTests.swift | 38 ++++++++++++++++++- .../QRLoginVerifyCoordinator.swift | 8 +++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/Tests/KeystoneTests/Tests/Login/QRLoginVerifyCoordinatorTests.swift b/Tests/KeystoneTests/Tests/Login/QRLoginVerifyCoordinatorTests.swift index da0ef9fb3ef3..b95e919a8fa3 100644 --- a/Tests/KeystoneTests/Tests/Login/QRLoginVerifyCoordinatorTests.swift +++ b/Tests/KeystoneTests/Tests/Login/QRLoginVerifyCoordinatorTests.swift @@ -143,6 +143,38 @@ class QRLoginVerifyCoordinatorTests: CoreDataTestCase { XCTAssertEqual(parentCoordinator.trackStack, expectedTrackStack) } + func testConfirmFromWaitingForUserVerificationAndAuthenticationRejected() { + let view = QRLoginVerifyViewMock() + let parentCoordinator = ParentCoorinatorMock() + let service = QRLoginServiceMock(coreDataStack: contextManager) + let connectionChecker = QRConnectionCheckerMock(mockConnectionAvailable: true) + + let coordinator = QRLoginVerifyCoordinator(token: testToken, + view: view, + parentCoordinator: parentCoordinator, + connectionChecker: connectionChecker, + service: service, + coreDataStack: contextManager) + + // Configure the mocks — server responds but rejects authentication + coordinator.state = .waitingForUserVerification + service.responseExpectation = .authenticationRejected + + // Trigger the action + coordinator.confirm() + + // Verify the coordinator is in the error state, not done + XCTAssertEqual(coordinator.state, .error) + + // Check the view stack + let expectedStack: [QRLoginVerifyViewMock.LoginState] = [.showAuthenticating, .showAuthenticationFailedError] + XCTAssertEqual(view.stateStack, expectedStack) + + // Verify tracks are being recorded correctly + let expectedTrackStack: [WPAnalyticsEvent] = [.qrLoginVerifyCodeApproved, .qrLoginVerifyCodeFailed] + XCTAssertEqual(parentCoordinator.trackStack, expectedTrackStack) + } + func testConfirmFromWaitingForUserVerificationAndFails() { let view = QRLoginVerifyViewMock() let parentCoordinator = ParentCoorinatorMock() @@ -375,6 +407,7 @@ private class QRLoginVerifyViewMock: QRLoginVerifyView { private class QRLoginServiceMock: QRLoginService { enum ResponseExpectation { case success + case authenticationRejected case failure } @@ -382,7 +415,7 @@ private class QRLoginServiceMock: QRLoginService { override func validate(token: QRLoginToken, success: @escaping(QRLoginValidationResponse) -> Void, failure: @escaping(Error?, QRLoginError?) -> Void) { switch responseExpectation { - case .success: + case .success, .authenticationRejected: let json = "{\"browser\": \"browser\",\"location\": \"location\"}" let data = json.data(using: .utf8) ?? Data() let jsonDecoder = JSONDecoder() @@ -403,6 +436,9 @@ private class QRLoginServiceMock: QRLoginService { case .success: success(true) + case .authenticationRejected: + success(false) + case .failure: failure(NSError.testInstance()) } diff --git a/WordPress/Classes/ViewRelated/QR Login/Coordinators/QRLoginVerifyCoordinator.swift b/WordPress/Classes/ViewRelated/QR Login/Coordinators/QRLoginVerifyCoordinator.swift index 69e1650c197b..7fe8ad447fad 100644 --- a/WordPress/Classes/ViewRelated/QR Login/Coordinators/QRLoginVerifyCoordinator.swift +++ b/WordPress/Classes/ViewRelated/QR Login/Coordinators/QRLoginVerifyCoordinator.swift @@ -90,7 +90,13 @@ extension QRLoginVerifyCoordinator { view.showAuthenticating() state = .authenticating - service.authenticate(token: token) { _ in + service.authenticate(token: token) { authenticated in + guard authenticated else { + self.state = .error + self.view.showAuthenticationFailedError() + self.parentCoordinator.track(.qrLoginVerifyCodeFailed, properties: ["error": "authentication_failed"]) + return + } self.parentCoordinator.track(.qrLoginAuthenticated) self.state = .done self.view.renderCompletion()