Skip to content

Commit

Permalink
Merge pull request #6257 from vector-im/ismail/6181_auth_soft_logout
Browse files Browse the repository at this point in the history
  • Loading branch information
ismailgulek authored Jun 9, 2022
2 parents 85db782 + bcb9288 commit 812dffe
Show file tree
Hide file tree
Showing 24 changed files with 1,215 additions and 92 deletions.
29 changes: 29 additions & 0 deletions Riot/Categories/MXFileStore.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation

extension MXFileStore {

func displayName(ofUserWithId userId: String) async -> String? {
await withCheckedContinuation({ continuation in
asyncUsers(withUserIds: [userId]) { users in
continuation.resume(returning: users.first?.displayname)
}
})
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ enum AuthenticationCoordinatorResult {
case didLogin(session: MXSession, authenticationFlow: AuthenticationFlow, authenticationType: AuthenticationType)
/// All of the required authentication steps including key verification is complete.
case didComplete
/// In case of soft logout, user has decided to clear all data
case clearAllData
/// The user has cancelled the associated authentication flow.
case cancel(AuthenticationFlow)
}
Expand All @@ -40,9 +42,6 @@ protocol AuthenticationCoordinatorProtocol: Coordinator, Presentable {
/// Update the screen to display registration or login.
func update(authenticationFlow: AuthenticationFlow)

/// Update the screen to use any credentials to use after a soft logout has taken place.
func update(softLogoutCredentials: MXCredentials)

/// Indicates to the coordinator to display any pending screens if it was created with
/// the `canPresentAdditionalScreens` parameter set to `false`
func presentPendingScreensIfNecessary()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ final class LegacyAuthenticationCoordinator: NSObject, AuthenticationCoordinator
private var canPresentAdditionalScreens: Bool
private var isWaitingToPresentCompleteSecurity = false
private var verificationListener: SessionVerificationListener?
private let authenticationService: AuthenticationService = .shared

/// The session created when successfully authenticated.
private var session: MXSession?
Expand All @@ -61,6 +62,7 @@ final class LegacyAuthenticationCoordinator: NSObject, AuthenticationCoordinator
self.canPresentAdditionalScreens = parameters.canPresentAdditionalScreens

let authenticationViewController = AuthenticationViewController()
authenticationViewController.softLogoutCredentials = authenticationService.softLogoutCredentials
self.authenticationViewController = authenticationViewController

// Preload the view as this can a second and lock up the UI at presentation.
Expand All @@ -87,10 +89,6 @@ final class LegacyAuthenticationCoordinator: NSObject, AuthenticationCoordinator
authenticationViewController.authType = authenticationFlow.mxkType
}

func update(softLogoutCredentials: MXCredentials) {
authenticationViewController.softLogoutCredentials = softLogoutCredentials
}

func presentPendingScreensIfNecessary() {
canPresentAdditionalScreens = true

Expand Down
8 changes: 7 additions & 1 deletion Riot/Modules/MatrixKit/Models/Account/MXKAccount.m
Original file line number Diff line number Diff line change
Expand Up @@ -871,7 +871,7 @@ - (void)closeSession:(BOOL)clearStore

- (void)logout:(void (^)(void))completion
{
if (!mxSession)
if (!mxSession || !mxSession.matrixRestClient)
{
MXLogDebug(@"[MXKAccount] logout: Need to open the closed session to make a logout request");
id<MXStore> store = [[[MXKAccountManager sharedManager].storeClass alloc] init];
Expand Down Expand Up @@ -958,6 +958,12 @@ - (void)logoutSendingServerRequest:(BOOL)sendLogoutServerRequest

- (void)softLogout
{
if (_isSoftLogout)
{
// do not close the session if already soft logged out
// it may break the current logout request and resetting session credentials can cause crashes
return;
}
_isSoftLogout = YES;
[[MXKAccountManager sharedManager] saveAccounts];

Expand Down
87 changes: 82 additions & 5 deletions Riot/Modules/Onboarding/AuthenticationCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,20 @@ final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtoc

/// Starts the authentication flow.
@MainActor private func startAuthenticationFlow() async {
if let softLogoutCredentials = authenticationService.softLogoutCredentials,
let homeserverAddress = softLogoutCredentials.homeServer {
do {
try await authenticationService.startFlow(.login, for: homeserverAddress)
} catch {
MXLog.error("[AuthenticationCoordinator] start: Failed to start")
displayError(message: error.localizedDescription)
}

await showSoftLogoutScreen(softLogoutCredentials)

return
}

let flow: AuthenticationFlow = initialScreen == .login ? .login : .register
if initialScreen != .selectServerForRegistration {
do {
Expand Down Expand Up @@ -232,6 +246,54 @@ final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtoc
}
}
}

/// Shows the soft logout screen.
@MainActor private func showSoftLogoutScreen(_ credentials: MXCredentials) async {
MXLog.debug("[AuthenticationCoordinator] showSoftLogoutScreen")

guard let userId = credentials.userId else {
MXLog.failure("[AuthenticationCoordinator] showSoftLogoutScreen: Missing userId.")
displayError(message: VectorL10n.errorCommonMessage)
return
}

let store = MXFileStore(credentials: credentials)
let userDisplayName = await store.displayName(ofUserWithId: userId) ?? ""

let cryptoStore = MXRealmCryptoStore(credentials: credentials)
let keyBackupNeeded = (cryptoStore?.inboundGroupSessions(toBackup: 1) ?? []).count > 0

let softLogoutCredentials = SoftLogoutCredentials(userId: userId,
homeserverName: credentials.homeServerName() ?? "",
userDisplayName: userDisplayName,
deviceId: credentials.deviceId)

let parameters = AuthenticationSoftLogoutCoordinatorParameters(navigationRouter: navigationRouter,
authenticationService: authenticationService,
credentials: softLogoutCredentials,
keyBackupNeeded: keyBackupNeeded)
let coordinator = AuthenticationSoftLogoutCoordinator(parameters: parameters)
coordinator.callback = { [weak self] result in
guard let self = self else { return }
switch result {
case .success(let session, let loginPassword):
self.password = loginPassword
self.authenticationType = .password
self.onSessionCreated(session: session, flow: .login)
case .clearAllData:
self.callback?(.clearAllData)
case .continueWithSSO(let provider):
self.presentSSOAuthentication(for: provider)
case .fallback:
self.showFallback(for: .login, deviceId: softLogoutCredentials.deviceId)
}
}

coordinator.start()
add(childCoordinator: coordinator)

navigationRouter.setRootModule(coordinator, popCompletion: nil)
}

/// Displays the next view in the flow based on the result from the registration screen.
@MainActor private func loginCoordinator(_ coordinator: AuthenticationLoginCoordinator,
Expand Down Expand Up @@ -503,8 +565,26 @@ final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtoc

// MARK: - Additional Screens

private func showFallback(for flow: AuthenticationFlow) {
let url = authenticationService.fallbackURL(for: flow)
private func showFallback(for flow: AuthenticationFlow, deviceId: String? = nil) {
var url = authenticationService.fallbackURL(for: flow)

if let deviceId = deviceId {
// add deviceId as `device_id` into the url
guard var urlComponents = URLComponents(string: url.absoluteString) else {
MXLog.error("[AuthenticationCoordinator] showFallback: could not create url components")
return
}
var queryItems = urlComponents.queryItems ?? []
queryItems.append(URLQueryItem(name: "device_id", value: deviceId))
urlComponents.queryItems = queryItems

if let newUrl = urlComponents.url {
url = newUrl
} else {
MXLog.error("[AuthenticationCoordinator] showFallback: could not create url from components")
return
}
}

MXLog.debug("[AuthenticationCoordinator] showFallback for: \(flow), url: \(url)")

Expand Down Expand Up @@ -717,9 +797,6 @@ extension AuthenticationCoordinator {
// unused
}

func update(softLogoutCredentials: MXCredentials) {
// unused
}
}

// MARK: - AuthFallBackViewControllerDelegate
Expand Down
Loading

0 comments on commit 812dffe

Please sign in to comment.