Keycloak Dev Service
A service providing a Keycloak identity and access management server for development and testing purposes.
It configures the Spring Security libraries on the classpath automatically so that a Spring Boot application needs no OAuth2 configuration to get started:
-
Spring Security OAuth2 Client, to log users in.
-
Spring Security OAuth2 Resource Server, to validate access tokens.
Dependencies
First, you need to add the Dev Service dependency to your project.
-
Gradle
-
Maven
dependencies {
testAndDevelopmentOnly "io.arconia:arconia-dev-services-keycloak"
}
<dependency>
<groupId>io.arconia</groupId>
<artifactId>arconia-dev-services-keycloak</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
You can optionally include the Spring Boot DevTools dependency to enable live reload of your application during development.
-
Gradle
-
Maven
dependencies {
developmentOnly "org.springframework.boot:spring-boot-devtools"
}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
|
When you use the Spring Boot DevTools in your project and run the application in dev mode, Arconia keeps the Dev Services running while you make changes to your code instead of restarting them with the application. This allows you to see the changes in real-time without having to restart the Dev Services. Changes to the configuration of a Dev Service take effect once you stop and start the application again. |
Running the Application
When using the Arconia Dev Services, you can keep running your application as you normally would. The Dev Services will automatically start when you run your application.
-
Gradle
-
Maven
-
CLI
./gradlew bootRun
./mvnw spring-boot:run
arconia dev
Unlike the lower-level Testcontainers support in Spring Boot, Arconia doesn’t require special tasks to run your application when using Dev Services (./gradlew bootTestRun or ./mvnw spring-boot:test-run) nor requires you to define a separate @SpringBootApplication class for configuring Testcontainers.
|
Your integration tests based on @SpringBootTest will automatically use the Dev Services without any additional configuration.
Test slices (e.g. @DataJdbcTest) load only a subset of the auto-configurations, so they don’t pick up Dev Services automatically. You can import the Dev Service explicitly. For example, for the PostgreSQL Dev Service:
@DataJdbcTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
@Import(PostgresqlDevServicesAutoConfiguration.class)
class CustomerRepositoryTests {
...
}
The Dev Service imports a realm named arconia with a confidential client (id arconia-app, secret arconia-secret), and two users:
| Username | Password | Realm Roles |
|---|---|---|
|
|
|
|
|
|
The application logs show the URLs of the Keycloak Admin Console, where you can log in as arconia/arconia, and of the realm’s OpenID Connect issuer.
...i.a.d.s.startup : Dev Service 'keycloak' is ready in container <container-id> (quay.io/keycloak/keycloak, reuse-strategy: framework)
...i.a.d.s.startup : Dev Service 'keycloak' links: Keycloak Admin Console: http://localhost:<port>/admin, OpenID Connect Issuer: http://localhost:<port>/realms/arconia
To reach the server from your own code, inject the KeycloakConnectionDetails bean, which provides the server URL, the realm, and the issuer URI.
Logging Users In
When Spring Security’s OAuth2 Client is on the classpath, the Dev Service registers the client as a ClientRegistrationRepository bean, with registration id keycloak. Start the login at /oauth2/authorization/keycloak and sign in as one of the users above.
The client is configured with the client.* properties of the Dev Service, which also declare it in the realm, so the two always match. Your spring.security.oauth2.client properties are not used while the Dev Service is active, so you can keep your production configuration as it is.
The Dev Service’s ClientRegistrationRepository replaces any your application defines, so production configuration never gets in the way in dev. To keep your own instead, set arconia.dev.services.keycloak.client.enabled=false.
Protecting a Resource Server
When Spring Security’s OAuth2 Resource Server is on the classpath, the Dev Service configures it to validate the tokens issued by the realm, taking precedence over your own configuration. It sets these spring.security.oauth2.resourceserver.jwt properties:
-
issuer-uriandjwk-set-uri, pointing at the realm. -
jws-algorithms, set toRS256. -
audiences, cleared, since the tokens can’t carry the audience your production identity provider adds.
Properties describing how your application reads a token, such as authorities-claim-name, are left as you configured them. To use your own configuration instead, set arconia.dev.services.keycloak.resource-server.enabled=false.
The client allows the password grant, so you can get a token without a browser, using the issuer URI from the application logs:
-
HTTPie
-
curl
http --form <issuer-uri>/protocol/openid-connect/token \
grant_type=password client_id=arconia-app client_secret=arconia-secret \
username=isabella password=isabella
curl -s <issuer-uri>/protocol/openid-connect/token \
-d grant_type=password -d client_id=arconia-app -d client_secret=arconia-secret \
-d username=isabella -d password=isabella
Send the access_token from the response as Authorization: Bearer <token>.
Customizing Users and Roles
arconia:
dev:
services:
keycloak:
realm:
users:
alice:
password: alice-password
roles: [editor, reader]
bob:
password: bob-password
roles: [reader]
Usernames must be lower-case, and each user gets the email address <username>@arconia.io and its capitalized username as first and last name, since Keycloak requires them to issue tokens. The realm roles are the ones the users hold.
Configuring users replaces the default users.
Importing a Realm
For anything the generated realm doesn’t cover, import your own realm file from the classpath or the filesystem:
arconia:
dev:
services:
keycloak:
realm:
import-path: classpath:keycloak/my-realm.json
client:
id: my-app
secret: my-secret
The realm name is read from the file, which must also declare the client configured in client.*. The imported realm takes precedence: realm.name and realm.users only apply to the generated realm.
|
The generated realm is meant for a throwaway container: it doesn’t require SSL, accepts any redirect URI, and its credentials are published in this documentation. Don’t use a realm exported from it anywhere else. |
Sharing Keycloak Across Applications
By default, in dev mode, an application connects to a Keycloak Dev Service started by another application if available, or else starts its own. Give every application sharing it the same realm.* configuration, so the realm is the same whichever starts first. To always start a container for the application alone, set arconia.dev.services.keycloak.reuse-strategy to none. Learn more about Reusing Dev Services.
Calling Back into the Application
The login flow needs no special networking, since the browser performs the redirects. If Keycloak has to call your application, as for OpenID Connect Back-Channel Logout, list its port under host-accessible-ports, and use http://host.testcontainers.internal:<port> as the URL in the realm.
arconia:
dev:
services:
keycloak:
host-accessible-ports:
- 8080
Configuring the Dev Service
You can configure the Dev Service via configuration properties.
| Property | Default | Description |
|---|---|---|
|
|
Whether the dev service is enabled. |
|
|
Full name of the container image used in the dev service. |
|
|
Environment variables to set in the service. |
|
|
Additional hostnames the dev service container is reachable by on the shared network, next to the name of the dev service. Only applicable when the shared network is enabled. |
|
|
Fixed port for exposing the Keycloak HTTP port to the host. When it’s 0 (default), a random available port is assigned dynamically. |
|
|
Resources from the classpath or host filesystem to copy into the container. They can be files or directories that will be copied to the specified destination path inside the container at startup and are immutable (read-only). |
|
|
Strategy for reusing a running container for the dev service instead of starting a new one. Only applicable in dev mode. |
|
|
Maximum waiting time for the service to start, counted from the container start. |
|
|
Files or directories to mount from the host filesystem into the container. They are mounted at the specified destination path inside the container at startup and are mutable (read-write). Changes in either the host or the container will be immediately reflected in the other. |
|
|
Username of the Keycloak administrator, used to access the Admin Console. |
|
|
Password of the Keycloak administrator, used to access the Admin Console. |
|
|
Name of the generated realm. |
|
Path to a Keycloak realm file to import instead of generating a realm, resolved from the classpath or the host filesystem. It can be prefixed with |
|
|
|
Users of the generated realm, by username, each with a |
|
|
Whether the dev service declares the client in the generated realm and registers it with Spring Security. |
|
|
Identifier of the client. With an imported realm, the realm file must declare it. |
|
|
Secret of the client. |
|
|
Identifier of the Spring Security client registration, as used in the login URL |
|
|
Whether the resource server is configured to validate the tokens issued by the dev service. |
|
|
Ports on the host that Keycloak can reach from inside its container, such as for OpenID Connect Back-Channel Logout. |
You can enable/disable the Dev Service selectively for a specific application mode (development, test), relying on one of the profiles which are automatically configured by Arconia (see Profiles).
You can enable/disable the Dev Service for a specific test class by using the @TestPropertySource annotation or equivalent Spring testing utilities.
|