mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 23:32:41 +00:00
Reauthentication: Add ReauthenticationCoordinator that handles reauthentication. It is used before calling an authenticated API.
This commit is contained in:
parent
ee2ac8ba96
commit
ece4327971
5 changed files with 328 additions and 0 deletions
157
Riot/Modules/Reauthentication/ReauthenticationCoordinator.swift
Normal file
157
Riot/Modules/Reauthentication/ReauthenticationCoordinator.swift
Normal file
|
@ -0,0 +1,157 @@
|
|||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh Reauthentication Reauthentication
|
||||
/*
|
||||
Copyright 2021 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 UIKit
|
||||
|
||||
enum ReauthenticationCoordinatorError: Error {
|
||||
case failToBuildPasswordParameters
|
||||
}
|
||||
|
||||
@objcMembers
|
||||
final class ReauthenticationCoordinator: ReauthenticationCoordinatorType {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let parameters: ReauthenticationCoordinatorParameters
|
||||
private let authenticationSessionService: AuthenticationSessionService
|
||||
private let authenticationParametersBuilder: AuthenticationParametersBuilder
|
||||
private let authenticatedSessionViewControllerFactory: AuthenticatedSessionViewControllerFactory
|
||||
|
||||
private var ssoAuthenticationPresenter: SSOAuthenticationPresenter?
|
||||
|
||||
private var authenticationSession: SSOAuthentificationSessionProtocol?
|
||||
|
||||
private var presentingViewController: UIViewController {
|
||||
return self.parameters.presenter.toPresentable()
|
||||
}
|
||||
|
||||
private weak var passwordViewController: UIViewController?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
|
||||
weak var delegate: ReauthenticationCoordinatorDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(parameters: ReauthenticationCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
self.authenticationSessionService = AuthenticationSessionService(session: parameters.session)
|
||||
self.authenticationParametersBuilder = AuthenticationParametersBuilder()
|
||||
self.authenticatedSessionViewControllerFactory = AuthenticatedSessionViewControllerFactory()
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
func start() {
|
||||
|
||||
self.authenticationSessionService.authenticationSession(for: self.parameters.authenticationSessionParameters) { (result) in
|
||||
|
||||
switch result {
|
||||
case .success(let authenticationSessionResult):
|
||||
|
||||
switch authenticationSessionResult {
|
||||
case .authenticationNotNeeded:
|
||||
NSLog("[ReauthenticationCoordinator] No need to login again")
|
||||
self.delegate?.reauthenticationCoordinatorDidComplete(self, withAuthenticationParameters: nil)
|
||||
case .authenticationNeeded(let authenticationSession):
|
||||
if self.authenticationSessionService.hasPasswordFlow(inFlows: authenticationSession.flows) {
|
||||
self.showPasswordAuthentication(with: authenticationSession)
|
||||
} else if let authenticationFallbackURL = self.authenticationSessionService.firstUncompletedStageAuthenticationFallbackURL(for: authenticationSession) {
|
||||
|
||||
self.showFallbackAuthentication(with: authenticationFallbackURL, authenticationSession: authenticationSession)
|
||||
} else {
|
||||
self.delegate?.reauthenticationCoordinator(self, didFailWithError: AuthenticationSessionServiceError.flowNotSupported)
|
||||
}
|
||||
}
|
||||
case .failure(let error):
|
||||
self.delegate?.reauthenticationCoordinator(self, didFailWithError: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.parameters.presenter.toPresentable()
|
||||
}
|
||||
|
||||
// MARK: - Private methods
|
||||
|
||||
private func showPasswordAuthentication(with authenticationSession: MXAuthenticationSession) {
|
||||
guard let userId = parameters.session.myUser.userId else {
|
||||
return
|
||||
}
|
||||
|
||||
let passwordViewController = self.authenticatedSessionViewControllerFactory.createPasswordViewController(title: self.parameters.title, message: self.parameters.message) { [weak self] (password) in
|
||||
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let sessionId = authenticationSession.session, let authenticationParameters = self.authenticationParametersBuilder.buildPasswordParameters(sessionId: sessionId, userId: userId, password: password) else {
|
||||
self.delegate?.reauthenticationCoordinator(self, didFailWithError: ReauthenticationCoordinatorError.failToBuildPasswordParameters)
|
||||
return
|
||||
}
|
||||
|
||||
self.delegate?.reauthenticationCoordinatorDidComplete(self, withAuthenticationParameters: authenticationParameters)
|
||||
|
||||
} onCancelled: { [weak self] in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
self.delegate?.reauthenticationCoordinatorDidCancel(self)
|
||||
}
|
||||
|
||||
self.presentingViewController.present(passwordViewController, animated: true)
|
||||
}
|
||||
|
||||
private func showFallbackAuthentication(with authenticationURL: URL, authenticationSession: MXAuthenticationSession) {
|
||||
|
||||
// NOTE: Prefer use a callback and the same mechanism as SSOAuthentificationSession instead of using custom WKWebView
|
||||
let reauthFallbackViewController: ReauthFallBackViewController = ReauthFallBackViewController(url: authenticationURL.absoluteString)
|
||||
reauthFallbackViewController.title = self.parameters.title
|
||||
|
||||
reauthFallbackViewController.didCancel = { [weak self] in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
self.delegate?.reauthenticationCoordinatorDidCancel(self)
|
||||
}
|
||||
|
||||
reauthFallbackViewController.didValidate = { [weak self] in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let sessionId = authenticationSession.session else {
|
||||
self.delegate?.reauthenticationCoordinator(self, didFailWithError: ReauthenticationCoordinatorError.failToBuildPasswordParameters)
|
||||
return
|
||||
}
|
||||
|
||||
let authenticationParameters = self.authenticationParametersBuilder.buildOAuthParameters(with: sessionId)
|
||||
self.delegate?.reauthenticationCoordinatorDidComplete(self, withAuthenticationParameters: authenticationParameters)
|
||||
}
|
||||
|
||||
let navigationController = RiotNavigationController(rootViewController: reauthFallbackViewController)
|
||||
|
||||
self.presentingViewController.present(navigationController, animated: true)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh Reauthentication Reauthentication
|
||||
/*
|
||||
Copyright 2021 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
|
||||
|
||||
@objc protocol ReauthenticationCoordinatorBridgePresenterDelegate {
|
||||
func reauthenticationCoordinatorBridgePresenterDidComplete(_ bridgePresenter: ReauthenticationCoordinatorBridgePresenter, withAuthenticationParameters authenticationParameters: [String: Any]?)
|
||||
func reauthenticationCoordinatorBridgePresenterDidCancel(_ bridgePresenter: ReauthenticationCoordinatorBridgePresenter)
|
||||
func reauthenticationCoordinatorBridgePresenter(_ bridgePresenter: ReauthenticationCoordinatorBridgePresenter, didFailWithError error: Error)
|
||||
|
||||
}
|
||||
|
||||
/// ReauthenticationCoordinatorBridgePresenter enables to start ReauthenticationCoordinator from a view controller.
|
||||
/// This bridge is used while waiting for global usage of coordinator pattern.
|
||||
/// It breaks the Coordinator abstraction and it has been introduced for Objective-C compatibility (mainly for integration in legacy view controllers).
|
||||
/// Each bridge should be removed once the underlying Coordinator has been integrated by another Coordinator.
|
||||
@objcMembers
|
||||
final class ReauthenticationCoordinatorBridgePresenter: NSObject {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let parameters: ReauthenticationCoordinatorParameters
|
||||
private var coordinator: ReauthenticationCoordinator?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var delegate: ReauthenticationCoordinatorBridgePresenterDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(parameters: ReauthenticationCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func present(from viewController: UIViewController, animated: Bool) {
|
||||
let reauthenticationCoordinator = ReauthenticationCoordinator(parameters: self.parameters)
|
||||
reauthenticationCoordinator.delegate = self
|
||||
viewController.present(reauthenticationCoordinator.toPresentable(), animated: animated, completion: nil)
|
||||
reauthenticationCoordinator.start()
|
||||
|
||||
self.coordinator = reauthenticationCoordinator
|
||||
}
|
||||
|
||||
func dismiss(animated: Bool, completion: (() -> Void)?) {
|
||||
guard let coordinator = self.coordinator else {
|
||||
return
|
||||
}
|
||||
coordinator.toPresentable().dismiss(animated: animated) {
|
||||
self.coordinator = nil
|
||||
|
||||
if let completion = completion {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ReauthenticationCoordinatorDelegate
|
||||
extension ReauthenticationCoordinatorBridgePresenter: ReauthenticationCoordinatorDelegate {
|
||||
func reauthenticationCoordinatorDidComplete(_ coordinator: ReauthenticationCoordinatorType, withAuthenticationParameters authenticationParameters: [String: Any]?) {
|
||||
self.delegate?.reauthenticationCoordinatorBridgePresenterDidComplete(self, withAuthenticationParameters: authenticationParameters)
|
||||
}
|
||||
|
||||
func reauthenticationCoordinatorDidCancel(_ coordinator: ReauthenticationCoordinatorType) {
|
||||
self.delegate?.reauthenticationCoordinatorBridgePresenterDidCancel(self)
|
||||
}
|
||||
|
||||
func reauthenticationCoordinator(_ coordinator: ReauthenticationCoordinatorType, didFailWithError error: Error) {
|
||||
self.delegate?.reauthenticationCoordinatorBridgePresenter(self, didFailWithError: error)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
//
|
||||
// Copyright 2021 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
|
||||
|
||||
/// ReauthenticationCoordinator input parameters
|
||||
@objcMembers
|
||||
class ReauthenticationCoordinatorParameters: NSObject {
|
||||
|
||||
/// The Matrix session
|
||||
let session: MXSession
|
||||
|
||||
/// The presenter used to show authentication screen(s)
|
||||
let presenter: Presentable
|
||||
|
||||
/// The title to use in the authentication screen if present.
|
||||
let title: String?
|
||||
|
||||
/// The message to use in the authentication screen if present.
|
||||
let message: String?
|
||||
|
||||
/// The authenticated API endpoint parameters
|
||||
let authenticationSessionParameters: AuthenticationSessionParameters
|
||||
|
||||
init(session: MXSession,
|
||||
presenter: Presentable,
|
||||
title: String?,
|
||||
message: String?,
|
||||
authenticationSessionParameters: AuthenticationSessionParameters) {
|
||||
self.session = session
|
||||
self.presenter = presenter
|
||||
self.title = title
|
||||
self.message = message
|
||||
self.authenticationSessionParameters = authenticationSessionParameters
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// File created from FlowTemplate
|
||||
// $ createRootCoordinator.sh Reauthentication Reauthentication
|
||||
/*
|
||||
Copyright 2021 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
|
||||
|
||||
protocol ReauthenticationCoordinatorDelegate: class {
|
||||
func reauthenticationCoordinatorDidComplete(_ coordinator: ReauthenticationCoordinatorType, withAuthenticationParameters: [String: Any]?)
|
||||
func reauthenticationCoordinatorDidCancel(_ coordinator: ReauthenticationCoordinatorType)
|
||||
func reauthenticationCoordinator(_ coordinator: ReauthenticationCoordinatorType, didFailWithError: Error)
|
||||
}
|
||||
|
||||
/// `ReauthenticationCoordinatorType` is a protocol describing a Coordinator that handle reauthentication. It is used before calling an authenticated API.
|
||||
protocol ReauthenticationCoordinatorType: Coordinator, Presentable {
|
||||
var delegate: ReauthenticationCoordinatorDelegate? { get }
|
||||
}
|
|
@ -26,3 +26,4 @@
|
|||
#import "RoomSettingsViewController.h"
|
||||
#import "JitsiWidgetData.h"
|
||||
#import "InviteRecentTableViewCell.h"
|
||||
#import "AuthFallBackViewController.h"
|
||||
|
|
Loading…
Reference in a new issue