diff --git a/app/shared/src/commonMain/kotlin/ru/fromchat/api/ApiClient.kt b/app/shared/src/commonMain/kotlin/ru/fromchat/api/ApiClient.kt index 7700848..ac84d9d 100644 --- a/app/shared/src/commonMain/kotlin/ru/fromchat/api/ApiClient.kt +++ b/app/shared/src/commonMain/kotlin/ru/fromchat/api/ApiClient.kt @@ -121,39 +121,46 @@ object ApiClient { } - suspend fun login(request: LoginRequest) = + suspend fun loginRequest(request: LoginRequest): LoginResponse = http .post("${Config.apiBaseUrl}/login") { contentType(ContentType.Application.Json) setBody(request) } - .body() - .also { - token = it.token - user = it.user - secureSettings.putString("auth_token", it.token) - settings.putString("user_info", json.encodeToString(it.user)) - settings.putInt("current_user_id", it.user.id) - // Upload any pending FCM token after successful login - MainScope().launch { - runCatching { - uploadPendingFcmTokenIfAvailable() - } - } - } + .body() - suspend fun register(request: RegisterRequest) = - http.post("${Config.apiBaseUrl}/register") { - contentType(ContentType.Application.Json) - setBody(request) - }.also { - // Upload any pending FCM token after successful registration - MainScope().launch { - runCatching { - uploadPendingFcmTokenIfAvailable() - } + suspend fun registerRequest(request: RegisterRequest): LoginResponse = + http + .post("${Config.apiBaseUrl}/register") { + contentType(ContentType.Application.Json) + setBody(request) + } + .body() + + /** + * Sets in-memory auth only so follow-up calls (e.g. crypto upload) use Bearer token. + * Persist with [persistSessionToStorage] only after identity keys are fully synced. + */ + fun bindSession(loginResponse: LoginResponse) { + token = loginResponse.token + user = loginResponse.user + } + + fun clearMemorySession() { + token = null + user = null + } + + suspend fun persistSessionToStorage(loginResponse: LoginResponse) { + secureSettings.putString("auth_token", loginResponse.token) + settings.putString("user_info", json.encodeToString(loginResponse.user)) + settings.putInt("current_user_id", loginResponse.user.id) + MainScope().launch { + runCatching { + uploadPendingFcmTokenIfAvailable() } } + } suspend fun getMessages(limit: Int = 50, beforeId: Int? = null) = http diff --git a/app/shared/src/commonMain/kotlin/ru/fromchat/ui/App.kt b/app/shared/src/commonMain/kotlin/ru/fromchat/ui/App.kt index 162af69..d4f32ae 100644 --- a/app/shared/src/commonMain/kotlin/ru/fromchat/ui/App.kt +++ b/app/shared/src/commonMain/kotlin/ru/fromchat/ui/App.kt @@ -157,7 +157,11 @@ fun App(scrollToMessageId: Int? = null, startAtPublicChat: Boolean = false) { composable("register") { RegisterScreen( - onRegistered = { navController.navigate("login") } + onRegistered = { + navController.navigate("chat") { + popUpTo("login") { inclusive = true } + } + } ) } diff --git a/app/shared/src/commonMain/kotlin/ru/fromchat/ui/auth/LoginScreen.kt b/app/shared/src/commonMain/kotlin/ru/fromchat/ui/auth/LoginScreen.kt index b111562..fe1220a 100644 --- a/app/shared/src/commonMain/kotlin/ru/fromchat/ui/auth/LoginScreen.kt +++ b/app/shared/src/commonMain/kotlin/ru/fromchat/ui/auth/LoginScreen.kt @@ -181,13 +181,19 @@ fun LoginScreen( onLoginSuccess() } ) { - val response = ApiClient.login(LoginRequest(trimmedUsername, derived)) - // Ensure identity keys and backup are initialized after successful login - IdentityKeyManager.ensureKeysOnLogin( - username = trimmedUsername, - password = trimmedPassword, - token = response.token - ) + val response = ApiClient.loginRequest(LoginRequest(trimmedUsername, derived)) + ApiClient.bindSession(response) + try { + IdentityKeyManager.ensureKeysOnLogin( + username = trimmedUsername, + password = trimmedPassword, + token = response.token + ) + } catch (e: Exception) { + ApiClient.clearMemorySession() + throw e + } + ApiClient.persistSessionToStorage(response) response } } diff --git a/app/shared/src/commonMain/kotlin/ru/fromchat/ui/auth/RegisterScreen.kt b/app/shared/src/commonMain/kotlin/ru/fromchat/ui/auth/RegisterScreen.kt index 49c800d..f2d95a3 100644 --- a/app/shared/src/commonMain/kotlin/ru/fromchat/ui/auth/RegisterScreen.kt +++ b/app/shared/src/commonMain/kotlin/ru/fromchat/ui/auth/RegisterScreen.kt @@ -36,6 +36,7 @@ import ru.fromchat.Res import ru.fromchat.api.ApiClient import ru.fromchat.api.RegisterRequest import ru.fromchat.api.apiRequest +import ru.fromchat.crypto.IdentityKeyManager import ru.fromchat.back import ru.fromchat.confirm_password import ru.fromchat.display_name @@ -165,8 +166,10 @@ fun RegisterScreen( // Derive auth secret before sending (matches frontend implementation) scope.launch { - val derived = deriveAuthSecret(username.trim(), password) - + val u = username.trim() + val display = displayName.trim() + val derived = deriveAuthSecret(u, password) + apiRequest( unexpectedError = errorUnexpected, onError = { message, _ -> @@ -176,14 +179,27 @@ fun RegisterScreen( onRegistered() } ) { - ApiClient.register( + val response = ApiClient.registerRequest( RegisterRequest( - username.trim(), - displayName.trim(), + u, + display, derived, derived ) ) + ApiClient.bindSession(response) + try { + IdentityKeyManager.ensureKeysOnLogin( + username = u, + password = password, + token = response.token + ) + } catch (e: Exception) { + ApiClient.clearMemorySession() + throw e + } + ApiClient.persistSessionToStorage(response) + response } } }