I use keycloak with my Ktor kotlin backend app, wh...
# ktor
a
I use keycloak with my Ktor kotlin backend app, when user registers or log-in, keycloak captures this event and sends HTTP request with user data to my server. How would you like to secure this? How should I authenticate my listener requests?
Copy code
fun sendEvent(event: Event) {
    sendEventGeneric(endpoint, event).apply {
        logger.infov("${event.type} event sent to $endpoint")
    }
}

fun sendAdminEvent(event: AdminEvent) {
    sendEventGeneric(adminEndpoint, event).apply {
        logger.infov("Admin event sent to $adminEndpoint")
    }
}


private fun sendEventGeneric(url: String, event: Any) {
    val eventData = gson.toJson(event)
    try {
        val httpEntity: HttpEntity = ByteArrayEntity(eventData.toString().toByteArray())
        val httpPost = HttpPost(url)
        httpPost.setHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_KEEP_ALIVE)
        httpPost.setHeader("Content-Type", "application/json")
        httpPost.entity = httpEntity

        val response = HttpTools.executeCall(client, httpPost)
        HttpTools.stopOnError(response)
        logger.infov("Response: ", response.responseAsJsonObject)
        return
    } catch (e: Exception) {
        logger.error("Error while sending event: ${e.message}", e)
        throw e
    }
}
Server side:
Copy code
fun Route.authRoutes(userApi: UserApi) {
    post<AuthEventDto>("/register") { authEventDto ->
        val offersResponse = userApi.saveUser(authEventDto)
        call.respondSuccess(data = offersResponse)
    }


    post<AuthEventDto>("/login") { authEventDto ->
        val offersResponse = userApi.updateUser(authEventDto)
        call.respondSuccess(data = offersResponse)
    }

}
j
To secure the communication between Keycloak and your Ktor backend, you should ensure that the requests are authenticated and encrypted. Here are some steps you can take to secure your listener requests: 1. Use HTTPS: Ensure that the endpoint Keycloak is sending requests to is secured with HTTPS. This encrypts the data in transit, preventing man-in-the-middle attacks. 2. Client Authentication: Use client certificates for mutual TLS authentication. This way, only clients with the correct certificate can establish a connection to your server. 3. Secret Key or Token: Implement a shared secret key or token that is known to both Keycloak and your server. This token can be included in the header of the request and checked on the server side. 4. IP Whitelisting: Restrict the incoming requests to the IPs that you know Keycloak will use to send the events. 5. Webhook Signature Verification: If Keycloak supports sending a signature with each event, you can verify this signature on the server side to ensure that the event is indeed from Keycloak. Here's an example of how you might modify your Ktor server code to include a simple shared secret key check:
Copy code
kotlin
fun Route.authRoutes(userApi: UserApi, secretKey: String) {
    post<AuthEventDto>("/register") { authEventDto ->
        val authHeader = call.request.headers["Authorization"]
        if (authHeader != secretKey) {
            call.respond(HttpStatusCode.Unauthorized, "Invalid secret key")
            return@post
        }
        val offersResponse = userApi.saveUser(authEventDto)
        call.respondSuccess(data = offersResponse)
    }

    post<AuthEventDto>("/login") { authEventDto ->
        val authHeader = call.request.headers["Authorization"]
        if (authHeader != secretKey) {
            call.respond(HttpStatusCode.Unauthorized, "Invalid secret key")
            return@post
        }
        val offersResponse = userApi.updateUser(authEventDto)
        call.respondSuccess(data = offersResponse)
    }
}
In this example, the
Authorization
header is expected to contain the shared secret key. If it doesn't match the
secretKey
expected by the server, the request is rejected with an
Unauthorized
status code. Remember to keep the
secretKey
secure and not hard-coded in your source code. It should be stored in a secure configuration store or environment variable. For a more robust solution, you might consider implementing OAuth 2.0 client credentials grant between Keycloak and your server, where your server acts as an OAuth 2.0 resource server. This is more complex to set up but provides a standard and secure method for machine-to-machine authentication.
a
Can you describe in more detail what control of the event requests you have? Can you configure the client's certificate?