// Version: 1.1.0 | Created: 2026-04-01 | Updated: 2026-04-02 // Auth repository: handles all Matrix login/logout API interactions. // Uses the Matrix Dart SDK — no raw HTTP calls for auth. import 'package:matrix/matrix.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../../../core/config/app_config.dart'; import '../../../core/network/matrix_client.dart'; import '../domain/auth_failure.dart'; part 'auth_repository.g.dart'; @Riverpod(keepAlive: true) AuthRepository authRepository(Ref ref) { return AuthRepository(client: ref.watch(matrixClientProvider)); } /// Handles authentication interactions with the Matrix homeserver. class AuthRepository { AuthRepository({required Client client}) : _client = client; final Client _client; /// Attempts password login. Returns [LoginResponse] on success, /// throws [AuthFailure] on failure. /// /// Matrix error codes mapped: /// M_FORBIDDEN → [InvalidCredentials] /// M_USER_DEACTIVATED → [AccountDisabled] /// Network error → [NetworkError] Future login({ required String username, required String password, }) async { try { // Set homeserver directly — avoids network round-trips and version checks // that checkHomeserver() makes. The setter is public in matrix 0.33.0. _client.homeserver = Uri.parse(AppConfig.matrixBaseUrl); return await _client.login( LoginType.mLoginPassword, identifier: AuthenticationUserIdentifier(user: username), password: password, initialDeviceDisplayName: AppConfig.appName, ); } on MatrixException catch (e) { throw switch (e.errcode) { 'M_FORBIDDEN' => const AuthFailure.invalidCredentials(), 'M_USER_DEACTIVATED' => const AuthFailure.accountDisabled(), _ => AuthFailure.serverError( statusCode: e.response?.statusCode, message: e.errorMessage, ), }; } on Exception catch (e) { // Covers SocketException, TimeoutException, etc. final msg = e.toString().toLowerCase(); if (msg.contains('socket') || msg.contains('connection') || msg.contains('host lookup') || msg.contains('timeout')) { throw AuthFailure.networkError(message: e.toString()); } throw AuthFailure.unknown(message: e.toString()); } } /// Logs out the current session on the homeserver. /// Silently succeeds if the token is already invalid (network-first logout). Future logout() async { try { await _client.logout(); } on MatrixException { // Token already invalid — treat as successful logout. } on Exception { // Network offline — proceed with local cleanup regardless. } } /// Restores an existing Matrix session using a stored access token. Future restoreSession({ required String accessToken, required String userId, required String deviceId, }) async { await _client.init( newToken: accessToken, newUserID: userId, newDeviceID: deviceId, newDeviceName: AppConfig.appName, newHomeserver: Uri.parse(AppConfig.matrixBaseUrl), newOlmAccount: null, ); } }