Skip to content

Commit 593142f

Browse files
committed
Migrate keycloak to latest
1 parent 0b94084 commit 593142f

File tree

2 files changed

+25
-25
lines changed

2 files changed

+25
-25
lines changed

build.sbt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ val kafkaVersion = "3.9.0"
1515
val activemqVersion = "5.18.5" // We are stuck with 5.x
1616
val artemisVersion = "2.39.0"
1717
val testContainersVersion = "1.20.4"
18-
val keycloakVersion = "26.0.8"
18+
val keycloakVersion = "26.3.2"
19+
val keycloakClientVersion = "26.0.6"
1920
val sttpVersion = "3.10.1"
2021
val influxdbVersion = "7.1.0"
2122
val awsClientVersion = "2.25.32"
@@ -128,15 +129,14 @@ libraryDependencies ++= Seq(
128129
"com.crobox.clickhouse" %% "client" % "1.2.6",
129130

130131
"org.opensearch" % "opensearch-testcontainers" % "2.1.2",
131-
"com.github.dasniko" % "testcontainers-keycloak" % "3.6.0",
132+
"com.github.dasniko" % "testcontainers-keycloak" % "3.8.0",
132133
"eu.rekawek.toxiproxy" % "toxiproxy-java" % "2.1.7",
133134
"org.testcontainers" % "junit-jupiter" % testContainersVersion % Test,
134135
"org.junit.jupiter" % "junit-jupiter-engine" % "5.9.2" % Test,
135136
"org.junit.jupiter" % "junit-jupiter-api" % "5.9.2" % Test,
136137

137138
"org.keycloak" % "keycloak-core" % keycloakVersion,
138-
"org.keycloak" % "keycloak-adapter-core" % "25.0.3",
139-
"org.keycloak" % "keycloak-admin-client" % "26.0.4",
139+
"org.keycloak" % "keycloak-admin-client" % keycloakClientVersion,
140140
"org.jboss.spec.javax.ws.rs" % "jboss-jaxrs-api_2.1_spec" % "2.0.2.Final",
141141

142142
"org.postgresql" % "postgresql" % "42.7.4",

src/main/scala/akkahttp/oidc/OIDCKeycloak.scala

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,9 @@ import org.apache.pekko.http.scaladsl.server.Directives.*
1111
import org.apache.pekko.http.scaladsl.server.{AuthenticationFailedRejection, Directive1, Route}
1212
import org.apache.pekko.http.scaladsl.unmarshalling.Unmarshal
1313
import org.keycloak.TokenVerifier
14-
import org.keycloak.adapters.KeycloakDeploymentBuilder
1514
import org.keycloak.admin.client.{CreatedResponseUtil, Keycloak}
1615
import org.keycloak.jose.jws.AlgorithmType
1716
import org.keycloak.representations.AccessToken
18-
import org.keycloak.representations.adapters.config.AdapterConfig
1917
import org.keycloak.representations.idm.{ClientRepresentation, CredentialRepresentation, UserRepresentation}
2018
import org.slf4j.{Logger, LoggerFactory}
2119

@@ -53,12 +51,15 @@ import scala.util.{Failure, Success}
5351
object OIDCKeycloak extends App with CORSHandler with JsonSupport {
5452
val logger: Logger = LoggerFactory.getLogger(this.getClass)
5553

54+
private val CLIENT_ID = "my-test-client"
55+
private val REALM_NAME = "test"
56+
5657
implicit val system: ActorSystem = ActorSystem()
5758
implicit val executionContext: ExecutionContextExecutor = system.dispatcher
5859

5960
def runKeycloak() = {
6061
// Pin to same version as "keycloakVersion" in build.sbt
61-
val keycloak = new KeycloakContainer("quay.io/keycloak/keycloak:26.1")
62+
val keycloak = new KeycloakContainer("quay.io/keycloak/keycloak:26.3")
6263
// Keycloak config taken from:
6364
// https://github.com/keycloak/keycloak/blob/main/examples/js-console/example-realm.json
6465
.withRealmImportFile("keycloak_realm_config.json")
@@ -81,7 +82,7 @@ object OIDCKeycloak extends App with CORSHandler with JsonSupport {
8182
def createTestUser(keycloakAdminClient: Keycloak): Unit = {
8283
val username = "test"
8384
val password = "test"
84-
val usersResource = keycloakAdminClient.realm("test").users()
85+
val usersResource = keycloakAdminClient.realm(REALM_NAME).users()
8586

8687
val user = new UserRepresentation()
8788
user.setEnabled(true)
@@ -106,13 +107,12 @@ object OIDCKeycloak extends App with CORSHandler with JsonSupport {
106107
userResource.resetPassword(passwordCred)
107108

108109
logger.info(s"User $username created with userId: $userId")
109-
logger.info(s"User $username/$password may sign in via: http://localhost:${keycloak.getHttpPort}/realms/test/account")
110+
logger.info(s"User $username/$password may sign in via: http://localhost:${keycloak.getHttpPort}/realms/$REALM_NAME/account")
110111
}
111112

112113
def createClientConfig(keycloakAdminClient: Keycloak): Unit = {
113-
val clientId = "my-test-client"
114114
val clientRepresentation = new ClientRepresentation()
115-
clientRepresentation.setClientId(clientId)
115+
clientRepresentation.setClientId(CLIENT_ID)
116116
clientRepresentation.setProtocol("openid-connect")
117117

118118
val redirectUriTestingOnly = new util.ArrayList[String]()
@@ -122,10 +122,10 @@ object OIDCKeycloak extends App with CORSHandler with JsonSupport {
122122
webOriginsTestingOnly.add("*")
123123
clientRepresentation.setWebOrigins(webOriginsTestingOnly)
124124

125-
val resp = keycloakAdminClient.realm("test").clients().create(clientRepresentation)
126-
logger.info(s"Successfully created client config for clientId: $clientId, response status: " + resp.getStatus)
125+
val resp = keycloakAdminClient.realm(REALM_NAME).clients().create(clientRepresentation)
126+
logger.info(s"Successfully created client config for clientId: $CLIENT_ID, response status: " + resp.getStatus)
127127

128-
val clients: util.List[ClientRepresentation] = keycloakAdminClient.realm("test").clients().findByClientId(clientId)
128+
val clients: util.List[ClientRepresentation] = keycloakAdminClient.realm(REALM_NAME).clients().findByClientId(CLIENT_ID)
129129
logger.info(s"Successfully read ClientRepresentation for clientId: ${clients.get(0).getClientId}")
130130
}
131131

@@ -136,12 +136,13 @@ object OIDCKeycloak extends App with CORSHandler with JsonSupport {
136136
}
137137

138138
def runBackendServer(keycloak: KeycloakContainer): Unit = {
139-
val config = new AdapterConfig()
140-
config.setAuthServerUrl(keycloak.getAuthServerUrl)
141-
config.setRealm("test")
142-
config.setResource("my-test-client")
143-
val keycloakDeployment = KeycloakDeploymentBuilder.build(config)
144-
logger.info("Dynamic authServerBaseUrl: " + keycloakDeployment.getAuthServerBaseUrl)
139+
val authServerUrl = keycloak.getAuthServerUrl
140+
val realmUrl = s"$authServerUrl/realms/$REALM_NAME"
141+
val jwksUrl = s"$realmUrl/protocol/openid-connect/certs"
142+
143+
logger.info(s"Realm URL: $realmUrl")
144+
logger.info(s"JSON Web Key Set (JWKS) URL: $jwksUrl")
145+
logger.info(s"Client ID: $CLIENT_ID")
145146

146147

147148
def generateKey(keyData: KeyData): PublicKey = {
@@ -153,7 +154,7 @@ object OIDCKeycloak extends App with CORSHandler with JsonSupport {
153154
}
154155

155156
val publicKeys: Future[Map[String, PublicKey]] =
156-
Http().singleRequest(HttpRequest(uri = keycloakDeployment.getJwksUrl)).flatMap(response => {
157+
Http().singleRequest(HttpRequest(uri = jwksUrl)).flatMap(response => {
157158
val json = Unmarshal(response).to[String]
158159
val keys = json.map { jsonString =>
159160
decode[Keys](jsonString) match {
@@ -164,9 +165,8 @@ object OIDCKeycloak extends App with CORSHandler with JsonSupport {
164165
keys.map(_.keys.map(k => (k.kid, generateKey(k))).toMap)
165166
})
166167

167-
168-
// Alternative:
169-
// https://doc.akka.io/docs/akka-http/current/routing-dsl/directives/security-directives/authenticateOAuth2.html
168+
// Alternative impl to:
169+
// https://pekko.apache.org/docs/pekko-http/current/routing-dsl/directives/security-directives/authenticateOAuth2.html
170170
def authenticate: Directive1[AccessToken] =
171171
extractCredentials.flatMap {
172172
case Some(OAuth2BearerToken(token)) =>
@@ -205,7 +205,7 @@ object OIDCKeycloak extends App with CORSHandler with JsonSupport {
205205
get {
206206
authenticate { token =>
207207
// To have "real data": Read 'UserRepresentation' from Keycloak via the admin client and then strip down
208-
val usersOrig = adminClient.realm("test").users().list().asScala
208+
val usersOrig = adminClient.realm(REALM_NAME).users().list().asScala
209209
val usersBasic = UsersKeycloak(usersOrig.collect(each => UserKeycloak(Option(each.getFirstName), Option(each.getLastName), Option(each.getEmail))).toSeq)
210210
complete(HttpResponse(StatusCodes.OK, entity = usersBasic.asJson.noSpaces))
211211
}

0 commit comments

Comments
 (0)