refactor(auth, design-system): remove unused methods and annotations for cleanup

- Deleted obsolete methods such as `exchangeAuthorizationCode` and `logout` from the Auth module.
- Removed unused browser PKCE utilities and associated constants.
- Annotated unused components across the Design System with `@Suppress("unused")` for clarity.
- Simplified `LoginViewModel` to handle logout by clearing UI state directly.
This commit is contained in:
2026-01-23 01:36:46 +01:00
parent d7cf8200e1
commit ec97b62afa
10 changed files with 18 additions and 106 deletions
@@ -3,19 +3,11 @@ package at.mocode.frontend.core.auth.data
import at.mocode.shared.core.AppConstants import at.mocode.shared.core.AppConstants
import io.ktor.client.* import io.ktor.client.*
import io.ktor.client.call.* import io.ktor.client.call.*
import io.ktor.client.request.*
import io.ktor.client.request.forms.* import io.ktor.client.request.forms.*
import io.ktor.http.* import io.ktor.http.*
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
/**
* Data classes for authentication API communication
*/
@Serializable
data class LoginRequest(
val username: String,
val password: String
)
@Serializable @Serializable
data class LoginResponse( data class LoginResponse(
val success: Boolean, val success: Boolean,
@@ -85,49 +77,6 @@ class AuthApiClient(
} }
} }
/**
* Exchange an authorization code (PKCE) for tokens
*/
suspend fun exchangeAuthorizationCode(code: String, codeVerifier: String, redirectUri: String): LoginResponse {
val tokenEndpoint = "$keycloakBaseUrl/realms/$realm/protocol/openid-connect/token"
return try {
val response = httpClient.submitForm(
url = tokenEndpoint,
formParameters = Parameters.build {
append("grant_type", "authorization_code")
append("client_id", clientId)
if (!clientSecret.isNullOrBlank()) {
append("client_secret", clientSecret)
}
append("code", code)
append("code_verifier", codeVerifier)
append("redirect_uri", redirectUri)
}
) {
contentType(ContentType.Application.FormUrlEncoded)
}
if (response.status.isSuccess()) {
val kc = response.body<KeycloakTokenResponse>()
LoginResponse(
success = true,
token = kc.access_token,
message = null
)
} else {
LoginResponse(
success = false,
message = "Code-Exchange fehlgeschlagen: HTTP ${'$'}{response.status.value}"
)
}
} catch (e: Exception) {
LoginResponse(
success = false,
message = "Code-Exchange Fehler: ${'$'}{e.message}"
)
}
}
/** /**
* Refresh authentication token * Refresh authentication token
*/ */
@@ -169,15 +118,6 @@ class AuthApiClient(
} }
} }
/**
* Logout and invalidate token
*/
suspend fun logout(token: String): Boolean {
// Empfehlung: Frontend-seitig Token lokal verwerfen.
// Optional könnten hier Keycloak-Endpoints für Token-Revocation aufgerufen werden.
return true
}
@Serializable @Serializable
private data class KeycloakTokenResponse( private data class KeycloakTokenResponse(
val access_token: String, val access_token: String,
@@ -70,6 +70,7 @@ data class AuthState(
* to prevent XSS attacks. The token is lost when the browser tab is closed * to prevent XSS attacks. The token is lost when the browser tab is closed
* or refreshed, requiring re-authentication. * or refreshed, requiring re-authentication.
*/ */
@Suppress("unused")
class AuthTokenManager { class AuthTokenManager {
private var currentToken: String? = null private var currentToken: String? = null
@@ -46,6 +46,10 @@ class LoginViewModel(
_uiState.value = _uiState.value.copy( _uiState.value = _uiState.value.copy(
isAuthenticated = authState.isAuthenticated isAuthenticated = authState.isAuthenticated
) )
// If logged out, clear credentials
if (!authState.isAuthenticated) {
_uiState.value = LoginUiState()
}
} }
} }
} }
@@ -129,15 +133,4 @@ class LoginViewModel(
} }
} }
} }
fun logout() {
authTokenManager.clearToken()
// Reset UI state (clear username/password)
_uiState.value = LoginUiState()
}
fun checkAuthenticationStatus() {
val isAuthenticated = authTokenManager.hasValidToken()
_uiState.value = _uiState.value.copy(isAuthenticated = isAuthenticated)
}
} }
@@ -7,6 +7,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@Suppress("unused")
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun AppScaffold( fun AppScaffold(
@@ -51,6 +51,7 @@ fun LoadingIndicator(
} }
} }
@Suppress("unused")
@Composable @Composable
fun FullScreenLoading( fun FullScreenLoading(
message: String = "Loading...", message: String = "Loading...",
@@ -67,6 +68,7 @@ fun FullScreenLoading(
} }
} }
@Suppress("unused")
@Composable @Composable
fun InlineLoading( fun InlineLoading(
message: String? = null, message: String? = null,
@@ -84,6 +86,7 @@ fun InlineLoading(
} }
} }
@Suppress("unused")
@Composable @Composable
fun LinearLoadingIndicator( fun LinearLoadingIndicator(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@@ -87,6 +87,7 @@ private fun ButtonContent(
} }
} }
@Suppress("unused")
@Composable @Composable
fun PrimaryButton( fun PrimaryButton(
text: String, text: String,
@@ -105,6 +106,7 @@ fun PrimaryButton(
fullWidth = fullWidth fullWidth = fullWidth
) )
@Suppress("unused")
@Composable @Composable
fun SecondaryButton( fun SecondaryButton(
text: String, text: String,
@@ -92,6 +92,7 @@ fun MeldestelleTextField(
} }
} }
@Suppress("unused")
@Composable @Composable
fun MeldestellePasswordField( fun MeldestellePasswordField(
value: String, value: String,
@@ -136,6 +137,7 @@ fun MeldestellePasswordField(
) )
} }
@Suppress("unused")
@Composable @Composable
fun MeldestelleEmailField( fun MeldestelleEmailField(
value: String, value: String,
@@ -169,6 +171,7 @@ fun MeldestelleEmailField(
/** /**
* Form validation utilities * Form validation utilities
*/ */
@Suppress("unused")
object FormValidation { object FormValidation {
fun validateEmail(email: String): String? { fun validateEmail(email: String): String? {
return when { return when {
@@ -35,6 +35,7 @@ private val DarkColorScheme = darkColorScheme(
onSurface = Color(0xFFE0E0E0) onSurface = Color(0xFFE0E0E0)
) )
@Suppress("unused")
@Composable @Composable
fun AppTheme( fun AppTheme(
darkTheme: Boolean = false, // For now, we'll default to light theme darkTheme: Boolean = false, // For now, we'll default to light theme
@@ -6,6 +6,7 @@ package at.mocode.shared.core
*/ */
object AppConstants { object AppConstants {
// Gateway base URL (reverse proxy / API gateway) // Gateway base URL (reverse proxy / API gateway)
// Used by NetworkConfig via PlatformConfig
const val GATEWAY_URL: String = "http://localhost:8081" const val GATEWAY_URL: String = "http://localhost:8081"
// Keycloak configuration // Keycloak configuration
@@ -21,33 +22,5 @@ object AppConstants {
// For the Desktop App Pilot, we use this to simulate a secure client. // For the Desktop App Pilot, we use this to simulate a secure client.
const val KEYCLOAK_CLIENT_SECRET: String = "postman-secret-123" const val KEYCLOAK_CLIENT_SECRET: String = "postman-secret-123"
// Default redirect URI for web PKCE flow (served by Nginx in web image) // Removed unused browser flow URLs (registerUrl, loginUrl, etc.) as we focus on Desktop App.
// We use the root path so Keycloak can redirect back to /?code=...
fun webRedirectUri(): String = "http://localhost:4000/"
fun registerUrl(): String =
"$KEYCLOAK_URL/realms/$KEYCLOAK_REALM/protocol/openid-connect/registrations?client_id=web-app&response_type=code&redirect_uri=${
encode(
webRedirectUri()
)
}"
fun loginUrl(): String =
"$KEYCLOAK_URL/realms/$KEYCLOAK_REALM/protocol/openid-connect/auth?client_id=web-app&response_type=code&redirect_uri=${
encode(
webRedirectUri()
)
}"
fun authorizeEndpoint(): String =
"$KEYCLOAK_URL/realms/$KEYCLOAK_REALM/protocol/openid-connect/auth"
fun tokenEndpoint(): String =
"$KEYCLOAK_URL/realms/$KEYCLOAK_REALM/protocol/openid-connect/token"
fun desktopDownloadUrl(): String = "http://localhost:4000/downloads/"
// Helper to URL-encode values (very small percent-encoding sufficient for URIs here)
private fun encode(value: String): String =
value.replace("://", ":%2F%2F").replace("/", "%2F").replace(":", "%3A")
} }
@@ -4,20 +4,15 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import at.mocode.clients.shared.navigation.AppScreen import at.mocode.clients.shared.navigation.AppScreen
import at.mocode.frontend.core.auth.data.AuthTokenManager import at.mocode.frontend.core.auth.data.AuthTokenManager
import at.mocode.frontend.core.auth.data.AuthApiClient
import at.mocode.frontend.core.auth.presentation.LoginScreen import at.mocode.frontend.core.auth.presentation.LoginScreen
import at.mocode.frontend.core.auth.presentation.LoginViewModel import at.mocode.frontend.core.auth.presentation.LoginViewModel
import at.mocode.ping.feature.presentation.PingScreen import at.mocode.ping.feature.presentation.PingScreen
import at.mocode.ping.feature.presentation.PingViewModel import at.mocode.ping.feature.presentation.PingViewModel
import at.mocode.shared.core.AppConstants
import at.mocode.frontend.core.designsystem.components.AppFooter import at.mocode.frontend.core.designsystem.components.AppFooter
import kotlinx.coroutines.launch
import androidx.compose.runtime.rememberCoroutineScope
import org.koin.compose.koinInject import org.koin.compose.koinInject
import org.koin.compose.viewmodel.koinViewModel import org.koin.compose.viewmodel.koinViewModel