Authentication aufräumen, 1. Schritt
This commit is contained in:
1 parent
bf7119f580
commit
677d57fb3c
6 files changed
+59
-69
No files matched your search
@@ -1,3 +1,3 @@
|
||||
#auto generated
|
||||
#Mon Feb 09 23:38:51 CET 2026
|
||||
build.number=226
|
||||
#Wed Feb 11 01:28:34 CET 2026
|
||||
build.number=233
|
||||
@@ -54,8 +54,9 @@ fun App() {
|
||||
}
|
||||
Button(onClick = {
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
logger.debug { "requesting '/test'" }
|
||||
test.request("/test")
|
||||
val testurl = "/books"
|
||||
logger.debug { "requesting $testurl" }
|
||||
test.request(testurl)
|
||||
}
|
||||
}) {
|
||||
Text("Test Token Refresh")
|
||||
@@ -77,13 +78,13 @@ fun App() {
|
||||
@Single
|
||||
class TestAuth(val httpClient: HttpClient) {
|
||||
|
||||
private val srv = "https://kas.keinekekse.net/"
|
||||
private val srv = "https://kas.keinekekse.net"
|
||||
suspend fun request(url: String) {
|
||||
logger.debug { "connecting" }
|
||||
// val response = httpClient.get(srv + "testdb/books")
|
||||
// val books: List<Book> = response.body()
|
||||
|
||||
val response = httpClient.get(srv + url + "testdb/books")
|
||||
val response = httpClient.get(srv + url)
|
||||
val body = response.bodyAsText()
|
||||
|
||||
logger.debug { "call to $url" }
|
||||
|
||||
@@ -4,13 +4,13 @@ import net.keinekekse.version.Version
|
||||
|
||||
object VERSION: Version {
|
||||
override val version = "1.0.0"
|
||||
override val fullVersion = "1.0.0+build.226"
|
||||
override val fullVersion = "1.0.0+build.233"
|
||||
override val major = 1
|
||||
override val minor = 0
|
||||
override val patch = 0
|
||||
override val preRelease = ""
|
||||
override val buildMetadata = "build.226"
|
||||
override val buildDate = "2026-02-09T22:38:51.084372Z"
|
||||
override val buildMetadata = "build.233"
|
||||
override val buildDate = "2026-02-11T00:28:34.345856Z"
|
||||
override val displayName = "Books App"
|
||||
override val copyright = "Copyright (c) 2026 Hartmut Bauer"
|
||||
override val name = "books"
|
||||
|
||||
@@ -17,8 +17,8 @@ sealed interface AuthResult {
|
||||
data class Error(val code: String, val message: String, val details: String? = null) :
|
||||
AuthResult
|
||||
|
||||
data class Fatal(val code: String, val message: String, val details: String? = null) :
|
||||
AuthResult
|
||||
// data class Fatal(val code: String, val message: String, val details: String? = null) :
|
||||
// AuthResult
|
||||
}
|
||||
|
||||
@Single
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package net.keinekekse.app.auth
|
||||
|
||||
import com.auth0.jwt.JWT
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.call.*
|
||||
@@ -25,7 +24,6 @@ import net.keinekekse.app.service.TokenService
|
||||
import org.koin.core.annotation.Single
|
||||
import java.awt.Desktop
|
||||
import java.net.Socket
|
||||
import java.util.*
|
||||
import kotlin.use
|
||||
|
||||
private val logger = KotlinLogging.logger {}
|
||||
@@ -50,40 +48,43 @@ class Authenticator(
|
||||
|
||||
override fun close() = httpClient.close()
|
||||
|
||||
suspend fun authenticate(): AuthResult {
|
||||
// check if necessary
|
||||
val tokens = tokenService.getTokens()
|
||||
if (tokens != null) {
|
||||
val access = JWT.decode(tokens.accessToken)
|
||||
val refresh = JWT.decode(tokens.refreshToken)
|
||||
val now = Date()
|
||||
logger.debug { "Now: $now" }
|
||||
logger.debug { "Token expires at: ${access.expiresAt}" }
|
||||
logger.debug { "Token is expired: ${access.expiresAt.before(Date())}" }
|
||||
logger.debug { "Refresh token expires at: ${refresh.expiresAt}" }
|
||||
logger.debug { "Refresh token is expired: ${refresh.expiresAt.before(Date())}" }
|
||||
if (access.expiresAt.before(now)) {
|
||||
// access_token ungültig
|
||||
if (refresh.expiresAt.before(now)) {
|
||||
// refresh_token ungültig
|
||||
return doAuthentication()
|
||||
} else {
|
||||
val result = requestRefreshToken(tokens)
|
||||
return if (result != null) {
|
||||
AuthResult.Success()
|
||||
} else {
|
||||
doAuthentication()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return AuthResult.Success()
|
||||
}
|
||||
} else {
|
||||
return doAuthentication()
|
||||
}
|
||||
}
|
||||
// suspend fun authenticate(): AuthResult {
|
||||
// // check if necessary
|
||||
// logger.debug { "Authenticator: authenticate()" }
|
||||
// val tokens = tokenService.getTokens()
|
||||
// if (tokens != null) {
|
||||
// val access = JWT.decode(tokens.accessToken)
|
||||
// val refresh = JWT.decode(tokens.refreshToken)
|
||||
// val now = Date()
|
||||
// logger.debug { "Now: $now" }
|
||||
// logger.debug { "Token expires at: ${access.expiresAt}" }
|
||||
// logger.debug { "Token is expired: ${access.expiresAt.before(Date())}" }
|
||||
// logger.debug { "Refresh token expires at: ${refresh.expiresAt}" }
|
||||
// logger.debug { "Refresh token is expired: ${refresh.expiresAt.before(Date())}" }
|
||||
// if (access.expiresAt.before(now)) {
|
||||
// // access_token ungültig
|
||||
// if (refresh.expiresAt.before(now)) {
|
||||
// // refresh_token ungültig
|
||||
// return doAuthentication()
|
||||
// } else {
|
||||
// val result = requestRefreshToken(tokens)
|
||||
// return if (result != null) {
|
||||
// AuthResult.Success()
|
||||
// } else {
|
||||
// doAuthentication()
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// logger.debug { "Authenticator: Token is still valid." }
|
||||
// return AuthResult.Success()
|
||||
// }
|
||||
// } else {
|
||||
// return doAuthentication()
|
||||
// }
|
||||
// }
|
||||
|
||||
private suspend fun doAuthentication(): AuthResult {
|
||||
private suspend fun authenticate(): AuthResult {
|
||||
logger.debug { "Authenticator: doAuthentication()" }
|
||||
val config = AuthRun(keycloakConfig)
|
||||
if (!testServerConnection(config)) {
|
||||
return AuthResult.Error("ERR001", "Der Anmeldeserver ist nicht erreichbar.", "")
|
||||
@@ -94,7 +95,7 @@ class Authenticator(
|
||||
val port = server.engine.resolvedConnectors().first().port
|
||||
config.buildRedirectUri(port)
|
||||
if (!waitForServer(port)) {
|
||||
return AuthResult.Fatal(
|
||||
return AuthResult.Error(
|
||||
"ERR009",
|
||||
"Schwerer Fehler",
|
||||
"Der interne Webserver konnte nicht gestartet werden. Keine Anmeldung möglich."
|
||||
@@ -117,6 +118,7 @@ class Authenticator(
|
||||
}
|
||||
|
||||
suspend fun reAuthenticate(): BearerTokens? {
|
||||
logger.debug { "Authenticator: reAuthenticate()" }
|
||||
val authResult = authenticate()
|
||||
if (authResult is AuthResult.Success) {
|
||||
return tokenService.getTokens()
|
||||
@@ -152,12 +154,6 @@ class Authenticator(
|
||||
var error = ""
|
||||
var errorDetails = ""
|
||||
when (val err = result.first) {
|
||||
is AuthResult.Fatal -> {
|
||||
logger.debug { err.code }
|
||||
error = err.message ?: ""
|
||||
errorDetails = err.details ?: ""
|
||||
}
|
||||
|
||||
is AuthResult.Error -> {
|
||||
logger.debug { err.code }
|
||||
error = err.message ?: ""
|
||||
@@ -272,13 +268,6 @@ class Authenticator(
|
||||
status = HttpStatusCode.BadRequest
|
||||
}
|
||||
|
||||
is AuthResult.Fatal -> {
|
||||
text =
|
||||
"<html><body><h1>Schwerer Fehler.</h1><h2>${htmlEscape(authResult.message)}</h2><h3>${
|
||||
htmlEscape(authResult.details ?: "")
|
||||
}</h3></body></html>"
|
||||
status = HttpStatusCode.InternalServerError
|
||||
}
|
||||
}
|
||||
return Pair(text, status)
|
||||
}
|
||||
@@ -305,7 +294,7 @@ class Authenticator(
|
||||
)
|
||||
}
|
||||
if (request.queryParameters["state"] != csrfProtectionState) {
|
||||
return AuthResult.Fatal(
|
||||
return AuthResult.Error(
|
||||
"ERR005",
|
||||
"Schwerer Fehler",
|
||||
"CSRF-Fehler in der Kommunikation mit dem Anmeldeserver. Mögliche Ursachen: Netzwerkfehler, Serverfehler oder Malware."
|
||||
@@ -349,7 +338,7 @@ class Authenticator(
|
||||
|
||||
private suspend fun requestTokens(config: AuthRun, authCode: String?): AuthResult {
|
||||
if (authCode == null) {
|
||||
return AuthResult.Fatal(
|
||||
return AuthResult.Error(
|
||||
"ERR003",
|
||||
"Schwerer Fehler",
|
||||
"Der Anmeldeserver lieferte ungültige Daten. Mögliche Ursachen: Netzwerkfehler, Serverfehler oder Malware."
|
||||
@@ -387,7 +376,7 @@ class Authenticator(
|
||||
try {
|
||||
val errorResponse = response.body<TokenErrorResponse>()
|
||||
return Pair(
|
||||
AuthResult.Fatal(
|
||||
AuthResult.Error(
|
||||
"ERR010",
|
||||
errorResponse.error,
|
||||
"${errorResponse.errorDescription} (${response.status})"
|
||||
@@ -410,7 +399,7 @@ class Authenticator(
|
||||
} catch (e: Exception) {
|
||||
// Fehler beim JSON-Konvertieren
|
||||
return Pair(
|
||||
AuthResult.Fatal(
|
||||
AuthResult.Error(
|
||||
"ERR008",
|
||||
"Schwerer Fehler",
|
||||
"Ungültige Daten beim Abholen der Zugriffstoken. Mögliche Ursachen: Netzwerkfehler, Serverfehler oder Malware."
|
||||
|
||||
@@ -41,14 +41,14 @@ fun createHttpClient(authenticator: Authenticator): HttpClient {
|
||||
logger.debug { "Refresh token is expired: ${refresh.expiresAt.before(Date())}" }
|
||||
|
||||
if (refresh.expiresAt.before(now)) {
|
||||
logger.debug { "refresh token expired - HOW TO authenticate" }
|
||||
logger.debug { "HTTPClient: refresh token expired - reAuthenticate()" }
|
||||
authenticator.reAuthenticate()
|
||||
} else {
|
||||
logger.debug { "refresh token valid - request new tokens" }
|
||||
logger.debug { "HTTPClient: refresh token valid - requestRefreshToken()" }
|
||||
authenticator.requestRefreshToken(oldTokens)
|
||||
}
|
||||
} else {
|
||||
logger.debug { "no tokens - authenticate" }
|
||||
logger.debug { "HTTPClient: no tokens - reAuthenticate()" }
|
||||
authenticator.reAuthenticate()
|
||||
}
|
||||
}
|
||||
@@ -71,9 +71,9 @@ fun createHttpClient(authenticator: Authenticator): HttpClient {
|
||||
}
|
||||
|
||||
client.plugin(HttpSend).intercept { request ->
|
||||
logger.debug { "HTTPClient: intercept ${request.url}" }
|
||||
logger.debug { "HTTPCli: intercept ${request.url}" }
|
||||
val originalCall = execute(request)
|
||||
logger.debug { "HTTPClient: response status ${originalCall.response.status}" }
|
||||
logger.debug { "HTTPCli: response status ${originalCall.response.status}" }
|
||||
originalCall
|
||||
// val originalCall = execute(request)
|
||||
// if (originalCall.response.status.value !in 100..399) {
|
||||
|
||||
Reference in new issue
Block a user