diff --git a/Brand/Intro/NCIntroViewController.swift b/Brand/Intro/NCIntroViewController.swift index c626a4c557..5cd48beb2d 100644 --- a/Brand/Intro/NCIntroViewController.swift +++ b/Brand/Intro/NCIntroViewController.swift @@ -164,7 +164,7 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol } @IBAction func signupWithProvider(_ sender: Any) { - if let viewController = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginProvider") as? NCLoginProvider { + if let viewController = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginProvider") as? NCWebViewLoginProvider { viewController.controller = self.controller viewController.initialURLString = NCBrandOptions.shared.linkloginPreferredProviders self.navigationController?.pushViewController(viewController, animated: true) diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj index 36507c1f2b..0deb1e49ba 100644 --- a/Nextcloud.xcodeproj/project.pbxproj +++ b/Nextcloud.xcodeproj/project.pbxproj @@ -45,6 +45,7 @@ AABD0C8A2D5F67A400F009E6 /* XCUIElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = AABD0C892D5F67A200F009E6 /* XCUIElement.swift */; }; AABD0C9B2D5F73FC00F009E6 /* Placeholder.swift in Sources */ = {isa = PBXBuildFile; fileRef = AABD0C9A2D5F73FA00F009E6 /* Placeholder.swift */; }; AAE330042D2ED20200B04903 /* NCShareNavigationTitleSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE330032D2ED1FF00B04903 /* NCShareNavigationTitleSetting.swift */; }; + AC72BE742EFEF4AD006ACB5F /* NCProviderLoginHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7ACF3912F3E697700567B56 /* NCProviderLoginHandler.swift */; }; AF1A9B6427D0CA1E00F17A9E /* UIAlertController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1A9B6327D0CA1E00F17A9E /* UIAlertController+Extension.swift */; }; AF1A9B6527D0CC0500F17A9E /* UIAlertController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1A9B6327D0CA1E00F17A9E /* UIAlertController+Extension.swift */; }; AF22B206277B4E4C00DAB0CC /* NCCreateFormUploadConflict.swift in Sources */ = {isa = PBXBuildFile; fileRef = F704B5E42430AA8000632F5F /* NCCreateFormUploadConflict.swift */; }; @@ -693,7 +694,8 @@ F7A8D74428F1827B008BBE1C /* ThreadSafeDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7245923289BB50B00474787 /* ThreadSafeDictionary.swift */; }; F7AC1CB028AB94490032D99F /* Array+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7AC1CAF28AB94490032D99F /* Array+Extension.swift */; }; F7AC934A296193050002BC0F /* Reasons to use Nextcloud.pdf in Resources */ = {isa = PBXBuildFile; fileRef = F7AC9349296193050002BC0F /* Reasons to use Nextcloud.pdf */; }; - F7AE00F5230D5F9E007ACF8A /* NCLoginProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7AE00F4230D5F9E007ACF8A /* NCLoginProvider.swift */; }; + F7ACF26E2F3CB59300567B56 /* NCLoginFlowPoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7ACF26D2F3CB59300567B56 /* NCLoginFlowPoller.swift */; }; + F7AE00F5230D5F9E007ACF8A /* NCWebViewLoginProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7AE00F4230D5F9E007ACF8A /* NCWebViewLoginProvider.swift */; }; F7AE00F8230E81CB007ACF8A /* NCBrowserWeb.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7AE00F7230E81CB007ACF8A /* NCBrowserWeb.swift */; }; F7AE00FA230E81EB007ACF8A /* NCBrowserWeb.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7AE00F9230E81EB007ACF8A /* NCBrowserWeb.storyboard */; }; F7B398422A6A91D5007538D6 /* NCSectionFirstHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7B398412A6A91D5007538D6 /* NCSectionFirstHeader.xib */; }; @@ -1611,7 +1613,9 @@ F7AA41E127C7CF8100494705 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/InfoPlist.strings"; sourceTree = ""; }; F7AC1CAF28AB94490032D99F /* Array+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Extension.swift"; sourceTree = ""; }; F7AC9349296193050002BC0F /* Reasons to use Nextcloud.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = "Reasons to use Nextcloud.pdf"; sourceTree = SOURCE_ROOT; }; - F7AE00F4230D5F9E007ACF8A /* NCLoginProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCLoginProvider.swift; sourceTree = ""; }; + F7ACF26D2F3CB59300567B56 /* NCLoginFlowPoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCLoginFlowPoller.swift; sourceTree = ""; }; + F7ACF3912F3E697700567B56 /* NCProviderLoginHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCProviderLoginHandler.swift; sourceTree = ""; }; + F7AE00F4230D5F9E007ACF8A /* NCWebViewLoginProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCWebViewLoginProvider.swift; sourceTree = ""; }; F7AE00F7230E81CB007ACF8A /* NCBrowserWeb.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCBrowserWeb.swift; sourceTree = ""; }; F7AE00F9230E81EB007ACF8A /* NCBrowserWeb.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCBrowserWeb.storyboard; sourceTree = ""; }; F7B1A7761EBB3C8000BFB6D1 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; @@ -2896,7 +2900,9 @@ F702F2F025EE5CDA008F8E80 /* NCLogin.storyboard */, F702F2F625EE5CEC008F8E80 /* NCLogin.swift */, F745B252222D88AE00346520 /* NCLoginQRCode.swift */, - F7AE00F4230D5F9E007ACF8A /* NCLoginProvider.swift */, + F7ACF26D2F3CB59300567B56 /* NCLoginFlowPoller.swift */, + F7ACF3912F3E697700567B56 /* NCProviderLoginHandler.swift */, + F7AE00F4230D5F9E007ACF8A /* NCWebViewLoginProvider.swift */, F7BC287D26663F6C004D46C5 /* NCViewCertificateDetails.storyboard */, F7BC287F26663F85004D46C5 /* NCViewCertificateDetails.swift */, ); @@ -4536,6 +4542,7 @@ F7E7AEA52BA32C6500512E52 /* NCCollectionViewDownloadThumbnail.swift in Sources */, AF730AF827834B1400B7520E /* NCShare+NCCellDelegate.swift in Sources */, F70460522499061800BB98A7 /* NotificationCenter+MainThread.swift in Sources */, + AC72BE742EFEF4AD006ACB5F /* NCProviderLoginHandler.swift in Sources */, F3C587AE2D47E4FE004532DB /* PHAssetCollectionThumbnailLoader.swift in Sources */, F78F74362163781100C2ADAD /* NCTrash.swift in Sources */, F39298972A3B12CB00509762 /* BaseNCMoreCell.swift in Sources */, @@ -4648,7 +4655,8 @@ F74C0436253F1CDC009762AB /* NCShares.swift in Sources */, F79699E72E689F68000EC82A /* NCMediaNavigationController.swift in Sources */, F7AC1CB028AB94490032D99F /* Array+Extension.swift in Sources */, - F7AE00F5230D5F9E007ACF8A /* NCLoginProvider.swift in Sources */, + F7ACF26E2F3CB59300567B56 /* NCLoginFlowPoller.swift in Sources */, + F7AE00F5230D5F9E007ACF8A /* NCWebViewLoginProvider.swift in Sources */, F707C26521A2DC5200F6181E /* NCStoreReview.swift in Sources */, F7CF06802E0FF3990063AD04 /* NCAppStateManager.swift in Sources */, F7BAADCB1ED5A87C00B7EAD4 /* NCManageDatabase.swift in Sources */, diff --git a/iOSClient/Login/NCLogin.storyboard b/iOSClient/Login/NCLogin.storyboard index 981eadcc58..19c321d8fe 100644 --- a/iOSClient/Login/NCLogin.storyboard +++ b/iOSClient/Login/NCLogin.storyboard @@ -147,10 +147,10 @@ - + - + diff --git a/iOSClient/Login/NCLogin.swift b/iOSClient/Login/NCLogin.swift index f483ed06ec..c1a6e272f9 100644 --- a/iOSClient/Login/NCLogin.swift +++ b/iOSClient/Login/NCLogin.swift @@ -8,7 +8,7 @@ import UIKit import NextcloudKit import SwiftEntryKit import SwiftUI -import SafariServices +import AuthenticationServices class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { @IBOutlet weak var imageBrand: UIImageView! @@ -28,6 +28,8 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { private var activeTextField = UITextField() private var shareAccounts: [NKShareAccounts.DataAccounts]? + private let loginFlowPoller = NCLoginFlowPoller() + private var authenticationSession: ASWebAuthenticationSession? /// Controller var controller: NCMainTabBarController? @@ -197,6 +199,16 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { } } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + if isMovingFromParent || isBeingDismissed { + loginFlowPoller.cancel() + authenticationSession?.cancel() + authenticationSession = nil + } + } + private func handleLoginWithAppConfig() { let accountCount = NCManageDatabase.shared.getAccounts()?.count ?? 0 @@ -330,14 +342,9 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { let loginOptions = NKRequestOptions(customUserAgent: userAgent) NextcloudKit.shared.getLoginFlowV2(serverUrl: url, options: loginOptions) { [self] token, endpoint, login, _, error in // Login Flow V2 - if error == .success, let token, let endpoint, let login { + if error == .success, let token, let endpoint, let login, let loginURL = URL(string: login) { nkLog(debug: "Successfully received login flow information.") - let safariVC = NCLoginProvider() - safariVC.initialURLString = login - safariVC.uiColor = textColor - safariVC.delegate = self - safariVC.startPolling(loginFlowV2Token: token, loginFlowV2Endpoint: endpoint, loginFlowV2Login: login) - navigationController?.pushViewController(safariVC, animated: true) + startASWebAuthenticationSession(loginURL: loginURL, token: token, endpoint: endpoint) } } case .failure(let error): @@ -371,6 +378,71 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { } } + private func startLoginFlowPolling(token: String, endpoint: String) { + loginFlowPoller.start(token: token, endpoint: endpoint) { [weak self] grant in + // Finish login v2 flow + await self?.handleLoginGrant(grant) + } + } + + private func startASWebAuthenticationSession(loginURL: URL, token: String, endpoint: String) { + let callbackScheme = URL(string: NCBrandOptions.shared.webLoginAutenticationProtocol)?.scheme ?? loginURL.scheme + + let session = ASWebAuthenticationSession(url: loginURL, + callbackURLScheme: callbackScheme, + completionHandler: handleAuthenticationSessionCompletion(callbackURL:error:)) + + session.presentationContextProvider = self + session.prefersEphemeralWebBrowserSession = true + + authenticationSession = session + + if session.start() { + startLoginFlowPolling(token: token, endpoint: endpoint) + } else { + authenticationSession = nil + loginFlowPoller.cancel() + let error = NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_error_something_wrong_") + NCContentPresenter().showError(error: error, priority: .max) + } + } + + @MainActor + private func handleLoginGrant(_ grant: NCLoginGrant) async { + authenticationSession?.cancel() + authenticationSession = nil + createAccount(urlBase: grant.urlBase, user: grant.loginName, password: grant.appPassword) + } + + @MainActor + private func handleAuthenticationSessionCompletion(callbackURL: URL?, error: Error?) { + loginFlowPoller.cancel() + authenticationSession = nil + + if let error = error as? ASWebAuthenticationSessionError, error.code == .canceledLogin { + // Do nothing as user canceled login flow + return + } + + if let error = error { + nkLog(error: "ASWebAuthenticationSession failed with error: \(error.localizedDescription)") + let error = NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_error_something_wrong_") + NCContentPresenter().showError(error: error, priority: .max) + return + } + + guard let callbackURL, let providerGrant = NCProviderLoginHandler.handle(callbackURL: callbackURL) else { + nkLog(error: "ASWebAuthenticationSession returned an invalid callback URL.") + let error = NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_error_something_wrong_") + NCContentPresenter().showError(error: error, priority: .max) + return + } + + Task { + await handleLoginGrant(NCLoginGrant(urlBase: providerGrant.urlBase, loginName: providerGrant.user, appPassword: providerGrant.password)) + } + } + // MARK: - QRCode func dismissQRCode(_ value: String?, metadataType: String?) { @@ -482,5 +554,16 @@ extension NCLogin: NCLoginProviderDelegate { func onBack() { loginButton.isEnabled = true loginButton.hideSpinnerAndShowButton() + loginFlowPoller.cancel() + authenticationSession?.cancel() + authenticationSession = nil + } +} + +// MARK: - ASWebAuthenticationPresentationContextProviding + +extension NCLogin: ASWebAuthenticationPresentationContextProviding { + func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { + view.window ?? UIWindow() } } diff --git a/iOSClient/Login/NCLoginFlowPoller.swift b/iOSClient/Login/NCLoginFlowPoller.swift new file mode 100644 index 0000000000..c8bc35b7b7 --- /dev/null +++ b/iOSClient/Login/NCLoginFlowPoller.swift @@ -0,0 +1,98 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-License-Identifier: GPL-3.0-or-later + +import Foundation +import NextcloudKit + +struct NCLoginGrant { + let urlBase: String + let loginName: String + let appPassword: String +} + +final class NCLoginFlowPoller { + private var pollingTask: Task? + + func start(token: String, endpoint: String, onGrant: @escaping @MainActor (NCLoginGrant) async -> Void) { + cancel() + + let options = NKRequestOptions(customUserAgent: userAgent) + nkLog(start: "Starting polling at \(endpoint) with token \(token)") + + pollingTask = Task { [weak self] in + guard let self else { return } + defer { self.pollingTask = nil } + + guard let grantValues = await self.waitForGrant(token: token, endpoint: endpoint, options: options) else { + return + } + + await onGrant(grantValues) + nkLog(debug: "Login flow polling task completed.") + } + + nkLog(debug: "Login flow polling task created.") + } + + func cancel() { + guard pollingTask != nil else { + return + } + + nkLog(debug: "Cancelling login polling task...") + pollingTask?.cancel() + pollingTask = nil + } + + private func waitForGrant(token: String, endpoint: String, options: NKRequestOptions) async -> NCLoginGrant? { + var grantValues: NCLoginGrant? + + repeat { + guard !Task.isCancelled else { + nkLog(debug: "Login polling task cancelled before receiving grant values.") + return nil + } + + grantValues = await pollOnce(token: token, endpoint: endpoint, options: options) + if grantValues == nil { + try? await Task.sleep(nanoseconds: 1_000_000_000) // .seconds() is not supported on iOS 15 yet. + } + } while grantValues == nil + + return grantValues + } + + private func pollOnce(token: String, endpoint: String, options: NKRequestOptions) async -> NCLoginGrant? { + await withCheckedContinuation { continuation in + NextcloudKit.shared.getLoginFlowV2Poll(token: token, endpoint: endpoint, options: options) { server, loginName, appPassword, _, error in + + guard error == .success else { + nkLog(error: "Login poll result for token \"\(token)\" is not successful!") + continuation.resume(returning: nil) + return + } + + guard let urlBase = server else { + nkLog(error: "Login poll response field for server for token \"\(token)\" is nil!") + continuation.resume(returning: nil) + return + } + + guard let user = loginName else { + nkLog(error: "Login poll response field for user name for token \"\(token)\" is nil!") + continuation.resume(returning: nil) + return + } + + guard let password = appPassword else { + nkLog(error: "Login poll response field for app password for token \"\(token)\" is nil!") + continuation.resume(returning: nil) + return + } + + nkLog(debug: "Returning login poll response for \"\(user)\" on \"\(urlBase)\" for token \"\(token)\".") + continuation.resume(returning: NCLoginGrant(urlBase: urlBase, loginName: user, appPassword: password)) + } + } + } +} diff --git a/iOSClient/Login/NCProviderLoginHandler.swift b/iOSClient/Login/NCProviderLoginHandler.swift new file mode 100644 index 0000000000..36d7927163 --- /dev/null +++ b/iOSClient/Login/NCProviderLoginHandler.swift @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2025 Iva Horn +// SPDX-FileCopyrightText: 2025 Milen Pivchev +// SPDX-License-Identifier: GPL-3.0-or-later + +import Foundation + +struct NCProviderLoginHandler { + static func handle(callbackURL: URL) -> (urlBase: String, user: String, password: String)? { + let callbackURLString = callbackURL.absoluteString.lowercased() + let protocolPrefix = NCBrandOptions.shared.webLoginAutenticationProtocol.lowercased() + + guard callbackURLString.hasPrefix(protocolPrefix), callbackURLString.contains("login") else { + return nil + } + + var server: String = "" + var user: String = "" + var password: String = "" + let keyValue = callbackURL.path.components(separatedBy: "&") + + for value in keyValue { + if value.contains("server:") { server = value } + if value.contains("user:") { user = value } + if value.contains("password:") { password = value } + } + + guard !server.isEmpty, !user.isEmpty, !password.isEmpty else { + return nil + } + + let serverClean = server.replacingOccurrences(of: "/server:", with: "") + let username = user.replacingOccurrences(of: "user:", with: "").replacingOccurrences(of: "+", with: " ") + let passwordClean = password.replacingOccurrences(of: "password:", with: "") + + return (serverClean, username, passwordClean) + } +} diff --git a/iOSClient/Login/NCLoginProvider.swift b/iOSClient/Login/NCWebViewLoginProvider.swift similarity index 54% rename from iOSClient/Login/NCLoginProvider.swift rename to iOSClient/Login/NCWebViewLoginProvider.swift index cbdd3a3337..fb0b206661 100644 --- a/iOSClient/Login/NCLoginProvider.swift +++ b/iOSClient/Login/NCWebViewLoginProvider.swift @@ -18,7 +18,7 @@ protocol NCLoginProviderDelegate: AnyObject { /// /// View which presents the web view to login at a Nextcloud instance. /// -class NCLoginProvider: UIViewController { +class NCWebViewLoginProvider: UIViewController { var webView: WKWebView! var titleView: String = "" var initialURLString = "" @@ -26,11 +26,6 @@ class NCLoginProvider: UIViewController { weak var delegate: NCLoginProviderDelegate? var controller: NCMainTabBarController? - /// - /// A polling loop active in the background to check for the current status of the login flow. - /// - var pollingTask: Task? - // MARK: - View Life Cycle override func viewDidLoad() { @@ -85,14 +80,6 @@ class NCLoginProvider: UIViewController { nkLog(debug: "Login provider view did disappear.") NCActivityIndicator.shared.stop() - - guard pollingTask != nil else { - return - } - - nkLog(debug: "Cancelling existing polling task because view did disappear...") - pollingTask?.cancel() - pollingTask = nil } // MARK: - Navigation @@ -119,98 +106,11 @@ class NCLoginProvider: UIViewController { navigationController?.popViewController(animated: true) } } - - // MARK: - Polling - - /// - /// Start checking the status of the login flow in the background periodally. - /// - func startPolling(loginFlowV2Token: String, loginFlowV2Endpoint: String, loginFlowV2Login: String) { - nkLog(start: "Starting polling at \(loginFlowV2Endpoint) with token \(loginFlowV2Token)") - pollingTask = createPollingTask(token: loginFlowV2Token, endpoint: loginFlowV2Endpoint) - nkLog(debug: "Polling task created.") - } - - /// - /// Fetch the server response and process it. - /// - private func poll(token: String, endpoint: String, options: NKRequestOptions) async -> (urlBase: String, loginName: String, appPassword: String)? { - await withCheckedContinuation { continuation in - NextcloudKit.shared.getLoginFlowV2Poll(token: token, endpoint: endpoint, options: options) {server, loginName, appPassword, _, error in - - guard error == .success else { - nkLog(error: "Login poll result for token \"\(token)\" is not successful!") - continuation.resume(returning: nil) - return - } - - guard let urlBase = server else { - nkLog(error: "Login poll response field for server for token \"\(token)\" is nil!") - continuation.resume(returning: nil) - return - } - - guard let user = loginName else { - nkLog(error: "Login poll response field for user name for token \"\(token)\" is nil!") - continuation.resume(returning: nil) - return - } - - guard let appPassword = appPassword else { - nkLog(error: "Login poll response field for app password for token \"\(token)\" is nil!") - continuation.resume(returning: nil) - return - } - - nkLog(debug: "Returning login poll response for \"\(user)\" on \"\(urlBase)\" for token \"\(token)\".") - continuation.resume(returning: (urlBase, user, appPassword)) - } - } - } - - /// - /// Handle the values acquired by polling successfully. - /// - private func handleGrant(urlBase: String, loginName: String, appPassword: String) async { - nkLog(debug: "Handling login grant values for \(loginName) on \(urlBase)") - - if controller == nil { - nkLog(debug: "View controller is still undefined, will resolve root view controller of first window.") - controller = UIApplication.shared.mainAppWindow?.rootViewController as? NCMainTabBarController - } - - await NCAccount().createAccount(viewController: self, urlBase: urlBase, user: loginName, password: appPassword, controller: controller) - nkLog(debug: "Account creation for \(loginName) on \(urlBase) completed based on login grant values.") - } - - /// - /// Set up the `Task` which frequently checks the server. - /// - private func createPollingTask(token: String, endpoint: String) -> Task { - let options = NKRequestOptions(customUserAgent: userAgent) - var grantValues: (urlBase: String, loginName: String, appPassword: String)? - - return Task { @MainActor in - repeat { - try Task.checkCancellation() - - grantValues = await poll(token: token, endpoint: endpoint, options: options) - try await Task.sleep(nanoseconds: 1_000_000_000) // .seconds() is not supported on iOS 15 yet. - } while grantValues == nil - - guard let grantValues else { - return - } - - await handleGrant(urlBase: grantValues.urlBase, loginName: grantValues.loginName, appPassword: grantValues.appPassword) - nkLog(debug: "Polling task completed.") - } - } } // MARK: - WKNavigationDelegate -extension NCLoginProvider: WKNavigationDelegate { +extension NCWebViewLoginProvider: WKNavigationDelegate { func webView(_ webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!) { nkLog(debug: "Web view did receive server redirect for provisional navigation.") @@ -236,33 +136,15 @@ extension NCLoginProvider: WKNavigationDelegate { return } - // Login via provider. - if currentWebViewURLString.hasPrefix(NCBrandOptions.shared.webLoginAutenticationProtocol) && currentWebViewURLString.contains("login") { + if currentWebViewURLString.hasPrefix(NCBrandOptions.shared.webLoginAutenticationProtocol), let grant = NCProviderLoginHandler.handle(callbackURL: currentWebViewURL) { nkLog(debug: "Web view redirect to provider login URL detected.") - var server: String = "" - var user: String = "" - var password: String = "" - let keyValue = currentWebViewURL.path.components(separatedBy: "&") - - for value in keyValue { - if value.contains("server:") { server = value } - if value.contains("user:") { user = value } - if value.contains("password:") { password = value } + if self.controller == nil { + self.controller = UIApplication.shared.mainAppWindow?.rootViewController as? NCMainTabBarController } - if !server.isEmpty, !user.isEmpty, !password.isEmpty { - let server: String = server.replacingOccurrences(of: "/server:", with: "") - let username: String = user.replacingOccurrences(of: "user:", with: "").replacingOccurrences(of: "+", with: " ") - let password: String = password.replacingOccurrences(of: "password:", with: "") - - if self.controller == nil { - self.controller = UIApplication.shared.mainAppWindow?.rootViewController as? NCMainTabBarController - } - - Task { @MainActor in - await NCAccount().createAccount(viewController: self, urlBase: server, user: username, password: password, controller: controller) - } + Task { @MainActor in + await NCAccount().createAccount(viewController: self, urlBase: grant.urlBase, user: grant.user, password: grant.password, controller: controller) } } }