Enable OIDC support (#1541)

Notification content is bypassed for now.
This commit is contained in:
Doug 2023-08-22 15:53:27 +01:00 committed by GitHub
parent d633b87609
commit 5e3a32157a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 239 additions and 70 deletions

View File

@ -231,6 +231,7 @@
518C93DC6516D3D018DE065F /* UNNotificationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49E751D7EDB6043238111D90 /* UNNotificationRequest.swift */; }; 518C93DC6516D3D018DE065F /* UNNotificationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49E751D7EDB6043238111D90 /* UNNotificationRequest.swift */; };
51C240F4660F7269203A9B3A /* MigrationScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75910F5A36EA8FF9BAD08D18 /* MigrationScreenUITests.swift */; }; 51C240F4660F7269203A9B3A /* MigrationScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75910F5A36EA8FF9BAD08D18 /* MigrationScreenUITests.swift */; };
520EEDAFBC778AB0B41F2F53 /* ClientMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADE6170EFE6A161B0A68AB61 /* ClientMock.swift */; }; 520EEDAFBC778AB0B41F2F53 /* ClientMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADE6170EFE6A161B0A68AB61 /* ClientMock.swift */; };
523C6800ED85D5810CF18C19 /* OIDCAccountSettingsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1D737F4672021D0A7D218CD /* OIDCAccountSettingsPresenter.swift */; };
5375902175B2FEA2949D7D74 /* LoginScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDDDDD9FE1A699D23A5E096 /* LoginScreen.swift */; }; 5375902175B2FEA2949D7D74 /* LoginScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDDDDD9FE1A699D23A5E096 /* LoginScreen.swift */; };
53A55748D5F587C9061F98BF /* ServerConfigurationScreenViewStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 277C20CDD5B64510401B6D0D /* ServerConfigurationScreenViewStateTests.swift */; }; 53A55748D5F587C9061F98BF /* ServerConfigurationScreenViewStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 277C20CDD5B64510401B6D0D /* ServerConfigurationScreenViewStateTests.swift */; };
53A59720F4729D9BBFFB7CAB /* NotificationSettingsEditScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD9CB3B9DFA353AB2B7CD9F8 /* NotificationSettingsEditScreenCoordinator.swift */; }; 53A59720F4729D9BBFFB7CAB /* NotificationSettingsEditScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD9CB3B9DFA353AB2B7CD9F8 /* NotificationSettingsEditScreenCoordinator.swift */; };
@ -1372,6 +1373,7 @@
C14D83B2B7CD5501A0089EFC /* LayoutDirection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutDirection.swift; sourceTree = "<group>"; }; C14D83B2B7CD5501A0089EFC /* LayoutDirection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutDirection.swift; sourceTree = "<group>"; };
C1511766C534367700C8DD75 /* RoomNotificationModeProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationModeProxy.swift; sourceTree = "<group>"; }; C1511766C534367700C8DD75 /* RoomNotificationModeProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationModeProxy.swift; sourceTree = "<group>"; };
C15E0017717EAE3A1D02D005 /* StaticLocationScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticLocationScreenCoordinator.swift; sourceTree = "<group>"; }; C15E0017717EAE3A1D02D005 /* StaticLocationScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticLocationScreenCoordinator.swift; sourceTree = "<group>"; };
C1D737F4672021D0A7D218CD /* OIDCAccountSettingsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OIDCAccountSettingsPresenter.swift; sourceTree = "<group>"; };
C23B3FAD8B23C421BC0D1B1E /* MapTilerGeoCodingServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerGeoCodingServiceProtocol.swift; sourceTree = "<group>"; }; C23B3FAD8B23C421BC0D1B1E /* MapTilerGeoCodingServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerGeoCodingServiceProtocol.swift; sourceTree = "<group>"; };
C2886615BEBAE33A0AA4D5F8 /* RoomScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenModels.swift; sourceTree = "<group>"; }; C2886615BEBAE33A0AA4D5F8 /* RoomScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenModels.swift; sourceTree = "<group>"; };
C2E9B841EE4878283ECDB554 /* InviteUsersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteUsersScreen.swift; sourceTree = "<group>"; }; C2E9B841EE4878283ECDB554 /* InviteUsersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteUsersScreen.swift; sourceTree = "<group>"; };
@ -2499,6 +2501,7 @@
70B74A432C241E56A7ACE610 /* Settings */ = { 70B74A432C241E56A7ACE610 /* Settings */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
EB5B1119B5AD79297F1D49EB /* AccountSettings */,
09C599CB430ABF160C1EE55C /* AnalyticsSettingsScreen */, 09C599CB430ABF160C1EE55C /* AnalyticsSettingsScreen */,
1CA6CD0DE6F0445156361B6D /* DeveloperOptionsScreen */, 1CA6CD0DE6F0445156361B6D /* DeveloperOptionsScreen */,
38A1C74493B816B8753F5BC2 /* LegalInformationScreen */, 38A1C74493B816B8753F5BC2 /* LegalInformationScreen */,
@ -3691,6 +3694,14 @@
path = View; path = View;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
EB5B1119B5AD79297F1D49EB /* AccountSettings */ = {
isa = PBXGroup;
children = (
C1D737F4672021D0A7D218CD /* OIDCAccountSettingsPresenter.swift */,
);
path = AccountSettings;
sourceTree = "<group>";
};
EBBEB5471737E9D116DF4738 /* Background */ = { EBBEB5471737E9D116DF4738 /* Background */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -4594,6 +4605,7 @@
D12F440F7973F1489F61389D /* NotificationSettingsScreenModels.swift in Sources */, D12F440F7973F1489F61389D /* NotificationSettingsScreenModels.swift in Sources */,
7F7EA51A9A43125A8CB6AC90 /* NotificationSettingsScreenViewModel.swift in Sources */, 7F7EA51A9A43125A8CB6AC90 /* NotificationSettingsScreenViewModel.swift in Sources */,
CBD2ABE4C1A47ECD99E1488E /* NotificationSettingsScreenViewModelProtocol.swift in Sources */, CBD2ABE4C1A47ECD99E1488E /* NotificationSettingsScreenViewModelProtocol.swift in Sources */,
523C6800ED85D5810CF18C19 /* OIDCAccountSettingsPresenter.swift in Sources */,
9A4E3D5AA44B041DAC3A0D81 /* OIDCAuthenticationPresenter.swift in Sources */, 9A4E3D5AA44B041DAC3A0D81 /* OIDCAuthenticationPresenter.swift in Sources */,
329571083B132E4941131835 /* OnboardingBackgroundImage.swift in Sources */, 329571083B132E4941131835 /* OnboardingBackgroundImage.swift in Sources */,
2CB6787E25B11711518E9588 /* OnboardingCoordinator.swift in Sources */, 2CB6787E25B11711518E9588 /* OnboardingCoordinator.swift in Sources */,
@ -5507,7 +5519,7 @@
repositoryURL = "https://github.com/matrix-org/matrix-rust-components-swift"; repositoryURL = "https://github.com/matrix-org/matrix-rust-components-swift";
requirement = { requirement = {
kind = exactVersion; kind = exactVersion;
version = "1.0.110-alpha"; version = "1.0.112-alpha";
}; };
}; };
821C67C9A7F8CC3FD41B28B4 /* XCRemoteSwiftPackageReference "emojibase-bindings" */ = { 821C67C9A7F8CC3FD41B28B4 /* XCRemoteSwiftPackageReference "emojibase-bindings" */ = {

View File

@ -129,8 +129,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/matrix-org/matrix-rust-components-swift", "location" : "https://github.com/matrix-org/matrix-rust-components-swift",
"state" : { "state" : {
"revision" : "d1a47b07b52012cd2bdad910488c0249003e05ce", "revision" : "32631740a23b8e7a23dcbc919bffc16ba3c3d270",
"version" : "1.0.110-alpha" "version" : "1.0.112-alpha"
} }
}, },
{ {

View File

@ -372,6 +372,7 @@
"screen_session_verification_they_match" = "They match"; "screen_session_verification_they_match" = "They match";
"screen_session_verification_waiting_to_accept_subtitle" = "Accept the request to start the verification process in your other session to continue."; "screen_session_verification_waiting_to_accept_subtitle" = "Accept the request to start the verification process in your other session to continue.";
"screen_session_verification_waiting_to_accept_title" = "Waiting to accept request"; "screen_session_verification_waiting_to_accept_title" = "Waiting to accept request";
"screen_settings_oidc_account" = "Account and devices";
"screen_share_location_title" = "Share location"; "screen_share_location_title" = "Share location";
"screen_share_my_location_action" = "Share my location"; "screen_share_my_location_action" = "Share my location";
"screen_share_open_apple_maps" = "Open in Apple Maps"; "screen_share_open_apple_maps" = "Open in Apple Maps";

View File

@ -543,11 +543,9 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
} }
private func startSync() { private func startSync() {
ServiceLocator.shared.analytics.signpost.beginSync() guard let userSession else { return }
guard let userSession else {
fatalError("User session not setup")
}
ServiceLocator.shared.analytics.signpost.beginSync()
userSession.clientProxy.startSync() userSession.clientProxy.startSync()
let identifier = "StaleDataIndicator" let identifier = "StaleDataIndicator"

View File

@ -938,6 +938,8 @@ public enum L10n {
public static var screenSessionVerificationWaitingToAcceptSubtitle: String { return L10n.tr("Localizable", "screen_session_verification_waiting_to_accept_subtitle") } public static var screenSessionVerificationWaitingToAcceptSubtitle: String { return L10n.tr("Localizable", "screen_session_verification_waiting_to_accept_subtitle") }
/// Waiting to accept request /// Waiting to accept request
public static var screenSessionVerificationWaitingToAcceptTitle: String { return L10n.tr("Localizable", "screen_session_verification_waiting_to_accept_title") } public static var screenSessionVerificationWaitingToAcceptTitle: String { return L10n.tr("Localizable", "screen_session_verification_waiting_to_accept_title") }
/// Account and devices
public static var screenSettingsOidcAccount: String { return L10n.tr("Localizable", "screen_settings_oidc_account") }
/// Share location /// Share location
public static var screenShareLocationTitle: String { return L10n.tr("Localizable", "screen_share_location_title") } public static var screenShareLocationTitle: String { return L10n.tr("Localizable", "screen_share_location_title") }
/// Share my location /// Share my location

View File

@ -31,6 +31,23 @@ class SDKClientMock: SDKClientProtocol {
return accountDataEventTypeReturnValue return accountDataEventTypeReturnValue
} }
} }
//MARK: - `accountUrl`
public var accountUrlCallsCount = 0
public var accountUrlCalled: Bool {
return accountUrlCallsCount > 0
}
public var accountUrlReturnValue: String?
public var accountUrlClosure: (() -> String?)?
public func `accountUrl`() -> String? {
accountUrlCallsCount += 1
if let accountUrlClosure = accountUrlClosure {
return accountUrlClosure()
} else {
return accountUrlReturnValue
}
}
//MARK: - `avatarUrl` //MARK: - `avatarUrl`
public var avatarUrlThrowableError: Error? public var avatarUrlThrowableError: Error?
@ -367,14 +384,19 @@ class SDKClientMock: SDKClientProtocol {
public var logoutCalled: Bool { public var logoutCalled: Bool {
return logoutCallsCount > 0 return logoutCallsCount > 0
} }
public var logoutClosure: (() throws -> Void)? public var logoutReturnValue: String?
public var logoutClosure: (() throws -> String?)?
public func `logout`() throws { public func `logout`() throws -> String? {
if let error = logoutThrowableError { if let error = logoutThrowableError {
throw error throw error
} }
logoutCallsCount += 1 logoutCallsCount += 1
try logoutClosure?() if let logoutClosure = logoutClosure {
return try logoutClosure()
} else {
return logoutReturnValue
}
} }
//MARK: - `notificationClient` //MARK: - `notificationClient`

View File

@ -100,7 +100,16 @@ class AuthenticationCoordinator: CoordinatorProtocol {
if isModallyPresented { if isModallyPresented {
navigationStackCoordinator.setSheetCoordinator(nil) navigationStackCoordinator.setSheetCoordinator(nil)
} else { } else {
// We are here because the default server failed to respond.
if authenticationService.homeserver.value.loginMode == .password {
// Add the password login screen directly to the flow, its fine.
showLoginScreen() showLoginScreen()
} else {
// OIDC is presented from the confirmation screen so replace the
// server selection screen which was inserted to handle the failure.
navigationStackCoordinator.pop()
showServerConfirmationScreen()
}
} }
case .dismiss: case .dismiss:
navigationStackCoordinator.setSheetCoordinator(nil) navigationStackCoordinator.setSheetCoordinator(nil)

View File

@ -0,0 +1,51 @@
//
// 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 that will display the user's account settings page.
///
/// A web authentication session is used so that the same session used for login is available
/// meaning that the user doesn't need to sign in again. `SFSafariViewController` doesn't
/// have access to this session, and for some reason `prefersEphemeralWebBrowserSession`
/// isn't sharing the session back to Safari.
@MainActor
class OIDCAccountSettingsPresenter: NSObject {
private let accountURL: URL
private let presentationAnchor: UIWindow
private let oidcRedirectURL: URL
init(accountURL: URL, presentationAnchor: UIWindow) {
self.accountURL = accountURL
self.presentationAnchor = presentationAnchor
oidcRedirectURL = ServiceLocator.shared.settings.oidcRedirectURL
super.init()
}
/// Presents a web authentication session for the supplied data.
func start() {
let session = ASWebAuthenticationSession(url: accountURL, callbackURLScheme: oidcRedirectURL.scheme) { _, _ in }
session.prefersEphemeralWebBrowserSession = false
session.presentationContextProvider = self
session.start()
}
}
// MARK: ASWebAuthenticationPresentationContextProviding
extension OIDCAccountSettingsPresenter: ASWebAuthenticationPresentationContextProviding {
func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { presentationAnchor }
}

View File

@ -48,6 +48,8 @@ final class SettingsScreenCoordinator: CoordinatorProtocol {
switch action { switch action {
case .close: case .close:
self.callback?(.dismiss) self.callback?(.dismiss)
case .account:
self.presentAccountSettings()
case .analytics: case .analytics:
self.presentAnalyticsScreen() self.presentAnalyticsScreen()
case .reportBug: case .reportBug:
@ -74,6 +76,26 @@ final class SettingsScreenCoordinator: CoordinatorProtocol {
// MARK: - Private // MARK: - Private
private var accountSettingsPresenter: OIDCAccountSettingsPresenter?
private func presentAccountSettings() {
guard let accountURL = viewModel.context.viewState.accountURL else {
MXLog.error("Account URL is missing.")
return
}
guard let window = viewModel.context.viewState.window else {
MXLog.error("The window is missing.")
return
}
// Safari never works in the simulator, use a Web Authentication Session instead.
accountSettingsPresenter = OIDCAccountSettingsPresenter(accountURL: accountURL, presentationAnchor: window)
accountSettingsPresenter?.start()
// Safari isn't working with the shared browser session 😕
// UIApplication.shared.open(accountURL)
}
private func presentAnalyticsScreen() { private func presentAnalyticsScreen() {
let coordinator = AnalyticsSettingsScreenCoordinator(parameters: .init(appSettings: ServiceLocator.shared.settings, let coordinator = AnalyticsSettingsScreenCoordinator(parameters: .init(appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics)) analytics: ServiceLocator.shared.analytics))

View File

@ -19,6 +19,7 @@ import UIKit
enum SettingsScreenViewModelAction { enum SettingsScreenViewModelAction {
case close case close
case account
case analytics case analytics
case reportBug case reportBug
case about case about
@ -32,11 +33,15 @@ struct SettingsScreenViewState: BindableState {
var bindings: SettingsScreenViewStateBindings var bindings: SettingsScreenViewStateBindings
var deviceID: String? var deviceID: String?
var userID: String var userID: String
var accountURL: URL?
var userAvatarURL: URL? var userAvatarURL: URL?
var userDisplayName: String? var userDisplayName: String?
var showSessionVerificationSection: Bool var showSessionVerificationSection: Bool
var showNotificationSettings: Bool var showNotificationSettings: Bool
var showDeveloperOptions: Bool var showDeveloperOptions: Bool
/// The presentation anchor used to display the OIDC account URL.
var window: UIWindow?
} }
struct SettingsScreenViewStateBindings { struct SettingsScreenViewStateBindings {
@ -45,6 +50,7 @@ struct SettingsScreenViewStateBindings {
enum SettingsScreenViewAction { enum SettingsScreenViewAction {
case close case close
case account
case analytics case analytics
case reportBug case reportBug
case about case about
@ -53,4 +59,7 @@ enum SettingsScreenViewAction {
case changedTimelineStyle case changedTimelineStyle
case developerOptions case developerOptions
case notifications case notifications
/// Updates the window used for the OIDC account URL anchor.
case updateWindow(UIWindow)
} }

View File

@ -39,6 +39,7 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo
super.init(initialViewState: .init(bindings: bindings, super.init(initialViewState: .init(bindings: bindings,
deviceID: userSession.deviceID, deviceID: userSession.deviceID,
userID: userSession.userID, userID: userSession.userID,
accountURL: userSession.clientProxy.accountURL,
showSessionVerificationSection: showSessionVerificationSection, showSessionVerificationSection: showSessionVerificationSection,
showNotificationSettings: appSettings.notificationSettingsEnabled, showNotificationSettings: appSettings.notificationSettingsEnabled,
showDeveloperOptions: appSettings.canShowDeveloperOptions), showDeveloperOptions: appSettings.canShowDeveloperOptions),
@ -85,6 +86,8 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo
switch viewAction { switch viewAction {
case .close: case .close:
callback?(.close) callback?(.close)
case .account:
callback?(.account)
case .analytics: case .analytics:
callback?(.analytics) callback?(.analytics)
case .reportBug: case .reportBug:
@ -101,6 +104,12 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo
callback?(.notifications) callback?(.notifications)
case .developerOptions: case .developerOptions:
callback?(.developerOptions) callback?(.developerOptions)
case .updateWindow(let window):
Task {
guard state.window != window else { return }
state.window = window
}
} }
} }
} }

View File

@ -46,6 +46,9 @@ struct SettingsScreen: View {
doneButton doneButton
} }
} }
.introspect(.window, on: .iOS(.v16)) { window in
context.send(viewAction: .updateWindow(window))
}
} }
private var versionText: Text { private var versionText: Text {
@ -100,6 +103,17 @@ struct SettingsScreen: View {
private var simplifiedSection: some View { private var simplifiedSection: some View {
Section { Section {
// Account
if context.viewState.accountURL != nil {
ListRow(label: .default(title: L10n.screenSettingsOidcAccount,
systemIcon: .person),
kind: .button {
context.send(viewAction: .account)
})
.accessibilityIdentifier("notificationsButton")
}
// Message layout
ListRow(label: .default(title: L10n.commonMessageLayout, ListRow(label: .default(title: L10n.commonMessageLayout,
systemIcon: .rectangleGrid1x2), systemIcon: .rectangleGrid1x2),
kind: .picker(selection: $context.timelineStyle, kind: .picker(selection: $context.timelineStyle,
@ -207,7 +221,8 @@ struct SettingsScreen_Previews: PreviewProvider {
verificationController.isVerified = false verificationController.isVerified = false
let userSession = MockUserSession(sessionVerificationController: verificationController, let userSession = MockUserSession(sessionVerificationController: verificationController,
clientProxy: MockClientProxy(userID: "@userid:example.com", clientProxy: MockClientProxy(userID: "@userid:example.com",
deviceID: "AAAAAAAAAAA"), deviceID: "AAAAAAAAAAA",
accountURL: "https://matrix.org/account"),
mediaProvider: MockMediaProvider()) mediaProvider: MockMediaProvider())
ServiceLocator.shared.settings.notificationSettingsEnabled = true ServiceLocator.shared.settings.notificationSettingsEnabled = true
return SettingsScreenViewModel(userSession: userSession, return SettingsScreenViewModel(userSession: userSession,

View File

@ -31,17 +31,17 @@ class AuthenticationServiceProxy: AuthenticationServiceProxyProtocol {
homeserverSubject = .init(LoginHomeserver(address: appSettings.defaultHomeserverAddress, homeserverSubject = .init(LoginHomeserver(address: appSettings.defaultHomeserverAddress,
loginMode: .unknown)) loginMode: .unknown))
// let oidcConfiguration = OidcConfiguration(clientName: InfoPlistReader.main.bundleDisplayName, let oidcConfiguration = OidcConfiguration(clientName: InfoPlistReader.main.bundleDisplayName,
// redirectUri: settings.oidcRedirectURL.absoluteString, redirectUri: appSettings.oidcRedirectURL.absoluteString,
// clientUri: appSettings.oidcClientURL.absoluteString, clientUri: appSettings.oidcClientURL.absoluteString,
// tosUri: appSettings.oidcTermsURL.absoluteString, tosUri: appSettings.oidcTermsURL.absoluteString,
// policyUri: appSettings.oidcPolicyURL.absoluteString, policyUri: appSettings.oidcPolicyURL.absoluteString,
// staticRegistrations: appSettings.oidcStaticRegistrations.mapKeys { $0.absoluteString }) staticRegistrations: appSettings.oidcStaticRegistrations.mapKeys { $0.absoluteString })
authenticationService = AuthenticationService(basePath: userSessionStore.baseDirectory.path, authenticationService = AuthenticationService(basePath: userSessionStore.baseDirectory.path,
passphrase: nil, passphrase: nil,
userAgent: UserAgentBuilder.makeASCIIUserAgent(), userAgent: UserAgentBuilder.makeASCIIUserAgent(),
// oidcConfiguration: oidcConfiguration, oidcConfiguration: oidcConfiguration,
customSlidingSyncProxy: appSettings.slidingSyncProxyURL?.absoluteString) customSlidingSyncProxy: appSettings.slidingSyncProxyURL?.absoluteString)
} }
@ -56,7 +56,7 @@ class AuthenticationServiceProxy: AuthenticationServiceProxyProtocol {
} }
if let details = authenticationService.homeserverDetails() { if let details = authenticationService.homeserverDetails() {
if details.authenticationIssuer() != nil { if details.supportsOidcLogin() {
homeserver.loginMode = .oidc homeserver.loginMode = .oidc
} else if details.supportsPasswordLogin() { } else if details.supportsPasswordLogin() {
homeserver.loginMode = .password homeserver.loginMode = .password
@ -77,31 +77,29 @@ class AuthenticationServiceProxy: AuthenticationServiceProxyProtocol {
} }
func urlForOIDCLogin() async -> Result<OIDCAuthenticationDataProxy, AuthenticationServiceError> { func urlForOIDCLogin() async -> Result<OIDCAuthenticationDataProxy, AuthenticationServiceError> {
.failure(.oidcError(.notSupported)) do {
// do { let oidcData = try await Task.dispatch(on: .global()) {
// let oidcData = try await Task.dispatch(on: .global()) { try self.authenticationService.urlForOidcLogin()
// try self.authenticationService.urlForOidcLogin() }
// } return .success(OIDCAuthenticationDataProxy(underlyingData: oidcData))
// return .success(OIDCAuthenticationDataProxy(underlyingData: oidcData)) } catch {
// } catch { MXLog.error("Failed to get URL for OIDC login: \(error)")
// MXLog.error("Failed to get URL for OIDC login: \(error)") return .failure(.oidcError(.urlFailure))
// return .failure(.oidcError(.urlFailure)) }
// }
} }
func loginWithOIDCCallback(_ callbackURL: URL, data: OIDCAuthenticationDataProxy) async -> Result<UserSessionProtocol, AuthenticationServiceError> { func loginWithOIDCCallback(_ callbackURL: URL, data: OIDCAuthenticationDataProxy) async -> Result<UserSessionProtocol, AuthenticationServiceError> {
.failure(.oidcError(.notSupported)) do {
// do { let client = try await Task.dispatch(on: .global()) {
// let client = try await Task.dispatch(on: .global()) { try self.authenticationService.loginWithOidcCallback(authenticationData: data.underlyingData, callbackUrl: callbackURL.absoluteString)
// try self.authenticationService.loginWithOidcCallback(authenticationData: data.underlyingData, callbackUrl: callbackURL.absoluteString) }
// } return await userSession(for: client)
// return await userSession(for: client) } catch AuthenticationError.OidcCancelled {
// } catch AuthenticationError.OidcCancelled { return .failure(.oidcError(.userCancellation))
// return .failure(.oidcError(.userCancellation)) } catch {
// } catch { MXLog.error("Login with OIDC failed: \(error)")
// MXLog.error("Login with OIDC failed: \(error)") return .failure(.failedLoggingIn)
// 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> {

View File

@ -66,16 +66,18 @@ enum OIDCError: Error {
} }
struct OIDCAuthenticationDataProxy: Equatable { struct OIDCAuthenticationDataProxy: Equatable {
// let underlyingData: OidcAuthenticationData let underlyingData: OidcAuthenticationData
//
// var url: URL { var url: URL {
// URL(string: underlyingData.loginUrl())! guard let url = URL(string: underlyingData.loginUrl()) else {
// } fatalError("OIDC login URL hasn't been validated.")
let url: URL = "https://theroadtonowhere" }
return url
}
} }
// extension OidcAuthenticationData: Equatable { extension OidcAuthenticationData: Equatable {
// public static func == (lhs: MatrixRustSDK.OidcAuthenticationData, rhs: MatrixRustSDK.OidcAuthenticationData) -> Bool { public static func == (lhs: MatrixRustSDK.OidcAuthenticationData, rhs: MatrixRustSDK.OidcAuthenticationData) -> Bool {
// lhs.loginUrl() == rhs.loginUrl() lhs.loginUrl() == rhs.loginUrl()
// } }
// } }

View File

@ -109,6 +109,10 @@ class ClientProxy: ClientProxyProtocol {
} }
} }
var accountURL: URL? {
client.accountUrl().flatMap(URL.init(string:))
}
func startSync() { func startSync() {
MXLog.info("Starting sync") MXLog.info("Starting sync")
@ -305,7 +309,8 @@ class ClientProxy: ClientProxyProtocol {
func logout() async { func logout() async {
await Task.dispatch(on: clientQueue) { await Task.dispatch(on: clientQueue) {
do { do {
try self.client.logout() // We aren't currently handling the RP initiated sign out URL.
_ = try self.client.logout()
} catch { } catch {
MXLog.error("Failed logging out with error: \(error)") MXLog.error("Failed logging out with error: \(error)")
} }

View File

@ -76,6 +76,8 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol {
var restorationToken: RestorationToken? { get } var restorationToken: RestorationToken? { get }
var accountURL: URL? { get }
var roomSummaryProvider: RoomSummaryProviderProtocol? { get } var roomSummaryProvider: RoomSummaryProviderProtocol? { get }
var inviteSummaryProvider: RoomSummaryProviderProtocol? { get } var inviteSummaryProvider: RoomSummaryProviderProtocol? { get }

View File

@ -25,6 +25,7 @@ class MockClientProxy: ClientProxyProtocol {
let deviceID: String? let deviceID: String?
let homeserver = "" let homeserver = ""
let restorationToken: RestorationToken? = nil let restorationToken: RestorationToken? = nil
let accountURL: URL?
var roomSummaryProvider: RoomSummaryProviderProtocol? = MockRoomSummaryProvider() var roomSummaryProvider: RoomSummaryProviderProtocol? = MockRoomSummaryProvider()
@ -32,9 +33,10 @@ class MockClientProxy: ClientProxyProtocol {
var avatarURLPublisher: AnyPublisher<URL?, Never> { Empty().eraseToAnyPublisher() } var avatarURLPublisher: AnyPublisher<URL?, Never> { Empty().eraseToAnyPublisher() }
init(userID: String, deviceID: String? = nil, roomSummaryProvider: RoomSummaryProviderProtocol? = MockRoomSummaryProvider()) { init(userID: String, deviceID: String? = nil, accountURL: URL? = nil, roomSummaryProvider: RoomSummaryProviderProtocol? = MockRoomSummaryProvider()) {
self.userID = userID self.userID = userID
self.deviceID = deviceID self.deviceID = deviceID
self.accountURL = accountURL
self.roomSummaryProvider = roomSummaryProvider self.roomSummaryProvider = roomSummaryProvider
} }

View File

@ -42,8 +42,8 @@ extension MatrixRustSDK.Session: Codable {
userId: container.decode(String.self, forKey: .userId), userId: container.decode(String.self, forKey: .userId),
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),
oidcData: container.decodeIfPresent(String.self, forKey: .oidcData),
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,12 +53,11 @@ extension MatrixRustSDK.Session: Codable {
try container.encode(userId, forKey: .userId) try container.encode(userId, forKey: .userId)
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(oidcData, forKey: .oidcData)
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, oidcData, slidingSyncProxy
// case oidcData
} }
} }

View File

@ -166,10 +166,10 @@ class MockScreen: Identifiable {
return navigationStackCoordinator return navigationStackCoordinator
case .settings: case .settings:
let navigationStackCoordinator = NavigationStackCoordinator() let navigationStackCoordinator = NavigationStackCoordinator()
let clientProxy = MockClientProxy(userID: "@mock:client.com", accountURL: "https://matrix.org/account")
let coordinator = SettingsScreenCoordinator(parameters: .init(navigationStackCoordinator: navigationStackCoordinator, let coordinator = SettingsScreenCoordinator(parameters: .init(navigationStackCoordinator: navigationStackCoordinator,
userIndicatorController: nil, userIndicatorController: nil,
userSession: MockUserSession(clientProxy: MockClientProxy(userID: "@mock:client.com"), userSession: MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider()),
mediaProvider: MockMediaProvider()),
bugReportService: BugReportServiceMock(), bugReportService: BugReportServiceMock(),
notificationSettings: NotificationSettingsProxyMock(with: .init()))) notificationSettings: NotificationSettingsProxyMock(with: .init())))
navigationStackCoordinator.setRootCoordinator(coordinator) navigationStackCoordinator.setRootCoordinator(coordinator)

View File

@ -41,6 +41,12 @@ class NotificationServiceExtension: UNNotificationServiceExtension {
return contentHandler(request.content) return contentHandler(request.content)
} }
if credentials.restorationToken.session.oidcData != nil {
// Notification content is disabled for OIDC sessions
// until token refresh is multi-process aware.
return contentHandler(request.content)
}
handler = contentHandler handler = contentHandler
modifiedContent = request.content.mutableCopy() as? UNMutableNotificationContent modifiedContent = request.content.mutableCopy() as? UNMutableNotificationContent

View File

@ -37,6 +37,7 @@ class KeychainControllerTests: XCTestCase {
userId: "userId", userId: "userId",
deviceId: "deviceId", deviceId: "deviceId",
homeserverUrl: "homeserverUrl", homeserverUrl: "homeserverUrl",
oidcData: "oidcData",
slidingSyncProxy: "https://my.sync.proxy")) slidingSyncProxy: "https://my.sync.proxy"))
keychain.setRestorationToken(restorationToken, forUsername: username) keychain.setRestorationToken(restorationToken, forUsername: username)
@ -52,6 +53,7 @@ class KeychainControllerTests: XCTestCase {
userId: "userId", userId: "userId",
deviceId: "deviceId", deviceId: "deviceId",
homeserverUrl: "homeserverUrl", homeserverUrl: "homeserverUrl",
oidcData: "oidcData",
slidingSyncProxy: "https://my.sync.proxy")) slidingSyncProxy: "https://my.sync.proxy"))
keychain.setRestorationToken(restorationToken, forUsername: username) keychain.setRestorationToken(restorationToken, forUsername: username)
XCTAssertEqual(keychain.restorationTokens().count, 1, "The keychain should have 1 restoration token.") XCTAssertEqual(keychain.restorationTokens().count, 1, "The keychain should have 1 restoration token.")
@ -73,6 +75,7 @@ class KeychainControllerTests: XCTestCase {
userId: "userId", userId: "userId",
deviceId: "deviceId", deviceId: "deviceId",
homeserverUrl: "homeserverUrl", homeserverUrl: "homeserverUrl",
oidcData: "oidcData",
slidingSyncProxy: "https://my.sync.proxy")) slidingSyncProxy: "https://my.sync.proxy"))
keychain.setRestorationToken(restorationToken, forUsername: "@test\(index):example.com") keychain.setRestorationToken(restorationToken, forUsername: "@test\(index):example.com")
} }
@ -93,6 +96,7 @@ class KeychainControllerTests: XCTestCase {
userId: "userId", userId: "userId",
deviceId: "deviceId", deviceId: "deviceId",
homeserverUrl: "homeserverUrl", homeserverUrl: "homeserverUrl",
oidcData: "oidcData",
slidingSyncProxy: "https://my.sync.proxy")) slidingSyncProxy: "https://my.sync.proxy"))
keychain.setRestorationToken(restorationToken, forUsername: "@test\(index):example.com") keychain.setRestorationToken(restorationToken, forUsername: "@test\(index):example.com")
} }

1
changelog.d/261.feature Normal file
View File

@ -0,0 +1 @@
Enable OIDC support, with notification content disabled for now.

View File

@ -45,7 +45,7 @@ packages:
# Element/Matrix dependencies # Element/Matrix dependencies
MatrixRustSDK: MatrixRustSDK:
url: https://github.com/matrix-org/matrix-rust-components-swift url: https://github.com/matrix-org/matrix-rust-components-swift
exactVersion: 1.0.110-alpha exactVersion: 1.0.112-alpha
# path: ../matrix-rust-sdk # path: ../matrix-rust-sdk
DesignKit: DesignKit:
path: DesignKit path: DesignKit