mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 01:20:12 +00:00
Merge the AuthenticationService with the QRCodeLoginService. (#4323)
* Merge the AuthenticationServer with the QRCodeLoginService. * Merge AuthenticationClientBuilderFactory and AuthenticationClientBuilder into AuthenticationClientFactory The separation is no longer needed now that password/OIDC login and QR code login have a similar API shape.
This commit is contained in:
@@ -14,7 +14,7 @@ class AuthenticationService: AuthenticationServiceProtocol {
|
||||
private var sessionDirectories: SessionDirectories
|
||||
private let passphrase: String
|
||||
|
||||
private let clientBuilderFactory: AuthenticationClientBuilderFactoryProtocol
|
||||
private let clientFactory: AuthenticationClientFactoryProtocol
|
||||
private let userSessionStore: UserSessionStoreProtocol
|
||||
private let appSettings: AppSettings
|
||||
private let appHooks: AppHooks
|
||||
@@ -23,14 +23,19 @@ class AuthenticationService: AuthenticationServiceProtocol {
|
||||
var homeserver: CurrentValuePublisher<LoginHomeserver, Never> { homeserverSubject.asCurrentValuePublisher() }
|
||||
private(set) var flow: AuthenticationFlow
|
||||
|
||||
private let qrLoginProgressSubject = PassthroughSubject<QrLoginProgress, Never>()
|
||||
var qrLoginProgressPublisher: AnyPublisher<QrLoginProgress, Never> {
|
||||
qrLoginProgressSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(userSessionStore: UserSessionStoreProtocol,
|
||||
encryptionKeyProvider: EncryptionKeyProviderProtocol,
|
||||
clientBuilderFactory: AuthenticationClientBuilderFactoryProtocol = AuthenticationClientBuilderFactory(),
|
||||
clientFactory: AuthenticationClientFactoryProtocol = AuthenticationClientFactory(),
|
||||
appSettings: AppSettings,
|
||||
appHooks: AppHooks) {
|
||||
sessionDirectories = .init()
|
||||
passphrase = encryptionKeyProvider.generateKey().base64EncodedString()
|
||||
self.clientBuilderFactory = clientBuilderFactory
|
||||
self.clientFactory = clientFactory
|
||||
self.userSessionStore = userSessionStore
|
||||
self.appSettings = appSettings
|
||||
self.appHooks = appHooks
|
||||
@@ -46,7 +51,7 @@ class AuthenticationService: AuthenticationServiceProtocol {
|
||||
do {
|
||||
var homeserver = LoginHomeserver(address: homeserverAddress, loginMode: .unknown)
|
||||
|
||||
let client = try await makeClientBuilder().build(homeserverAddress: homeserverAddress)
|
||||
let client = try await makeClient(homeserverAddress: homeserverAddress)
|
||||
let loginDetails = await client.homeserverLoginDetails()
|
||||
|
||||
MXLog.info("Sliding sync: \(client.slidingSyncVersion())")
|
||||
@@ -146,6 +151,45 @@ class AuthenticationService: AuthenticationServiceProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
func loginWithQRCode(data: Data) async -> Result<UserSessionProtocol, AuthenticationServiceError> {
|
||||
let qrData: QrCodeData
|
||||
do {
|
||||
qrData = try QrCodeData.fromBytes(bytes: data)
|
||||
} catch {
|
||||
MXLog.error("QRCode decode error: \(error)")
|
||||
return .failure(.qrCodeError(.invalidQRCode))
|
||||
}
|
||||
|
||||
guard let scannedServerName = qrData.serverName() else {
|
||||
MXLog.error("The QR code is from a device that is not yet signed in.")
|
||||
return .failure(.qrCodeError(.deviceNotSignedIn))
|
||||
}
|
||||
|
||||
if !appSettings.allowOtherAccountProviders, !appSettings.accountProviders.contains(scannedServerName) {
|
||||
MXLog.error("The scanned device's server is not allowed: \(scannedServerName)")
|
||||
return .failure(.qrCodeError(.providerNotAllowed(scannedProvider: scannedServerName, allowedProviders: appSettings.accountProviders)))
|
||||
}
|
||||
|
||||
let listener = SDKListener { [weak self] progress in
|
||||
self?.qrLoginProgressSubject.send(progress)
|
||||
}
|
||||
|
||||
do {
|
||||
let client = try await makeClient(homeserverAddress: scannedServerName)
|
||||
try await client.loginWithQrCode(qrCodeData: qrData,
|
||||
oidcConfiguration: appSettings.oidcConfiguration.rustValue,
|
||||
progressListener: listener)
|
||||
MXLog.info("Sliding sync: \(client.slidingSyncVersion())")
|
||||
return await userSession(for: client)
|
||||
} catch let error as HumanQrLoginError {
|
||||
MXLog.error("QRCode login error: \(error)")
|
||||
return .failure(error.serviceError)
|
||||
} catch {
|
||||
MXLog.error("QRCode login unknown error: \(error)")
|
||||
return .failure(.qrCodeError(.unknown))
|
||||
}
|
||||
}
|
||||
|
||||
func reset() {
|
||||
homeserverSubject.send(LoginHomeserver(address: appSettings.accountProviders[0], loginMode: .unknown))
|
||||
flow = .login
|
||||
@@ -154,16 +198,17 @@ class AuthenticationService: AuthenticationServiceProtocol {
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func makeClientBuilder() -> AuthenticationClientBuilderProtocol {
|
||||
private func makeClient(homeserverAddress: String) async throws -> ClientProtocol {
|
||||
// Use a fresh session directory each time the user enters a different server
|
||||
// so that caches (e.g. server versions) are always fresh for the new server.
|
||||
rotateSessionDirectory()
|
||||
|
||||
return clientBuilderFactory.makeBuilder(sessionDirectories: sessionDirectories,
|
||||
passphrase: passphrase,
|
||||
clientSessionDelegate: userSessionStore.clientSessionDelegate,
|
||||
appSettings: appSettings,
|
||||
appHooks: appHooks)
|
||||
return try await clientFactory.makeClient(homeserverAddress: homeserverAddress,
|
||||
sessionDirectories: sessionDirectories,
|
||||
passphrase: passphrase,
|
||||
clientSessionDelegate: userSessionStore.clientSessionDelegate,
|
||||
appSettings: appSettings,
|
||||
appHooks: appHooks)
|
||||
}
|
||||
|
||||
private func rotateSessionDirectory() {
|
||||
@@ -181,13 +226,36 @@ class AuthenticationService: AuthenticationServiceProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
private extension HumanQrLoginError {
|
||||
var serviceError: AuthenticationServiceError {
|
||||
switch self {
|
||||
case .Cancelled:
|
||||
.qrCodeError(.cancelled)
|
||||
case .ConnectionInsecure:
|
||||
.qrCodeError(.connectionInsecure)
|
||||
case .Declined:
|
||||
.qrCodeError(.declined)
|
||||
case .LinkingNotSupported:
|
||||
.qrCodeError(.linkingNotSupported)
|
||||
case .Expired:
|
||||
.qrCodeError(.expired)
|
||||
case .SlidingSyncNotAvailable:
|
||||
.qrCodeError(.deviceNotSupported)
|
||||
case .OtherDeviceNotSignedIn:
|
||||
.qrCodeError(.deviceNotSignedIn)
|
||||
case .Unknown, .OidcMetadataInvalid:
|
||||
.qrCodeError(.unknown)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Mocks
|
||||
|
||||
extension AuthenticationService {
|
||||
static var mock: AuthenticationService {
|
||||
AuthenticationService(userSessionStore: UserSessionStoreMock(configuration: .init()),
|
||||
encryptionKeyProvider: EncryptionKeyProvider(),
|
||||
clientBuilderFactory: AuthenticationClientBuilderFactoryMock(configuration: .init()),
|
||||
clientFactory: AuthenticationClientFactoryMock(configuration: .init()),
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
appHooks: AppHooks())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user