Skip to content

Commit

Permalink
Configure the AuthenticationService later now that we have 2 flows on…
Browse files Browse the repository at this point in the history
… the start screen. (#3316)

* Don't query the homeserver until confirming it (or selecting a different one).

* Setup the infrastructure to test AuthenticationService.

Implement basic tests for configuration & password login.

* Use the real AuthenticationService with a mock Client in all of the tests.

* Add tests for the ServerConfirmationScreenViewModel.

* Remove redundant view state and test for it.
  • Loading branch information
pixlwave authored Sep 25, 2024
1 parent af8c161 commit a8dbda9
Show file tree
Hide file tree
Showing 44 changed files with 1,209 additions and 298 deletions.
48 changes: 36 additions & 12 deletions ElementX.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion ElementX/Sources/Application/AppCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
encryptionKeyProvider: EncryptionKeyProvider(),
appSettings: appSettings,
appHooks: appHooks)
_ = await authenticationService.configure(for: userSession.clientProxy.homeserver)
_ = await authenticationService.configure(for: userSession.clientProxy.homeserver, flow: .login)

let parameters = SoftLogoutScreenCoordinatorParameters(authenticationService: authenticationService,
credentials: credentials,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,11 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {

switch action {
case .loginManually:
Task { await self.startAuthentication(flow: .login) }
showServerConfirmationScreen(authenticationFlow: .login)
case .loginWithQR:
startQRCodeLogin()
case .register:
Task { await self.startAuthentication(flow: .register) }
showServerConfirmationScreen(authenticationFlow: .register)
case .reportProblem:
showReportProblemScreen()
}
Expand All @@ -113,7 +113,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
switch action {
case .signInManually:
navigationStackCoordinator.setSheetCoordinator(nil)
Task { await self.startAuthentication(flow: .login) }
showServerConfirmationScreen(authenticationFlow: .login)
case .cancel:
navigationStackCoordinator.setSheetCoordinator(nil)
case .done(let userSession):
Expand All @@ -137,25 +137,14 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
bugReportFlowCoordinator?.start()
}

private func startAuthentication(flow: AuthenticationFlow) async {
startLoading()

switch await authenticationService.configure(for: appSettings.defaultHomeserverAddress) {
case .success:
stopLoading()
showServerConfirmationScreen(authenticationFlow: flow)
case .failure:
stopLoading()
showServerSelectionScreen(authenticationFlow: flow, isModallyPresented: false)
}
}

private func showServerSelectionScreen(authenticationFlow: AuthenticationFlow, isModallyPresented: Bool) {
// TODO: Move this method after showServerConfirmationScreen
private func showServerSelectionScreen(authenticationFlow: AuthenticationFlow) {
let navigationCoordinator = NavigationStackCoordinator()

let parameters = ServerSelectionScreenCoordinatorParameters(authenticationService: authenticationService,
userIndicatorController: userIndicatorController,
isModallyPresented: isModallyPresented)
authenticationFlow: authenticationFlow,
slidingSyncLearnMoreURL: appSettings.slidingSyncLearnMoreURL,
userIndicatorController: userIndicatorController)
let coordinator = ServerSelectionScreenCoordinator(parameters: parameters)

coordinator.actions
Expand All @@ -164,42 +153,26 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {

switch action {
case .updated:
if isModallyPresented {
navigationStackCoordinator.setSheetCoordinator(nil)
} else {
// We are here because the default server failed to respond.
if authenticationService.homeserver.value.loginMode == .password {
if authenticationFlow == .login {
// Add the password login screen directly to the flow, its fine.
showLoginScreen()
} else {
// Add the web registration screen directly to the flow, its fine.
showWebRegistration()
}
} else {
// OIDC is presented from the confirmation screen so replace the
// server selection screen which was inserted to handle the failure.
navigationStackCoordinator.pop()
showServerConfirmationScreen(authenticationFlow: authenticationFlow)
}
}
navigationStackCoordinator.setSheetCoordinator(nil)
case .dismiss:
navigationStackCoordinator.setSheetCoordinator(nil)
}
}
.store(in: &cancellables)

if isModallyPresented {
navigationCoordinator.setRootCoordinator(coordinator)
navigationStackCoordinator.setSheetCoordinator(navigationCoordinator)
} else {
navigationStackCoordinator.push(coordinator)
}
navigationCoordinator.setRootCoordinator(coordinator)
navigationStackCoordinator.setSheetCoordinator(navigationCoordinator)
}

private func showServerConfirmationScreen(authenticationFlow: AuthenticationFlow) {
// Reset the service back to the default homeserver before continuing. This ensures
// we check that registration is supported if it was previously configured for login.
authenticationService.reset()

let parameters = ServerConfirmationScreenCoordinatorParameters(authenticationService: authenticationService,
authenticationFlow: authenticationFlow)
authenticationFlow: authenticationFlow,
slidingSyncLearnMoreURL: appSettings.slidingSyncLearnMoreURL,
userIndicatorController: userIndicatorController)
let coordinator = ServerConfirmationScreenCoordinator(parameters: parameters)

coordinator.actions.sink { [weak self] action in
Expand All @@ -215,7 +188,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
showLoginScreen()
}
case .changeServer:
showServerSelectionScreen(authenticationFlow: authenticationFlow, isModallyPresented: true)
showServerSelectionScreen(authenticationFlow: authenticationFlow)
}
}
.store(in: &cancellables)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// Copyright 2024 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only
// Please see LICENSE in the repository root for full details.
//

import Foundation
import MatrixRustSDK

extension AuthenticationClientBuilderFactoryMock {
struct Configuration {
var builderConfiguration: AuthenticationClientBuilderMock.Configuration = .init()
}

convenience init(configuration: Configuration) {
self.init()

let clientBuilder = AuthenticationClientBuilderMock(configuration: configuration.builderConfiguration)
makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksReturnValue = clientBuilder
}
}
47 changes: 47 additions & 0 deletions ElementX/Sources/Mocks/AuthenticationClientBuilderMock.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// Copyright 2024 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only
// Please see LICENSE in the repository root for full details.
//

import Foundation
import MatrixRustSDK

extension AuthenticationClientBuilderMock {
struct Configuration {
var homeserverClients = [
"matrix.org": ClientSDKMock(configuration: .init()),
"example.com": ClientSDKMock(configuration: .init(serverAddress: "example.com",
homeserverURL: "https://matrix.example.com",
slidingSyncVersion: .native,
supportsPasswordLogin: true,
elementWellKnown: "")),
"company.com": ClientSDKMock(configuration: .init(serverAddress: "company.com",
homeserverURL: "https://matrix.company.com",
slidingSyncVersion: .native,
oidcLoginURL: "https://auth.company.com/oidc",
supportsPasswordLogin: false,
elementWellKnown: "")),
"server.net": ClientSDKMock(configuration: .init(serverAddress: "server.net",
homeserverURL: "https://matrix.example.com",
slidingSyncVersion: .native,
supportsPasswordLogin: false,
elementWellKnown: ""))
]
var qrCodeClient = ClientSDKMock(configuration: .init())
}

convenience init(configuration: Configuration) {
self.init()

buildHomeserverAddressClosure = { address in
guard let client = configuration.homeserverClients[address] else {
throw ClientBuildError.ServerUnreachable(message: "Not a known homeserver.")
}
return client
}

buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerReturnValue = configuration.qrCodeClient
}
}
Loading

0 comments on commit a8dbda9

Please sign in to comment.