Restore the .oidcCallback route for external authentication. (#5391)

* Restore the .oidcCallback route (partially reverts #3461) for external authentication.

* Make sure OIDC also works for non-http URLs.

* Remove oidcAuthentication from the state machine.

There isn't a reliable way to detect failure/cancellation when e.g. the user returns from an external app without interacting with the MAS page.
This commit is contained in:
Doug
2026-05-05 12:47:07 +01:00
committed by GitHub
parent aaf81e00f3
commit fef942f529
17 changed files with 181 additions and 52 deletions
@@ -276,6 +276,12 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
case .accountProvisioningLink:
handleAppRoute(route,
windowType: windowType)
case .oidcCallback(let url):
if stateMachine.state == .softLogout {
softLogoutCoordinator?.handleOIDCRedirectURL(url)
} else {
authenticationFlowCoordinator?.handleOIDCRedirectURL(url)
}
case .userProfile(let userID):
if isExternalURL {
handleAppRoute(route,
@@ -712,6 +718,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
let parameters = SoftLogoutScreenCoordinatorParameters(authenticationService: authenticationService,
credentials: credentials,
keyBackupNeeded: false,
appMediator: appMediator,
appSettings: appSettings,
userIndicatorController: ServiceLocator.shared.userIndicatorController)
let coordinator = SoftLogoutScreenCoordinator(parameters: parameters)
@@ -14,6 +14,9 @@ import MatrixRustSDK
enum AppRoute: Hashable {
/// An account provisioning link generated externally.
case accountProvisioningLink(AccountProvisioningParameters)
/// An external callback used to complete login with OIDC. This is only used when authentication
/// requires an external app so cannot be done within the built in web authentication session.
case oidcCallback(url: URL)
/// The app's home screen.
case roomList
@@ -58,6 +61,7 @@ enum AppRoute: Hashable {
var isAuthenticationRoute: Bool {
switch self {
case .accountProvisioningLink: true
case .oidcCallback: true
default: false
}
}
@@ -82,7 +86,8 @@ struct AppRouteURLParser {
AppGroupURLParser(),
MatrixPermalinkParser(),
ElementWebURLParser(domains: appSettings.elementWebHosts),
AccountProvisioningURLParser(domain: appSettings.accountProvisioningHost)
AccountProvisioningURLParser(domain: appSettings.accountProvisioningHost),
OIDCCallbackURLParser(redirectURL: appSettings.oidcRedirectURL)
]
}
@@ -198,3 +203,13 @@ private struct AccountProvisioningURLParser: URLParser {
return .accountProvisioningLink(.init(accountProvider: serverName, loginHint: loginHint))
}
}
/// The parser for the OIDC callback URL. This always returns a `.oidcCallback`.
struct OIDCCallbackURLParser: URLParser {
let redirectURL: URL
func route(from url: URL) -> AppRoute? {
guard url.absoluteString.starts(with: redirectURL.absoluteString) else { return nil }
return .oidcCallback(url: url)
}
}
@@ -251,8 +251,9 @@ final class AppSettings {
/// Any pre-defined static client registrations for OIDC issuers.
let oidcStaticRegistrations: [URL: String] = ["https://id.thirdroom.io/realms/thirdroom": "elementx"]
/// The redirect URL used for OIDC. This no longer uses universal links so we don't need the bundle ID to avoid conflicts between Element X, Nightly and PR builds.
private(set) var oidcRedirectURL: URL = "https://element.io/oidc/login"
/// The redirect URL used for OIDC. For the normal case we don't actually need the bundle ID as the web authentication session handles the redirect internally.
/// However in the case where MAS sends the user to an external app, we need to make sure that the system will open the correct variant of the app (e.g. Nightly).
private(set) var oidcRedirectURL: URL! = URL(string: "https://element.io/oauth/ios/\(InfoPlistReader.main.bundleIdentifier)")
private(set) lazy var oidcConfiguration = OIDCConfiguration(clientName: InfoPlistReader.main.bundleDisplayName,
redirectURI: oidcRedirectURL,