mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 06:55:59 +00:00
Remove AppAuth and prepare for Rust OIDC. (#870)
This commit is contained in:
@@ -1,14 +1,5 @@
|
|||||||
{
|
{
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
|
||||||
"identity" : "appauth-ios",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/openid/AppAuth-iOS",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "71cde449f13d453227e687458144bde372d30fc7",
|
|
||||||
"version" : "1.6.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"identity" : "compound-design-tokens",
|
"identity" : "compound-design-tokens",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
@@ -181,7 +172,7 @@
|
|||||||
{
|
{
|
||||||
"identity" : "swift-snapshot-testing",
|
"identity" : "swift-snapshot-testing",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/pointfreeco/swift-snapshot-testing.git",
|
"location" : "https://github.com/pointfreeco/swift-snapshot-testing",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "cef5b3f6f11781dd4591bdd1dd0a3d22bd609334",
|
"revision" : "cef5b3f6f11781dd4591bdd1dd0a3d22bd609334",
|
||||||
"version" : "1.11.0"
|
"version" : "1.11.0"
|
||||||
|
|||||||
@@ -159,6 +159,10 @@ class AppCoordinator: AppCoordinatorProtocol {
|
|||||||
self.presentSplashScreen()
|
self.presentSplashScreen()
|
||||||
case (.restoringSession, .createdUserSession, .signedIn):
|
case (.restoringSession, .createdUserSession, .signedIn):
|
||||||
self.setupUserSession()
|
self.setupUserSession()
|
||||||
|
case (.signingOut, .signOut, .signingOut):
|
||||||
|
// We can ignore signOut when already in the process of signing out,
|
||||||
|
// such as the SDK sending an authError due to token invalidation.
|
||||||
|
break
|
||||||
case (_, .signOut(let isSoft), .signingOut):
|
case (_, .signOut(let isSoft), .signingOut):
|
||||||
self.logout(isSoft: isSoft)
|
self.logout(isSoft: isSoft)
|
||||||
case (.signingOut, .completedSigningOut(let isSoft), .signedOut):
|
case (.signingOut, .completedSigningOut(let isSoft), .signedOut):
|
||||||
@@ -336,7 +340,9 @@ class AppCoordinator: AppCoordinatorProtocol {
|
|||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
switch callback {
|
switch callback {
|
||||||
case .didReceiveAuthError(let isSoftLogout):
|
case .didReceiveAuthError(let isSoftLogout):
|
||||||
self.stateMachine.processEvent(.signOut(isSoft: isSoftLogout))
|
stateMachine.processEvent(.signOut(isSoft: isSoftLogout))
|
||||||
|
case .updateRestorationToken:
|
||||||
|
userSessionStore.refreshRestorationToken(for: userSession)
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,17 @@ final class AppSettings {
|
|||||||
/// The URL that is opened when tapping the Learn more button on the sliding sync alert during authentication.
|
/// The URL that is opened when tapping the Learn more button on the sliding sync alert during authentication.
|
||||||
let slidingSyncLearnMoreURL = URL(staticString: "https://github.com/matrix-org/sliding-sync/blob/main/docs/Landing.md")
|
let slidingSyncLearnMoreURL = URL(staticString: "https://github.com/matrix-org/sliding-sync/blob/main/docs/Landing.md")
|
||||||
|
|
||||||
|
/// The redirect URL used for OIDC.
|
||||||
|
let oidcRedirectURL = URL(staticString: "io.element:/callback")
|
||||||
|
/// The app's main URL shown when using OIDC.
|
||||||
|
let oidcClientURL = URL(staticString: "https://element.io")
|
||||||
|
/// The app's Terms of Service URL shown when using OIDC.
|
||||||
|
let oidcTermsURL = URL(staticString: "https://element.io/user-terms-of-service")
|
||||||
|
/// The app's Privacy Policy URL shown when using OIDC.
|
||||||
|
let oidcPolicyURL = URL(staticString: "https://element.io/privacy")
|
||||||
|
/// Any pre-defined static client registrations for OIDC issuers.
|
||||||
|
let oidcStaticRegistrations = [URL(staticString: "https://id.thirdroom.io/realms/thirdroom"): "elementx"]
|
||||||
|
|
||||||
// MARK: - Notifications
|
// MARK: - Notifications
|
||||||
|
|
||||||
var pusherAppId: String {
|
var pusherAppId: String {
|
||||||
|
|||||||
@@ -24,4 +24,9 @@ extension Dictionary {
|
|||||||
}
|
}
|
||||||
return String(data: data, encoding: .utf8)
|
return String(data: data, encoding: .utf8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a dictionary containing the original values keyed by the results of mapping the given closure over its keys.
|
||||||
|
func mapKeys<T>(_ transform: (Key) -> T) -> [T: Value] {
|
||||||
|
.init(map { (transform($0.key), $0.value) }) { first, _ in first }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,8 +64,7 @@ extension LoginHomeserver {
|
|||||||
|
|
||||||
/// A mock homeserver that supports only supports authentication via a single SSO provider.
|
/// A mock homeserver that supports only supports authentication via a single SSO provider.
|
||||||
static var mockOIDC: LoginHomeserver {
|
static var mockOIDC: LoginHomeserver {
|
||||||
let issuerURL = URL(staticString: "https://auth.company.com")
|
LoginHomeserver(address: "company.com", loginMode: .oidc)
|
||||||
return LoginHomeserver(address: "company.com", loginMode: .oidc(issuerURL))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A mock homeserver that only with no supported login flows.
|
/// A mock homeserver that only with no supported login flows.
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ import Foundation
|
|||||||
enum LoginMode: Equatable {
|
enum LoginMode: Equatable {
|
||||||
/// The login mode hasn't been determined yet.
|
/// The login mode hasn't been determined yet.
|
||||||
case unknown
|
case unknown
|
||||||
/// The homeserver supports login via OpenID Connect at the associated URL.
|
/// The homeserver supports login via OpenID Connect.
|
||||||
case oidc(URL)
|
case oidc
|
||||||
/// The homeserver supports login with a password.
|
/// The homeserver supports login with a password.
|
||||||
case password
|
case password
|
||||||
/// The homeserver only allows login with unsupported mechanisms. Use fallback instead.
|
/// The homeserver only allows login with unsupported mechanisms. Use fallback instead.
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppAuth
|
import Combine
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct LoginScreenCoordinatorParameters {
|
struct LoginScreenCoordinatorParameters {
|
||||||
@@ -32,16 +32,10 @@ enum LoginScreenCoordinatorAction {
|
|||||||
final class LoginScreenCoordinator: CoordinatorProtocol {
|
final class LoginScreenCoordinator: CoordinatorProtocol {
|
||||||
private let parameters: LoginScreenCoordinatorParameters
|
private let parameters: LoginScreenCoordinatorParameters
|
||||||
private var viewModel: LoginScreenViewModelProtocol
|
private var viewModel: LoginScreenViewModelProtocol
|
||||||
private let hostingController: UIViewController
|
|
||||||
/// Passed to the OIDC service to provide a view controller from which to present the authentication session.
|
|
||||||
private let oidcUserAgent: OIDExternalUserAgentIOS?
|
|
||||||
|
|
||||||
private var currentTask: Task<Void, Error>? {
|
@CancellableTask private var currentTask: Task<Void, Error>?
|
||||||
willSet {
|
|
||||||
currentTask?.cancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
private let oidcAuthenticationPresenter: OIDCAuthenticationPresenter
|
||||||
private var authenticationService: AuthenticationServiceProxyProtocol { parameters.authenticationService }
|
private var authenticationService: AuthenticationServiceProxyProtocol { parameters.authenticationService }
|
||||||
private var navigationStackCoordinator: NavigationStackCoordinator { parameters.navigationStackCoordinator }
|
private var navigationStackCoordinator: NavigationStackCoordinator { parameters.navigationStackCoordinator }
|
||||||
|
|
||||||
@@ -54,8 +48,7 @@ final class LoginScreenCoordinator: CoordinatorProtocol {
|
|||||||
|
|
||||||
viewModel = LoginScreenViewModel(homeserver: parameters.authenticationService.homeserver)
|
viewModel = LoginScreenViewModel(homeserver: parameters.authenticationService.homeserver)
|
||||||
|
|
||||||
hostingController = UIHostingController(rootView: LoginScreen(context: viewModel.context))
|
oidcAuthenticationPresenter = OIDCAuthenticationPresenter(authenticationService: parameters.authenticationService)
|
||||||
oidcUserAgent = OIDExternalUserAgentIOS(presenting: hostingController)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Public
|
// MARK: - Public
|
||||||
@@ -74,7 +67,7 @@ final class LoginScreenCoordinator: CoordinatorProtocol {
|
|||||||
case .login(let username, let password):
|
case .login(let username, let password):
|
||||||
self.login(username: username, password: password)
|
self.login(username: username, password: password)
|
||||||
case .continueWithOIDC:
|
case .continueWithOIDC:
|
||||||
self.loginWithOIDC()
|
self.continueWithOIDC()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,27 +119,34 @@ final class LoginScreenCoordinator: CoordinatorProtocol {
|
|||||||
viewModel.displayError(.alert(L10n.screenLoginErrorDeactivatedAccount))
|
viewModel.displayError(.alert(L10n.screenLoginErrorDeactivatedAccount))
|
||||||
case .slidingSyncNotAvailable:
|
case .slidingSyncNotAvailable:
|
||||||
viewModel.displayError(.slidingSyncAlert)
|
viewModel.displayError(.slidingSyncAlert)
|
||||||
|
case .oidcError(.notSupported):
|
||||||
|
// Temporary alert hijacking the use of .notSupported, can be removed when OIDC support is in the SDK.
|
||||||
|
viewModel.displayError(.alert(L10n.commonServerNotSupported))
|
||||||
|
case .oidcError(.userCancellation):
|
||||||
|
// No need to show an error, the user cancelled authentication.
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
viewModel.displayError(.alert(L10n.errorUnknown))
|
viewModel.displayError(.alert(L10n.errorUnknown))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loginWithOIDC() {
|
private func continueWithOIDC() {
|
||||||
guard let oidcUserAgent else {
|
|
||||||
handleError(AuthenticationServiceError.oidcError(.notSupported))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
startLoading(isInteractionBlocking: true)
|
startLoading(isInteractionBlocking: true)
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
switch await authenticationService.loginWithOIDC(userAgent: oidcUserAgent) {
|
switch await authenticationService.urlForOIDCLogin() {
|
||||||
case .success(let userSession):
|
|
||||||
callback?(.signedIn(userSession))
|
|
||||||
stopLoading()
|
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
stopLoading()
|
stopLoading()
|
||||||
handleError(error)
|
handleError(error)
|
||||||
|
case .success(let oidcData):
|
||||||
|
stopLoading()
|
||||||
|
|
||||||
|
switch await oidcAuthenticationPresenter.authenticate(using: oidcData) {
|
||||||
|
case .success(let userSession):
|
||||||
|
callback?(.signedIn(userSession))
|
||||||
|
case .failure(let error):
|
||||||
|
handleError(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2023 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 AuthenticationServices
|
||||||
|
|
||||||
|
/// Presents a web authentication session for an OIDC request.
|
||||||
|
class OIDCAuthenticationPresenter {
|
||||||
|
private let authenticationService: AuthenticationServiceProxyProtocol
|
||||||
|
private let oidcPresentationContent = OIDCPresentationContext()
|
||||||
|
|
||||||
|
init(authenticationService: AuthenticationServiceProxyProtocol) {
|
||||||
|
self.authenticationService = authenticationService
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Presents a web authentication session for the supplied data.
|
||||||
|
func authenticate(using oidcData: OIDCAuthenticationDataProxy) async -> Result<UserSessionProtocol, AuthenticationServiceError> {
|
||||||
|
await withCheckedContinuation { continuation in
|
||||||
|
let session = ASWebAuthenticationSession(url: oidcData.url,
|
||||||
|
callbackURLScheme: ServiceLocator.shared.settings.oidcRedirectURL.scheme) { [weak self] url, error in
|
||||||
|
guard let self else { return }
|
||||||
|
|
||||||
|
guard let url else {
|
||||||
|
if let nsError = error as? NSError,
|
||||||
|
nsError.domain == ASWebAuthenticationSessionErrorDomain,
|
||||||
|
nsError.code == ASWebAuthenticationSessionError.canceledLogin.rawValue {
|
||||||
|
continuation.resume(returning: .failure(AuthenticationServiceError.oidcError(.userCancellation)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
continuation.resume(returning: .failure(AuthenticationServiceError.oidcError(.unknown)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
completeAuthentication(callbackURL: url, data: oidcData, continuation: continuation)
|
||||||
|
}
|
||||||
|
|
||||||
|
session.prefersEphemeralWebBrowserSession = false
|
||||||
|
session.presentationContextProvider = oidcPresentationContent
|
||||||
|
session.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func completeAuthentication(callbackURL: URL,
|
||||||
|
data: OIDCAuthenticationDataProxy,
|
||||||
|
continuation: CheckedContinuation<Result<UserSessionProtocol, AuthenticationServiceError>, Never>) {
|
||||||
|
Task {
|
||||||
|
switch await authenticationService.loginWithOIDCCallback(callbackURL, data: data) {
|
||||||
|
case .success(let userSession):
|
||||||
|
continuation.resume(returning: .success(userSession))
|
||||||
|
case .failure(let error):
|
||||||
|
continuation.resume(returning: .failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OIDCPresentationContext: NSObject, ASWebAuthenticationPresentationContextProviding {
|
||||||
|
func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
|
||||||
|
guard let window = UIApplication.shared.connectedScenes.compactMap({ $0 as? UIWindowScene }).first?.keyWindow else {
|
||||||
|
fatalError("Failed to find the main window.")
|
||||||
|
}
|
||||||
|
return window
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
-19
@@ -14,7 +14,6 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppAuth
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct SoftLogoutScreenCoordinatorParameters {
|
struct SoftLogoutScreenCoordinatorParameters {
|
||||||
@@ -43,11 +42,8 @@ enum SoftLogoutScreenCoordinatorResult: CustomStringConvertible {
|
|||||||
final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
|
final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
|
||||||
private let parameters: SoftLogoutScreenCoordinatorParameters
|
private let parameters: SoftLogoutScreenCoordinatorParameters
|
||||||
private var viewModel: SoftLogoutScreenViewModelProtocol
|
private var viewModel: SoftLogoutScreenViewModelProtocol
|
||||||
private let hostingController: UIViewController
|
|
||||||
/// Passed to the OIDC service to provide a view controller from which to present the authentication session.
|
|
||||||
private let oidcUserAgent: OIDExternalUserAgentIOS?
|
|
||||||
|
|
||||||
/// The wizard used to handle the registration flow.
|
private let oidcAuthenticationPresenter: OIDCAuthenticationPresenter
|
||||||
private var authenticationService: AuthenticationServiceProxyProtocol { parameters.authenticationService }
|
private var authenticationService: AuthenticationServiceProxyProtocol { parameters.authenticationService }
|
||||||
|
|
||||||
var callback: (@MainActor (SoftLogoutScreenCoordinatorResult) -> Void)?
|
var callback: (@MainActor (SoftLogoutScreenCoordinatorResult) -> Void)?
|
||||||
@@ -56,13 +52,11 @@ final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
|
|||||||
self.parameters = parameters
|
self.parameters = parameters
|
||||||
|
|
||||||
let homeserver = parameters.authenticationService.homeserver
|
let homeserver = parameters.authenticationService.homeserver
|
||||||
|
|
||||||
viewModel = SoftLogoutScreenViewModel(credentials: parameters.credentials,
|
viewModel = SoftLogoutScreenViewModel(credentials: parameters.credentials,
|
||||||
homeserver: homeserver,
|
homeserver: homeserver,
|
||||||
keyBackupNeeded: parameters.keyBackupNeeded)
|
keyBackupNeeded: parameters.keyBackupNeeded)
|
||||||
|
|
||||||
hostingController = UIHostingController(rootView: SoftLogoutScreen(context: viewModel.context))
|
oidcAuthenticationPresenter = OIDCAuthenticationPresenter(authenticationService: parameters.authenticationService)
|
||||||
oidcUserAgent = OIDExternalUserAgentIOS(presenting: hostingController)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Public
|
// MARK: - Public
|
||||||
@@ -80,7 +74,7 @@ final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
|
|||||||
case .clearAllData:
|
case .clearAllData:
|
||||||
self.callback?(.clearAllData)
|
self.callback?(.clearAllData)
|
||||||
case .continueWithOIDC:
|
case .continueWithOIDC:
|
||||||
self.loginWithOIDC()
|
self.continueWithOIDC()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,22 +130,23 @@ final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loginWithOIDC() {
|
private func continueWithOIDC() {
|
||||||
guard let oidcUserAgent else {
|
|
||||||
handleError(AuthenticationServiceError.oidcError(.notSupported))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
startLoading()
|
startLoading()
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
switch await authenticationService.loginWithOIDC(userAgent: oidcUserAgent) {
|
switch await authenticationService.urlForOIDCLogin() {
|
||||||
case .success(let userSession):
|
|
||||||
callback?(.signedIn(userSession))
|
|
||||||
stopLoading()
|
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
stopLoading()
|
stopLoading()
|
||||||
handleError(error)
|
handleError(error)
|
||||||
|
case .success(let oidcData):
|
||||||
|
stopLoading()
|
||||||
|
|
||||||
|
switch await oidcAuthenticationPresenter.authenticate(using: oidcData) {
|
||||||
|
case .success(let userSession):
|
||||||
|
callback?(.signedIn(userSession))
|
||||||
|
case .failure(let error):
|
||||||
|
handleError(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,6 +158,12 @@ final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
|
|||||||
viewModel.displayError(.alert(L10n.screenLoginErrorInvalidCredentials))
|
viewModel.displayError(.alert(L10n.screenLoginErrorInvalidCredentials))
|
||||||
case .accountDeactivated:
|
case .accountDeactivated:
|
||||||
viewModel.displayError(.alert(L10n.screenLoginErrorDeactivatedAccount))
|
viewModel.displayError(.alert(L10n.screenLoginErrorDeactivatedAccount))
|
||||||
|
case .oidcError(.notSupported):
|
||||||
|
// Temporary alert hijacking the use of .notSupported, can be removed when OIDC support is in the SDK.
|
||||||
|
viewModel.displayError(.alert(L10n.commonServerNotSupported))
|
||||||
|
case .oidcError(.userCancellation):
|
||||||
|
// No need to show an error, the user cancelled authentication.
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
viewModel.displayError(.alert(L10n.errorUnknown))
|
viewModel.displayError(.alert(L10n.errorUnknown))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppAuth
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import MatrixRustSDK
|
import MatrixRustSDK
|
||||||
|
|
||||||
@@ -26,8 +25,18 @@ class AuthenticationServiceProxy: AuthenticationServiceProxyProtocol {
|
|||||||
|
|
||||||
init(userSessionStore: UserSessionStoreProtocol) {
|
init(userSessionStore: UserSessionStoreProtocol) {
|
||||||
self.userSessionStore = userSessionStore
|
self.userSessionStore = userSessionStore
|
||||||
|
|
||||||
|
// guard let settings = ServiceLocator.shared.settings else { fatalError("The settings must be set.") }
|
||||||
|
// let oidcConfiguration = OidcConfiguration(clientName: InfoPlistReader.main.bundleDisplayName,
|
||||||
|
// redirectUri: settings.oidcRedirectURL.absoluteString,
|
||||||
|
// clientUri: settings.oidcClientURL.absoluteString,
|
||||||
|
// tosUri: settings.oidcTermsURL.absoluteString,
|
||||||
|
// policyUri: settings.oidcPolicyURL.absoluteString,
|
||||||
|
// staticRegistrations: settings.oidcStaticRegistrations.mapKeys { $0.absoluteString })
|
||||||
|
|
||||||
authenticationService = AuthenticationService(basePath: userSessionStore.baseDirectory.path,
|
authenticationService = AuthenticationService(basePath: userSessionStore.baseDirectory.path,
|
||||||
passphrase: nil,
|
passphrase: nil,
|
||||||
|
// oidcConfiguration: oidcConfiguration,
|
||||||
customSlidingSyncProxy: ServiceLocator.shared.settings.slidingSyncProxyURL?.absoluteString)
|
customSlidingSyncProxy: ServiceLocator.shared.settings.slidingSyncProxyURL?.absoluteString)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,8 +51,8 @@ class AuthenticationServiceProxy: AuthenticationServiceProxyProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let details = authenticationService.homeserverDetails() {
|
if let details = authenticationService.homeserverDetails() {
|
||||||
if let issuer = details.authenticationIssuer(), let issuerURL = URL(string: issuer) {
|
if details.authenticationIssuer() != nil {
|
||||||
homeserver.loginMode = .oidc(issuerURL)
|
homeserver.loginMode = .oidc
|
||||||
} else if details.supportsPasswordLogin() {
|
} else if details.supportsPasswordLogin() {
|
||||||
homeserver.loginMode = .password
|
homeserver.loginMode = .password
|
||||||
} else {
|
} else {
|
||||||
@@ -62,40 +71,32 @@ class AuthenticationServiceProxy: AuthenticationServiceProxyProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loginWithOIDC(userAgent: OIDExternalUserAgentIOS) async -> Result<UserSessionProtocol, AuthenticationServiceError> {
|
func urlForOIDCLogin() async -> Result<OIDCAuthenticationDataProxy, AuthenticationServiceError> {
|
||||||
guard case let .oidc(issuerURL) = homeserver.loginMode else {
|
.failure(.oidcError(.notSupported))
|
||||||
return .failure(.oidcError(.notSupported))
|
// do {
|
||||||
}
|
// let oidcData = try await Task.dispatch(on: .global()) {
|
||||||
|
// try self.authenticationService.urlForOidcLogin()
|
||||||
|
// }
|
||||||
|
// return .success(OIDCAuthenticationDataProxy(underlyingData: oidcData))
|
||||||
|
// } catch {
|
||||||
|
// MXLog.error("Failed to get URL for OIDC login: \(error)")
|
||||||
|
// return .failure(.oidcError(.urlFailure))
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
let token: String
|
func loginWithOIDCCallback(_ callbackURL: URL, data: OIDCAuthenticationDataProxy) async -> Result<UserSessionProtocol, AuthenticationServiceError> {
|
||||||
let deviceID = generateDeviceID()
|
.failure(.oidcError(.notSupported))
|
||||||
do {
|
// do {
|
||||||
let oidcService = OIDCService(issuerURL: issuerURL)
|
// let client = try await Task.dispatch(on: .global()) {
|
||||||
let configuration = try await oidcService.metadata()
|
// try self.authenticationService.loginWithOidcCallback(authenticationData: data.underlyingData, callbackUrl: callbackURL.absoluteString)
|
||||||
let registationResponse = try await oidcService.registerClient(metadata: configuration)
|
// }
|
||||||
let authResponse = try await oidcService.presentWebAuthentication(metadata: configuration,
|
// return await userSession(for: client)
|
||||||
clientID: registationResponse.clientID,
|
// } catch AuthenticationError.OidcCancelled {
|
||||||
scope: "openid urn:matrix:org.matrix.msc2967.client:api:* urn:matrix:org.matrix.msc2967.client:device:\(deviceID)",
|
// return .failure(.oidcError(.userCancellation))
|
||||||
userAgent: userAgent)
|
// } catch {
|
||||||
let tokenResponse = try await oidcService.redeemCodeForTokens(authResponse: authResponse)
|
// MXLog.error("Login with OIDC failed: \(error)")
|
||||||
|
// return .failure(.failedLoggingIn)
|
||||||
guard let accessToken = tokenResponse.accessToken else { return .failure(.oidcError(.unknown)) }
|
// }
|
||||||
token = accessToken
|
|
||||||
} catch let error as OIDCError {
|
|
||||||
MXLog.error("Login with OIDC failed: \(error)")
|
|
||||||
return .failure(.oidcError(error))
|
|
||||||
} catch {
|
|
||||||
MXLog.error("Login with OIDC failed: \(error)")
|
|
||||||
return .failure(.failedLoggingIn)
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
let client = try authenticationService.restoreWithAccessToken(token: token, deviceId: deviceID)
|
|
||||||
return await userSession(for: client)
|
|
||||||
} catch {
|
|
||||||
MXLog.error("Failed restoring with access token: \(error)")
|
|
||||||
return .failure(.failedLoggingIn)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func login(username: String, password: String, initialDeviceName: String?, deviceId: String?) async -> Result<UserSessionProtocol, AuthenticationServiceError> {
|
func login(username: String, password: String, initialDeviceName: String?, deviceId: String?) async -> Result<UserSessionProtocol, AuthenticationServiceError> {
|
||||||
@@ -133,13 +134,4 @@ class AuthenticationServiceProxy: AuthenticationServiceProxyProtocol {
|
|||||||
return .failure(.failedLoggingIn)
|
return .failure(.failedLoggingIn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func generateDeviceID() -> String {
|
|
||||||
var deviceID = ""
|
|
||||||
for _ in 0..<10 {
|
|
||||||
guard let scalar = UnicodeScalar(Int.random(in: 65...90)) else { fatalError() }
|
|
||||||
deviceID.append(Character(scalar))
|
|
||||||
}
|
|
||||||
return deviceID
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppAuth
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import MatrixRustSDK
|
||||||
|
|
||||||
enum AuthenticationServiceError: Error {
|
enum AuthenticationServiceError: Error {
|
||||||
/// An error occurred during OIDC authentication.
|
/// An error occurred during OIDC authentication.
|
||||||
@@ -34,7 +34,37 @@ protocol AuthenticationServiceProxyProtocol {
|
|||||||
/// Sets up the service for login on the specified homeserver address.
|
/// Sets up the service for login on the specified homeserver address.
|
||||||
func configure(for homeserverAddress: String) async -> Result<Void, AuthenticationServiceError>
|
func configure(for homeserverAddress: String) async -> Result<Void, AuthenticationServiceError>
|
||||||
/// Performs login using OIDC for the current homeserver.
|
/// Performs login using OIDC for the current homeserver.
|
||||||
func loginWithOIDC(userAgent: OIDExternalUserAgentIOS) async -> Result<UserSessionProtocol, AuthenticationServiceError>
|
func urlForOIDCLogin() async -> Result<OIDCAuthenticationDataProxy, AuthenticationServiceError>
|
||||||
|
/// Add docs.
|
||||||
|
func loginWithOIDCCallback(_ callbackURL: URL, data: OIDCAuthenticationDataProxy) async -> Result<UserSessionProtocol, AuthenticationServiceError>
|
||||||
/// Performs a password login using the current homeserver.
|
/// Performs a password login using the current homeserver.
|
||||||
func login(username: String, password: String, initialDeviceName: String?, deviceId: String?) async -> Result<UserSessionProtocol, AuthenticationServiceError>
|
func login(username: String, password: String, initialDeviceName: String?, deviceId: String?) async -> Result<UserSessionProtocol, AuthenticationServiceError>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - OIDC
|
||||||
|
|
||||||
|
enum OIDCError: Error {
|
||||||
|
/// Failed to get the URL that should be presented for login.
|
||||||
|
case urlFailure
|
||||||
|
/// The user cancelled the login.
|
||||||
|
case userCancellation
|
||||||
|
/// OIDC isn't supported on the currently configured server.
|
||||||
|
case notSupported
|
||||||
|
/// An unknown error occurred.
|
||||||
|
case unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OIDCAuthenticationDataProxy: Equatable {
|
||||||
|
// let underlyingData: OidcAuthenticationData
|
||||||
|
//
|
||||||
|
// var url: URL {
|
||||||
|
// URL(string: underlyingData.loginUrl())!
|
||||||
|
// }
|
||||||
|
let url = URL(staticString: "https://theroadtonowhere")
|
||||||
|
}
|
||||||
|
|
||||||
|
// extension OidcAuthenticationData: Equatable {
|
||||||
|
// public static func == (lhs: MatrixRustSDK.OidcAuthenticationData, rhs: MatrixRustSDK.OidcAuthenticationData) -> Bool {
|
||||||
|
// lhs.loginUrl() == rhs.loginUrl()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|||||||
@@ -14,13 +14,13 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppAuth
|
import Foundation
|
||||||
|
import MatrixRustSDK
|
||||||
|
|
||||||
class MockAuthenticationServiceProxy: AuthenticationServiceProxyProtocol {
|
class MockAuthenticationServiceProxy: AuthenticationServiceProxyProtocol {
|
||||||
let validCredentials = (username: "alice", password: "12345678")
|
let validCredentials = (username: "alice", password: "12345678")
|
||||||
|
|
||||||
private(set) var homeserver: LoginHomeserver = .mockMatrixDotOrg
|
private(set) var homeserver: LoginHomeserver = .mockMatrixDotOrg
|
||||||
var oidcUserAgent: OIDExternalUserAgentIOS?
|
|
||||||
|
|
||||||
func configure(for homeserverAddress: String) async -> Result<Void, AuthenticationServiceError> {
|
func configure(for homeserverAddress: String) async -> Result<Void, AuthenticationServiceError> {
|
||||||
// Map the address to the mock homeservers
|
// Map the address to the mock homeservers
|
||||||
@@ -42,7 +42,11 @@ class MockAuthenticationServiceProxy: AuthenticationServiceProxyProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loginWithOIDC(userAgent: OIDExternalUserAgentIOS) async -> Result<UserSessionProtocol, AuthenticationServiceError> {
|
func urlForOIDCLogin() async -> Result<OIDCAuthenticationDataProxy, AuthenticationServiceError> {
|
||||||
|
.failure(.oidcError(.notSupported))
|
||||||
|
}
|
||||||
|
|
||||||
|
func loginWithOIDCCallback(_ callbackURL: URL, data: OIDCAuthenticationDataProxy) async -> Result<UserSessionProtocol, AuthenticationServiceError> {
|
||||||
.failure(.oidcError(.notSupported))
|
.failure(.oidcError(.notSupported))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,145 +0,0 @@
|
|||||||
//
|
|
||||||
// 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 AppAuth
|
|
||||||
|
|
||||||
/// Errors thrown by the OIDC service.
|
|
||||||
enum OIDCError: Error {
|
|
||||||
case notSupported
|
|
||||||
case metadataMissingRegistrationEndpoint
|
|
||||||
case userCancellation
|
|
||||||
case missingTokenExchangeRequest
|
|
||||||
case unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A proof of concept implementation of a service that assists with authentication via OIDC.
|
|
||||||
/// It will be replaced by an implementation in the Rust SDK tracked in the following issue:
|
|
||||||
/// https://github.com/matrix-org/matrix-rust-sdk/issues/859
|
|
||||||
class OIDCService {
|
|
||||||
private let issuerURL: URL
|
|
||||||
private var authState: OIDAuthState
|
|
||||||
|
|
||||||
private var metadata: OIDServiceConfiguration?
|
|
||||||
/// Redirect URI for the request. Must match the `client_uri` in reverse DNS format.
|
|
||||||
private var redirectURI = URL(staticString: "io.element:/callback")
|
|
||||||
|
|
||||||
/// Maintains a strong ref to the authorization session that's in progress.
|
|
||||||
private var session: OIDExternalUserAgentSession?
|
|
||||||
|
|
||||||
init(issuerURL: URL) {
|
|
||||||
self.issuerURL = issuerURL
|
|
||||||
authState = OIDAuthState(authorizationResponse: nil, tokenResponse: nil, registrationResponse: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get OpenID Connect endpoints and ensure that dynamic client registration is configured.
|
|
||||||
func metadata() async throws -> OIDServiceConfiguration {
|
|
||||||
let metadata = try await OIDAuthorizationService.discoverConfiguration(forIssuer: issuerURL)
|
|
||||||
|
|
||||||
guard metadata.registrationEndpoint != nil else {
|
|
||||||
throw OIDCError.metadataMissingRegistrationEndpoint
|
|
||||||
}
|
|
||||||
|
|
||||||
return metadata
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Perform dynamic client registration and then store the response
|
|
||||||
func registerClient(metadata: OIDServiceConfiguration) async throws -> OIDRegistrationResponse {
|
|
||||||
let extraParams = [
|
|
||||||
"client_name": "ElementX iOS",
|
|
||||||
"client_uri": "https://element.io",
|
|
||||||
"tos_uri": "https://element.io/user-terms-of-service",
|
|
||||||
"policy_uri": "https://element.io/privacy"
|
|
||||||
]
|
|
||||||
|
|
||||||
let nonTemplatizedRequest = OIDRegistrationRequest(configuration: metadata,
|
|
||||||
redirectURIs: [redirectURI],
|
|
||||||
responseTypes: nil,
|
|
||||||
grantTypes: [OIDGrantTypeAuthorizationCode, OIDGrantTypeRefreshToken],
|
|
||||||
subjectType: nil,
|
|
||||||
tokenEndpointAuthMethod: "none",
|
|
||||||
additionalParameters: extraParams)
|
|
||||||
|
|
||||||
let registrationResponse = try await OIDAuthorizationService.perform(nonTemplatizedRequest)
|
|
||||||
|
|
||||||
MXLog.info("Registration data retrieved successfully")
|
|
||||||
MXLog.debug("Created dynamic client: ID: \(registrationResponse.clientID)")
|
|
||||||
|
|
||||||
// This is a PoC, a complete implementation would persist the client ID and secret for reuse.
|
|
||||||
|
|
||||||
return registrationResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Trigger a redirect with standard parameters.
|
|
||||||
/// `acr_values` can be sent as an extra parameter, to control authentication methods.
|
|
||||||
func presentWebAuthentication(metadata: OIDServiceConfiguration,
|
|
||||||
clientID: String,
|
|
||||||
scope: String,
|
|
||||||
userAgent: OIDExternalUserAgent) async throws -> OIDAuthorizationResponse {
|
|
||||||
let scopesArray = scope.components(separatedBy: " ")
|
|
||||||
let request = OIDAuthorizationRequest(configuration: metadata,
|
|
||||||
clientId: clientID,
|
|
||||||
clientSecret: nil,
|
|
||||||
scopes: scopesArray,
|
|
||||||
redirectURL: redirectURI,
|
|
||||||
responseType: OIDResponseTypeCode,
|
|
||||||
additionalParameters: nil)
|
|
||||||
let result: OIDAuthorizationResponse = try await withCheckedThrowingContinuation { continuation in
|
|
||||||
self.session = OIDAuthorizationService.present(request, externalUserAgent: userAgent) { response, error in
|
|
||||||
guard let response else {
|
|
||||||
if let error {
|
|
||||||
MXLog.info("User cancelled the ASWebAuthenticationSession window")
|
|
||||||
continuation.resume(with: .failure(self.isUserCancellationError(error) ? OIDCError.userCancellation : error))
|
|
||||||
} else {
|
|
||||||
continuation.resume(with: .failure(OIDCError.unknown))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
MXLog.info("Authorization response received successfully")
|
|
||||||
continuation.resume(with: .success(response))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handle the authorization response, including the user closing the Chrome Custom Tab
|
|
||||||
func redeemCodeForTokens(authResponse: OIDAuthorizationResponse) async throws -> OIDTokenResponse {
|
|
||||||
guard let request = authResponse.tokenExchangeRequest() else { throw OIDCError.missingTokenExchangeRequest }
|
|
||||||
return try await OIDAuthorizationService.perform(request, originalAuthorizationResponse: authResponse)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// We can check for specific error codes to handle the user cancelling the ASWebAuthenticationSession window.
|
|
||||||
private func isUserCancellationError(_ error: Error) -> Bool {
|
|
||||||
let error = error as NSError
|
|
||||||
return error.domain == OIDGeneralErrorDomain && error.code == OIDErrorCode.userCanceledAuthorizationFlow.rawValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension OIDAuthorizationService {
|
|
||||||
/// An async version of `perform(_:originalAuthorizationResponse:callback:)`.
|
|
||||||
class func perform(_ request: OIDTokenRequest,
|
|
||||||
originalAuthorizationResponse authorizationResponse: OIDAuthorizationResponse?) async throws -> OIDTokenResponse {
|
|
||||||
try await withCheckedThrowingContinuation { continuation in
|
|
||||||
perform(request, originalAuthorizationResponse: authorizationResponse) { response, error in
|
|
||||||
guard let response else {
|
|
||||||
continuation.resume(with: .failure(error ?? OIDCError.unknown))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
continuation.resume(with: .success(response))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -28,13 +28,16 @@ private class WeakClientProxyWrapper: ClientDelegate, NotificationDelegate, Slid
|
|||||||
|
|
||||||
// MARK: - ClientDelegate
|
// MARK: - ClientDelegate
|
||||||
|
|
||||||
func didReceiveSyncUpdate() { }
|
|
||||||
|
|
||||||
func didReceiveAuthError(isSoftLogout: Bool) {
|
func didReceiveAuthError(isSoftLogout: Bool) {
|
||||||
MXLog.error("Received authentication error, softlogout=\(isSoftLogout)")
|
MXLog.error("Received authentication error, softlogout=\(isSoftLogout)")
|
||||||
clientProxy?.didReceiveAuthError(isSoftLogout: isSoftLogout)
|
clientProxy?.didReceiveAuthError(isSoftLogout: isSoftLogout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func didRefreshTokens() {
|
||||||
|
MXLog.info("The session has updated tokens.")
|
||||||
|
clientProxy?.updateRestorationToken()
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - SlidingSyncDelegate
|
// MARK: - SlidingSyncDelegate
|
||||||
|
|
||||||
func didReceiveSyncUpdate(summary: UpdateSummary) {
|
func didReceiveSyncUpdate(summary: UpdateSummary) {
|
||||||
@@ -554,6 +557,10 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fileprivate func updateRestorationToken() {
|
||||||
|
callbacks.send(.updateRestorationToken)
|
||||||
|
}
|
||||||
|
|
||||||
fileprivate func didReceiveAuthError(isSoftLogout: Bool) {
|
fileprivate func didReceiveAuthError(isSoftLogout: Bool) {
|
||||||
callbacks.send(.receivedAuthError(isSoftLogout: isSoftLogout))
|
callbacks.send(.receivedAuthError(isSoftLogout: isSoftLogout))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ enum ClientProxyCallback {
|
|||||||
case receivedSyncUpdate
|
case receivedSyncUpdate
|
||||||
case receivedAuthError(isSoftLogout: Bool)
|
case receivedAuthError(isSoftLogout: Bool)
|
||||||
case receivedNotification(NotificationItemProxyProtocol)
|
case receivedNotification(NotificationItemProxyProtocol)
|
||||||
|
case updateRestorationToken
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ClientProxyError: Error {
|
enum ClientProxyError: Error {
|
||||||
|
|||||||
@@ -89,9 +89,15 @@ class UserSession: UserSessionProtocol {
|
|||||||
authErrorCancellable = clientProxy.callbacks
|
authErrorCancellable = clientProxy.callbacks
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] callback in
|
.sink { [weak self] callback in
|
||||||
if case .receivedAuthError(let isSoftLogout) = callback {
|
guard let self else { return }
|
||||||
self?.callbacks.send(.didReceiveAuthError(isSoftLogout: isSoftLogout))
|
switch callback {
|
||||||
self?.tearDownAuthErrorWatchdog()
|
case .receivedAuthError(let isSoftLogout):
|
||||||
|
callbacks.send(.didReceiveAuthError(isSoftLogout: isSoftLogout))
|
||||||
|
tearDownAuthErrorWatchdog()
|
||||||
|
case .updateRestorationToken:
|
||||||
|
callbacks.send(.updateRestorationToken)
|
||||||
|
default:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ enum UserSessionCallback {
|
|||||||
case sessionVerificationNeeded
|
case sessionVerificationNeeded
|
||||||
case didVerifySession
|
case didVerifySession
|
||||||
case didReceiveAuthError(isSoftLogout: Bool)
|
case didReceiveAuthError(isSoftLogout: Bool)
|
||||||
|
case updateRestorationToken
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol UserSessionProtocol {
|
protocol UserSessionProtocol {
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ extension MatrixRustSDK.Session: Codable {
|
|||||||
deviceId: container.decode(String.self, forKey: .deviceId),
|
deviceId: container.decode(String.self, forKey: .deviceId),
|
||||||
homeserverUrl: container.decode(String.self, forKey: .homeserverUrl),
|
homeserverUrl: container.decode(String.self, forKey: .homeserverUrl),
|
||||||
slidingSyncProxy: container.decode(String.self, forKey: .slidingSyncProxy))
|
slidingSyncProxy: container.decode(String.self, forKey: .slidingSyncProxy))
|
||||||
|
// oidcData: container.decodeIfPresent(String.self, forKey: .oidcData)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
public func encode(to encoder: Encoder) throws {
|
||||||
@@ -53,9 +54,11 @@ extension MatrixRustSDK.Session: Codable {
|
|||||||
try container.encode(deviceId, forKey: .deviceId)
|
try container.encode(deviceId, forKey: .deviceId)
|
||||||
try container.encode(homeserverUrl, forKey: .homeserverUrl)
|
try container.encode(homeserverUrl, forKey: .homeserverUrl)
|
||||||
try container.encode(slidingSyncProxy, forKey: .slidingSyncProxy)
|
try container.encode(slidingSyncProxy, forKey: .slidingSyncProxy)
|
||||||
|
// try container.encode(oidcData, forKey: .oidcData)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case accessToken, refreshToken, userId, deviceId, homeserverUrl, slidingSyncProxy
|
case accessToken, refreshToken, userId, deviceId, homeserverUrl, slidingSyncProxy
|
||||||
|
// case oidcData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,7 +132,6 @@ targets:
|
|||||||
- package: Compound
|
- package: Compound
|
||||||
- package: Algorithms
|
- package: Algorithms
|
||||||
- package: AnalyticsEvents
|
- package: AnalyticsEvents
|
||||||
- package: AppAuth
|
|
||||||
- package: Collections
|
- package: Collections
|
||||||
- package: DeviceKit
|
- package: DeviceKit
|
||||||
- package: DTCoreText
|
- package: DTCoreText
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ targets:
|
|||||||
- target: ElementX
|
- target: ElementX
|
||||||
- package: MatrixRustSDK
|
- package: MatrixRustSDK
|
||||||
- package: AnalyticsEvents
|
- package: AnalyticsEvents
|
||||||
- package: AppAuth
|
|
||||||
- package: DeviceKit
|
- package: DeviceKit
|
||||||
- package: DTCoreText
|
- package: DTCoreText
|
||||||
- package: KeychainAccess
|
- package: KeychainAccess
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
Remove AppAuth library and prepare for Rust OIDC.
|
||||||
@@ -56,9 +56,6 @@ packages:
|
|||||||
AnalyticsEvents:
|
AnalyticsEvents:
|
||||||
url: https://github.com/matrix-org/matrix-analytics-events
|
url: https://github.com/matrix-org/matrix-analytics-events
|
||||||
minorVersion: 0.5.0
|
minorVersion: 0.5.0
|
||||||
AppAuth:
|
|
||||||
url: https://github.com/openid/AppAuth-iOS
|
|
||||||
minorVersion: 1.6.0
|
|
||||||
Collections:
|
Collections:
|
||||||
url: https://github.com/apple/swift-collections
|
url: https://github.com/apple/swift-collections
|
||||||
minorVersion: 1.0.0
|
minorVersion: 1.0.0
|
||||||
|
|||||||
Reference in New Issue
Block a user