refactor: update Dockerfile paths, network module auth flow, and Keycloak config

Updated Dockerfiles to fix frontend path references after refactoring. Refactored `networkModule` to replace the `Auth` plugin with manual auth header injection for enhanced logout support. Adjusted Keycloak realm configuration to set default credentials as non-temporary. Improved error handling in `AuthApiClient` with detailed response messages.
This commit is contained in:
2026-01-23 15:42:07 +01:00
parent a3ac6a52be
commit 48ee074dbd
7 changed files with 44 additions and 35 deletions
@@ -4,6 +4,7 @@ import at.mocode.shared.core.AppConstants
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.request.forms.*
import io.ktor.client.statement.*
import io.ktor.http.*
import kotlinx.serialization.Serializable
@@ -63,9 +64,10 @@ class AuthApiClient(
username = username
)
} else {
val errorBody = response.bodyAsText()
LoginResponse(
success = false,
message = "Login fehlgeschlagen: HTTP ${response.status.value}"
message = "Login fehlgeschlagen: HTTP ${response.status.value} - $errorBody"
)
}
} catch (e: Exception) {
@@ -104,9 +106,10 @@ class AuthApiClient(
message = null
)
} else {
val errorBody = response.bodyAsText()
LoginResponse(
success = false,
message = "Token refresh fehlgeschlagen: HTTP ${response.status.value}"
message = "Token refresh fehlgeschlagen: HTTP ${response.status.value} - $errorBody"
)
}
} catch (e: Exception) {
@@ -15,10 +15,10 @@ val authModule = module {
// Single in-memory token manager
single { AuthTokenManager() }
// AuthApiClient with injected apiClient and DEV client secret
// AuthApiClient with injected baseHttpClient (NOT apiClient)
single {
AuthApiClient(
httpClient = get(named("apiClient")),
httpClient = get(named("baseHttpClient")),
clientSecret = AppConstants.KEYCLOAK_CLIENT_SECRET
)
}
@@ -30,8 +30,7 @@ val authModule = module {
single<TokenProvider> {
object : TokenProvider {
override fun getAccessToken(): String? {
val token = get<AuthTokenManager>().getToken()
return token
return get<AuthTokenManager>().getToken()
}
}
}
@@ -2,10 +2,9 @@ package at.mocode.frontend.core.network
import io.ktor.client.*
import io.ktor.client.plugins.*
import io.ktor.client.plugins.auth.*
import io.ktor.client.plugins.auth.providers.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.plugins.logging.*
import io.ktor.client.request.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.json.Json
import org.koin.core.qualifier.named
@@ -19,10 +18,28 @@ interface TokenProvider {
}
/**
* Koin module that provides a preconfigured Ktor HttpClient under the named qualifier "apiClient".
* The client uses the environment-aware base URL from NetworkConfig.
* Koin module providing HttpClients.
*/
val networkModule = module {
// 1. Base Client (Raw, for Auth/Keycloak)
single(named("baseHttpClient")) {
HttpClient {
install(ContentNegotiation) {
json(Json { ignoreUnknownKeys = true; isLenient = true; encodeDefaults = true })
}
install(Logging) {
logger = object : Logger {
override fun log(message: String) {
println("[baseClient] $message")
}
}
level = LogLevel.INFO
}
}
}
// 2. API Client (Configured for Gateway & Auth Header)
single(named("apiClient")) {
val tokenProvider: TokenProvider? = try { get<TokenProvider>() } catch (_: Throwable) { null }
HttpClient {
@@ -54,19 +71,15 @@ val networkModule = module {
exponentialDelay()
}
// Authentication plugin (Bearer)
install(Auth) {
bearer {
loadTokens {
val token = tokenProvider?.getAccessToken()
token?.let { BearerTokens(it, refreshToken = "") }
}
// Only send token to our API base URL
sendWithoutRequest { request ->
val base = NetworkConfig.baseUrl.trimEnd('/')
val url = request.url.toString()
url.startsWith(base)
}
// Manual Auth Header Injection (instead of Auth plugin) to support immediate logout
install(DefaultRequest) {
val base = NetworkConfig.baseUrl.trimEnd('/')
url(base) // Set base URL
// Inject Authorization header if token is present
val token = tokenProvider?.getAccessToken()
if (token != null) {
header("Authorization", "Bearer $token")
}
}
@@ -79,12 +92,6 @@ val networkModule = module {
}
level = LogLevel.INFO
}
// Set base URL
defaultRequest {
// Set only the base URL; endpoints will append paths
url(NetworkConfig.baseUrl)
}
}
}
}