<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Keycloak Archives - Codersee blog- Kotlin on the backend</title>
	<atom:link href="https://blog.codersee.com/tag/keycloak/feed/" rel="self" type="application/rss+xml" />
	<link></link>
	<description>Kotlin &#38; Backend Tutorials - Learn Through Practice.</description>
	<lastBuildDate>Wed, 16 Apr 2025 04:50:29 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://blog.codersee.com/wp-content/uploads/2025/04/cropped-codersee_logo_circle_2-32x32.png</url>
	<title>Keycloak Archives - Codersee blog- Kotlin on the backend</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>How to Set Up Keycloak Admin Client with Spring Boot and Kotlin?</title>
		<link>https://blog.codersee.com/how-to-set-up-keycloak-admin-client-with-spring-boot-and-kotlin/</link>
					<comments>https://blog.codersee.com/how-to-set-up-keycloak-admin-client-with-spring-boot-and-kotlin/#comments</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Mon, 23 Nov 2020 08:00:20 +0000</pubDate>
				<category><![CDATA[Spring]]></category>
		<category><![CDATA[Keycloak]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<guid isPermaLink="false">http://codersee.com/?p=1653</guid>

					<description><![CDATA[<p>In today's article, I would like to show you how to set up the Keycloak admin client with Spring Boot and Kotlin.</p>
<p>The post <a href="https://blog.codersee.com/how-to-set-up-keycloak-admin-client-with-spring-boot-and-kotlin/">How to Set Up Keycloak Admin Client with Spring Boot and Kotlin?</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="h-1-introduction">1. Introduction</h2>



<p>Hello and welcome to the second article in a series showing how to <strong>secure Spring Boot applications with Keycloak</strong>. In the previous tutorial, we&#8217;ve learned how to set up the <strong>Keycloak server</strong> and secure <strong>Spring Boot REST API</strong> endpoints with it. I highly encourage you to <a href="http://codersee.com/keycloak-with-spring-boot-and-kotlin-introduction/" target="_blank" rel="noopener noreferrer">check it out</a> before starting this guide.</p>



<p>In today&#8217;s article, I would like to show you how to set up the<strong> Keycloak admin client with Spring Boot and Kotlin</strong>. I will walk you step by step through the process of setting roles, groups, and users with the<a href="https://mvnrepository.com/artifact/org.keycloak/keycloak-admin-client" target="_blank" rel="noopener noreferrer"> Keycloak Admin REST Client</a>.</p>



<h2 class="wp-block-heading" id="h-2-run-keycloak-server">2. Run Keycloak Server</h2>



<p>Just like in the previous article, we will bootstrap the Keycloak server using the <em><strong>docker run</strong></em> command:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">docker run -p 8090:8080 -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin quay.io/keycloak/keycloak:11.0.3
</pre>



<p>After the server is ready, let&#8217;s head to the admin console under the <em><strong>http://localhost:8090/auth/admin</strong></em> address and create a new realm called <strong>example</strong> (I have described it thoroughly in the <a href="http://codersee.com/keycloak-with-spring-boot-and-kotlin-introduction/" target="_blank" rel="noopener noreferrer">3rd paragraph of the previous post</a>).</p>



<h2 class="wp-block-heading" id="h-2-create-and-configure-admin-client">2. Create and Configure Admin Client</h2>


<p>[elementor-template id=&#8221;9007393&#8243;]</p>



<h3 class="wp-block-heading" id="h-2-1-create-new-client">2.1. Create New Client</h3>



<p>As the next step, let&#8217;s create the admin client within our realm. Let&#8217;s hit the <strong><em>Create</em></strong> button on the <em><strong>Clients</strong></em> page and name and set up a new client called <strong>admin-spring-boot</strong>:</p>



<figure class="wp-block-image"><img fetchpriority="high" decoding="async" width="709" height="354" src="http://blog.codersee.com/wp-content/uploads/2020/11/1-create-client.png" alt="Image is a screenshot showing how to create an admin client inside the Keycloak Admin Console." class="wp-image-1655" srcset="https://blog.codersee.com/wp-content/uploads/2020/11/1-create-client.png 709w, https://blog.codersee.com/wp-content/uploads/2020/11/1-create-client-300x150.png 300w" sizes="(max-width: 709px) 100vw, 709px" /></figure>



<h3 class="wp-block-heading" id="h-2-2-configure-access-type">2.2. Configure Access Type</h3>



<p>On the next page, let&#8217;s configure our client as follows:</p>



<figure class="wp-block-image"><img decoding="async" width="692" height="672" src="http://blog.codersee.com/wp-content/uploads/2020/11/2-set-access-type-1.png" alt="The image shows a screenshot from Keycloak Admin Console showing how to set Access Type for new client." class="wp-image-1657" srcset="https://blog.codersee.com/wp-content/uploads/2020/11/2-set-access-type-1.png 692w, https://blog.codersee.com/wp-content/uploads/2020/11/2-set-access-type-1-300x291.png 300w" sizes="(max-width: 692px) 100vw, 692px" /></figure>



<p>Please notice two things here. <strong>Confidential access-type</strong> indicates, that we will need a secret to authenticate this client against the Keycloak server. On the other hand, the <strong>service accounts enabled</strong> set to <strong>ON</strong> allows us to generate a token dedicated to this client.</p>



<h3 class="wp-block-heading" id="h-2-3-obtain-client-credentials">2.3. Obtain Client Credentials</h3>



<p>Nextly, let&#8217;s go to the Credentials tab and copy the secret:</p>



<p><img decoding="async" class="alignnone size-full wp-image-1660" src="http://blog.codersee.com/wp-content/uploads/2020/11/3-get-credentials.png" alt="Image contains a screenshot from Keycloak client app showing how to get credentials for Keycloak client." width="635" height="332" srcset="https://blog.codersee.com/wp-content/uploads/2020/11/3-get-credentials.png 635w, https://blog.codersee.com/wp-content/uploads/2020/11/3-get-credentials-300x157.png 300w" sizes="(max-width: 635px) 100vw, 635px" /><br>The above value will be used later in our Spring Boot application to set up the connection.</p>



<h3 class="wp-block-heading" id="h-2-4-assign-roles-to-service-account">2.4. Assign Roles to Service Account</h3>



<p>The last step, we will need to do in Keycloak is to assign the appropriate roles to the service account associated with our client. Let&#8217;s switch to the <strong>Service Account Roles</strong> panel and add the following roles from the<strong> realm-management</strong> set:</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="875" height="528" src="http://blog.codersee.com/wp-content/uploads/2020/11/4-assign-roles-sa.png" alt="The image is a screenshot from Keycloak panel showing how to assign roles to the service account" class="wp-image-1661" srcset="https://blog.codersee.com/wp-content/uploads/2020/11/4-assign-roles-sa.png 875w, https://blog.codersee.com/wp-content/uploads/2020/11/4-assign-roles-sa-300x181.png 300w, https://blog.codersee.com/wp-content/uploads/2020/11/4-assign-roles-sa-768x463.png 768w" sizes="auto, (max-width: 875px) 100vw, 875px" /></figure>



<p>Please keep in mind, that I have chosen these roles for demonstrational purposes. In real-life scenarios, we should always follow the <strong>Least Privilege</strong> rule.</p>



<figure class="wp-block-image aligncenter"><a href="https://codersee.com/newsletter/"><img loading="lazy" decoding="async" width="1024" height="576" src="http://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-1024x576.png" alt="Image shows two ebooks people can get for free after joining newsletter" class="wp-image-3002956" srcset="https://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-1024x576.png 1024w, https://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-300x169.png 300w, https://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-768x432.png 768w, https://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter.png 1440w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></figure>



<h2 class="wp-block-heading" id="h-3-set-up-spring-boot-application">3. Set Up Spring Boot Application</h2>



<p>With all the above being done, we can start creating our <strong>Spring Boot</strong> project. In this paragraph, we will see how to set it up from scratch and interact with the Keycloak service using the <strong>admin client library</strong>.</p>



<h3 class="wp-block-heading" id="h-3-1-imports">3.1. Imports</h3>



<p>As always, let&#8217;s start with the imports. Let&#8217;s head to the <em><strong>build.gradle.kts</strong></em> and add the following lines:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="groovy" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">implementation("org.keycloak:keycloak-spring-boot-starter:11.0.3")
implementation("org.keycloak:keycloak-admin-client:11.0.3")
implementation("org.springframework.boot:spring-boot-starter-web")
</pre>



<p>Technically, we do not need the web starter to interact with Keycloak. However, in this guide, we will create a few endpoints to better illustrate the fully-working system.</p>



<h3 class="wp-block-heading" id="h-3-2-configure-application-properties">3.2. Configure Application Properties</h3>



<p>As the next step, let&#8217;s go to the <em><strong>application.yaml</strong></em> file and put the following configuration there:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="yaml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">keycloak:
  realm: example
  resource: admin-spring-boot
  auth-server-url: http://localhost:8090/auth
  credentials:
    secret: &lt;KEYCLOAK-CLIENT-CREDENTIALS>
</pre>



<p>The secret should be set with the value from paragraph <strong>2.3</strong>.</p>



<h3 class="wp-block-heading" id="h-3-3-create-keycloak-client-config">3.3. Create Keycloak Client Config</h3>



<p>Nextly, let&#8217;s create the <strong>KeycloakClientConfig</strong> class:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Configuration
class KeycloakClientConfig(
    @Value("\${keycloak.credentials.secret}")
    private val secretKey: String,
    @Value("\${keycloak.resource}")
    private val clientId: String,
    @Value("\${keycloak.auth-server-url}")
    private val authUrl: String,
    @Value("\${keycloak.realm}")
    private val realm: String
) {

    @Bean
    fun keycloak(): Keycloak {
        return KeycloakBuilder.builder()
            .grantType(CLIENT_CREDENTIALS)
            .serverUrl(authUrl)
            .realm(realm)
            .clientId(clientId)
            .clientSecret(secretKey)
            .build()
    }
}
</pre>



<p>In this configuration, we use the <strong>KeycloakBuilder</strong> class to customize the <strong>RESTEasy client</strong> used to communicate with the Keycloak server. Additionally, we annotate the <em><strong>keycloak()</strong> </em>function with <em><strong>@Bean</strong></em> annotation, so that we will be able to inject this bean into our services later.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Thanks to the<strong> @Value</strong> annotation, the content of the <strong>application.yaml</strong> file is injected into our properties</p>
</blockquote>



<h3 class="wp-block-heading" id="h-3-4-manage-roles">3.4. Manage Roles</h3>



<p>This time, instead of defining services first, and then implementing the controllers&#8217; layer and testing, we will focus on Keycloak groups, roles, and users separately.</p>



<p>As the first step, let&#8217;s create the <strong>RoleService</strong> class with three functions:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Service
class RoleService(
    private val keycloak: Keycloak,
    @Value("\${keycloak.realm}")
    private val realm: String
) {
    fun create(name: String) {
        val role = RoleRepresentation()
        role.name = name

        keycloak
            .realm(realm)
            .roles()
            .create(role)
    }

    fun findAll(): List&lt;RoleRepresentation> =
        keycloak
            .realm(realm)
            .roles()
            .list()

    fun findByName(roleName: String): RoleRepresentation =
        keycloak
            .realm(realm)
            .roles()
            .get(roleName)
            .toRepresentation()
}
</pre>



<p>As you might have noticed- the first function will <strong>create a new role</strong> within the Keycloak instance. The second one will be used to <strong>obtain already created roles</strong>. On the other hand, the third one will be used by the <strong>UserController</strong> later, to obtain the <strong>RoleRepresentation</strong>.</p>



<p>Let&#8217;s create a controller class, which will utilize these functions:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@RestController
@RequestMapping("/api/role")
class RoleController(
    private val roleService: RoleService
) {
    @GetMapping
    fun findAll() =
        roleService.findAll()

    @PostMapping
    fun createRole(@RequestParam name: String) =
        roleService.create(name)
}
</pre>



<p>Finally, let&#8217;s build and run the application. To test if everything is working as expected, let&#8217;s use the <strong>cURL</strong> command to create a new role:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl --location --request POST 'localhost:8080/api/role?name=ROLE_EXAMPLE'
</pre>



<p>To verify, we can either utilize the second endpoint:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl 'localhost:8080/api/role'
</pre>



<p>Or use the <strong>Keycloak Admin Console</strong>:<br><img loading="lazy" decoding="async" class="alignnone size-full wp-image-1664" src="http://blog.codersee.com/wp-content/uploads/2020/11/5-roles-keycloak-admin-console.png" alt="The image shows the view of all roles in Keycloak from Keycloak Admin Console." width="651" height="276" srcset="https://blog.codersee.com/wp-content/uploads/2020/11/5-roles-keycloak-admin-console.png 651w, https://blog.codersee.com/wp-content/uploads/2020/11/5-roles-keycloak-admin-console-300x127.png 300w" sizes="auto, (max-width: 651px) 100vw, 651px" /></p>



<h3 class="wp-block-heading" id="h-3-5-manage-groups">3.5. Manage Groups</h3>



<p>Similarly, let&#8217;s implement the logic responsible for Keycloak group management. Just like in the previous paragraph, let&#8217;s start with the service layer:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Service
class GroupService(
    private val keycloak: Keycloak,
    @Value("\${keycloak.realm}")
    private val realm: String
) {
    fun create(name: String) {
        val group = GroupRepresentation()
        group.name = name

        keycloak
            .realm(realm)
            .groups()
            .add(group)
    }

    fun findAll(): List&lt;GroupRepresentation> =
        keycloak
            .realm(realm)
            .groups()
            .groups()
}
</pre>



<p>This service looks almost the same, as the <strong>RoleService</strong>. You might have also noticed that in the second function, we&#8217;ve used <em><strong>groups()</strong></em> method two times. It&#8217;s not a typo- the creators decided to use this name in <strong>GroupsResource</strong> class instead of the <em><strong>list() </strong></em>(unfortunately).</p>



<p>We might want to test the group&#8217;s functionality as well.&nbsp; Let&#8217;s implement the <strong>GroupController</strong>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@RestController
@RequestMapping("/api/group")
class GroupController(
    private val groupService: GroupService
) {
    @GetMapping
    fun findAll() =
        groupService.findAll()

    @PostMapping
    fun createGroup(@RequestParam name: String) =
        groupService.create(name)
}
</pre>



<p>Similarly, after the following command:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl --location --request POST 'localhost:8080/api/group?name=example_group'
</pre>



<p>We should see the newly created group:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl 'localhost:8080/api/group'
</pre>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="528" height="233" src="http://blog.codersee.com/wp-content/uploads/2020/11/6-created-group.png" alt="Image shows the screenshot from Keycloak Admin Console showing created groups." class="wp-image-1665" srcset="https://blog.codersee.com/wp-content/uploads/2020/11/6-created-group.png 528w, https://blog.codersee.com/wp-content/uploads/2020/11/6-created-group-300x132.png 300w" sizes="auto, (max-width: 528px) 100vw, 528px" /></figure>



<h3 class="wp-block-heading" id="h-3-6-manage-users">3.6. Manage Users</h3>



<p>As the last example, let&#8217;s create the logic responsible for user management. Let&#8217;s start by adding the <strong>UserRequest</strong> class to the project:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">class UserRequest(
    val username: String,
    val password: String
)
</pre>



<p>This simple class will be used to create new users later.</p>



<p>With that being done, let&#8217;s create the <strong>UserService</strong> class:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Service
class UserService(
    private val keycloak: Keycloak,
    @Value("\${keycloak.realm}")
    private val realm: String
) {
    fun findAll(): List&lt;UserRepresentation> =
        keycloak
            .realm(realm)
            .users()
            .list()

    fun findByUsername(username: String): List&lt;UserRepresentation> =
        keycloak
            .realm(realm)
            .users()
            .search(username)

    fun findById(id: String): UserRepresentation =
        keycloak
            .realm(realm)
            .users()
            .get(id)
            .toRepresentation()

    fun assignToGroup(userId: String, groupId: String) {
        keycloak
            .realm(realm)
            .users()
            .get(userId)
            .joinGroup(groupId)
    }

    fun assignRole(userId: String, roleRepresentation: RoleRepresentation) {
        keycloak
            .realm(realm)
            .users()
            .get(userId)
            .roles()
            .realmLevel()
            .add(listOf(roleRepresentation))
    }

    fun create(request: UserRequest): Response {
        val password = preparePasswordRepresentation(request.password)
        val user = prepareUserRepresentation(request, password)

        return keycloak
            .realm(realm)
            .users()
            .create(user)
    }

    private fun preparePasswordRepresentation(
        password: String
    ): CredentialRepresentation {
        val cR = CredentialRepresentation()
        cR.isTemporary = false
        cR.type = CredentialRepresentation.PASSWORD
        cR.value = password
        return cR
    }

    private fun prepareUserRepresentation(
        request: UserRequest,
        cR: CredentialRepresentation
    ): UserRepresentation {
        val newUser = UserRepresentation()
        newUser.username = request.username
        newUser.credentials = listOf(cR)
        newUser.isEnabled = true
        return newUser
    }
}
</pre>



<p>The responsibility of the three first functions is pretty simple- they allow us to find all users, or alternatively to fetch specific ones based on the username or the identifier.</p>



<p>Let&#8217;s analyze the logic responsible for <strong>users creation</strong>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun create(request: UserRequest): Response {
    val password = preparePasswordRepresentation(request.password)
    val user = prepareUserRepresentation(request, password)

    return keycloak
        .realm(realm)
        .users()
        .create(user)
}

private fun preparePasswordRepresentation(
    password: String
): CredentialRepresentation {
    val cR = CredentialRepresentation()
    cR.isTemporary = false
    cR.type = CredentialRepresentation.PASSWORD
    cR.value = password
    return cR
}

private fun prepareUserRepresentation(
    request: UserRequest,
    cR: CredentialRepresentation
): UserRepresentation {
    val newUser = UserRepresentation()
    newUser.username = request.username
    newUser.credentials = listOf(cR)
    newUser.isEnabled = true
    return newUser
}
</pre>



<p>The public function <em><strong>create()</strong></em> takes the <strong>userRequest</strong> as a parameter and prepares <strong>CredentialRepresentation</strong> first- setting the password value and making it non-temporary. Nextly,&nbsp; this password representation is passed to the<em><strong> prepareUserRepresentation()</strong></em> function and assigned to the <strong>UserRepresentation</strong>. In this function, the desired user is annotated as enabled as well.</p>



<p>The <strong>UserService</strong> class contains two additional functions as well: <em><strong>assignToGroup</strong> </em>and <em><strong>assignRole</strong></em>, which might be used to add groups/roles to the user.</p>



<p>Finally, let&#8217;s create the <strong>UserController</strong> function:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@RestController
@RequestMapping("/api/user")
class UserController(
    private val userService: UserService,
    private val roleService: RoleService
) {
    @GetMapping
    fun findAll() =
        userService.findAll()

    @GetMapping("/{id}")
    fun findById(@PathVariable id: String) =
        userService.findById(id)

    @GetMapping("/username/{username}")
    fun findByUsername(@PathVariable username: String) =
        userService.findByUsername(username)

    @PostMapping
    fun create(@RequestBody userRequest: UserRequest): ResponseEntity&lt;URI> {
        val response = userService.create(userRequest)

        if (response.status != 201)
            throw RuntimeException("User was not created")

        return ResponseEntity.created(response.location).build()
    }

    @PostMapping("/{userId}/group/{groupId}")
    fun assignToGroup(
        @PathVariable userId: String,
        @PathVariable groupId: String
    ) {
        userService.assignToGroup(userId, groupId)
    }

    @PostMapping("/{userId}/role/{roleName}")
    fun assignRole(
        @PathVariable userId: String,
        @PathVariable roleName: String
    ) {
        val role = roleService.findByName(roleName)

        userService.assignRole(userId, role)
    }
}
</pre>



<p>In order to test if everything is good, let&#8217;s start by creating a new user:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl -i --location --request POST 'localhost:8080/api/user' \
--header 'Content-Type: application/json' \
--data-raw '{
    "username": "user",
    "password": "password"
}'
</pre>



<p>Please notice one, <strong>very important thing</strong>. When the Keycloak user is created successfully,<strong> the response body will be empty</strong>. If we would like to obtain the user ID, for instance, to save it in our database, we would need to extract it from the <strong>Location header of the response </strong>(that&#8217;s why we&#8217;ve used the <strong>-i flag</strong> in our command):</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Location: http://localhost:8090/auth/admin/realms/example/users/[CREATED-USER-ID]
</pre>



<p>Additionally, we can check the Users page in the <strong>Keycloak Admin Console</strong>:</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="712" height="241" src="http://blog.codersee.com/wp-content/uploads/2020/11/7-user-view.png" alt="The image shows the Users page inside the Keycloak Admin Console." class="wp-image-1666" srcset="https://blog.codersee.com/wp-content/uploads/2020/11/7-user-view.png 712w, https://blog.codersee.com/wp-content/uploads/2020/11/7-user-view-300x102.png 300w" sizes="auto, (max-width: 712px) 100vw, 712px" /></figure>



<p>From now on, we should be able to sign in using the provided username and password combination. Nevertheless,&nbsp; we would have to set up a new client for this purpose (I&#8217;ve described this process in<a href="http://codersee.com/keycloak-with-spring-boot-and-kotlin-introduction/" target="_blank" rel="noopener noreferrer"> this tutorial</a>).</p>



<p>Following, let&#8217;s add our user to the group:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl --location --request POST 'localhost:8080/api/user/35520db6-3df0-4693-b8b1-f9abc09d7b86/group/7eb8db7a-7693-4df3-b682-d955da67d453'
</pre>



<p>After the operation finishes, we should see the following in the user <em><strong>Groups</strong></em> tab:<br><img loading="lazy" decoding="async" class="alignnone size-full wp-image-1667" src="http://blog.codersee.com/wp-content/uploads/2020/11/8-user-groups.png" alt="the image shows the view of user groups." width="559" height="311" srcset="https://blog.codersee.com/wp-content/uploads/2020/11/8-user-groups.png 559w, https://blog.codersee.com/wp-content/uploads/2020/11/8-user-groups-300x167.png 300w" sizes="auto, (max-width: 559px) 100vw, 559px" /></p>



<p>As the last step, let&#8217;s assign the <strong>ROLE_EXAMPLE</strong> to our user:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl --location --request POST 'localhost:8080/api/user/35520db6-3df0-4693-b8b1-f9abc09d7b86/role/ROLE_EXAMPLE'
</pre>



<p>Similarly, when we head to the<em><strong> Role Mappings</strong> </em>tab, we should see that the role has been assigned successfully:<br><img loading="lazy" decoding="async" class="alignnone size-full wp-image-1668" src="http://blog.codersee.com/wp-content/uploads/2020/11/9-user-roles.png" alt="The image shows user assigned roles in Keycloak Admin Console." width="918" height="352" srcset="https://blog.codersee.com/wp-content/uploads/2020/11/9-user-roles.png 918w, https://blog.codersee.com/wp-content/uploads/2020/11/9-user-roles-300x115.png 300w, https://blog.codersee.com/wp-content/uploads/2020/11/9-user-roles-768x294.png 768w" sizes="auto, (max-width: 918px) 100vw, 918px" /></p>



<h2 class="wp-block-heading" id="h-4-summary">4. Summary</h2>



<p>And that would be all for this article. We&#8217;ve learned today, how to set up the <strong>Keycloak Admin Client with Spring Boot and Kotlin</strong>. I hope you really liked the two most recent articles related to this topic. If you would like me to cover some special aspects of the <strong>Keycloak/Spring Boot integration</strong>, please let me know in the comments, or by the <strong><a href="http://codersee.com/contact/" target="_blank" rel="noopener noreferrer">contact</a> </strong>form.</p>



<p>Also, for the source code, please refer to this <a href="https://github.com/codersee-blog/kotlin-spring-boot-keycloak-admin" target="_blank" rel="noopener noreferrer">GitHub repository</a>.</p>
<p>The post <a href="https://blog.codersee.com/how-to-set-up-keycloak-admin-client-with-spring-boot-and-kotlin/">How to Set Up Keycloak Admin Client with Spring Boot and Kotlin?</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/how-to-set-up-keycloak-admin-client-with-spring-boot-and-kotlin/feed/</wfw:commentRss>
			<slash:comments>14</slash:comments>
		
		
			</item>
		<item>
		<title>Keycloak with Spring Boot and Kotlin- Introduction</title>
		<link>https://blog.codersee.com/keycloak-with-spring-boot-and-kotlin-introduction/</link>
					<comments>https://blog.codersee.com/keycloak-with-spring-boot-and-kotlin-introduction/#comments</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Thu, 19 Nov 2020 07:56:30 +0000</pubDate>
				<category><![CDATA[Spring]]></category>
		<category><![CDATA[JWT]]></category>
		<category><![CDATA[Keycloak]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<guid isPermaLink="false">http://codersee.com/?p=1625</guid>

					<description><![CDATA[<p>In this step by step guide, I will show you how to set up a Keycloak server and connect to it using Spring Boot and Kotlin.</p>
<p>The post <a href="https://blog.codersee.com/keycloak-with-spring-boot-and-kotlin-introduction/">Keycloak with Spring Boot and Kotlin- Introduction</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2 class="article-heading-introduction">1. Introduction</h2>
<p>Hello and welcome to the first article in a series showing how to use <strong>Keycloak with Spring Boot</strong> applications.</p>
<p>In this step by step guide, I will show you how to set up a <strong>Keycloak server</strong> and connect to it using <strong>Spring Boot and Kotlin</strong>. Additionally, you will learn how to secure <strong>REST</strong> endpoints with <strong>Keycloak</strong> combined with the <em><strong>@PreAuthorize</strong></em> annotation.</p>
<h2 class="article-heading-main">2. What is Keycloak?</h2>
<p>According to the <a href="https://www.keycloak.org/" target="_blank" rel="noopener noreferrer">documentation</a>, <strong>Keycloak</strong> is the Open Source Identity and Access Management solution. In simple terms, it allows us to add authentication and secure our applications with minimum effort. With Keycloak, plenty of topics, like user storage, authentication, and many many more, are available out of the box.</p>
<p>In addition, the <em><strong>keycloak-spring-boot-starter</strong></em> makes all the process of configuration really straightforward, thus saving our precious time.</p>
<h2 class="article-heading-main">3. Install Keycloak</h2>
<p>As the first step in this tutorial will be the installation of the <strong>Keycloak server</strong>. There are plenty of ways to achieve that, and I highly recommend you visit the <a href="https://www.keycloak.org/getting-started" target="_blank" rel="noopener noreferrer">official documentation</a> to choose the most suitable one for you.</p>
<p>To keep this tutorial as simple as possible, I&#8217;ve decided to deploy it as a Docker container. If you have the Docker environment already installed on your machine, all you need to do is to type the following command:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">docker run -p 8090:8080 -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin -e DB_VENDOR=h2 quay.io/keycloak/keycloak:11.0.3
</pre>
<p>With this command, we create a container using the <strong>keycloak:11.0.3</strong> image and publish it&#8217;s <strong>8080</strong> port to the <strong>8180</strong> port of the host. Additionally, we pass the <strong>KEYCLOAK_USER</strong> and <strong>KEYCLOAK_PASSWORD</strong> environment variables for the initial<em> admin user (root)</em>.</p>
<p>However, the simplicity comes with its own restrictions. By default, the Keycloak server runs using the<strong> H2 database</strong>, so all the configuration will be lost after the container is terminated. To prevent that we can export a realm configuration to the <strong>JSON file</strong> (I will show you this step later).</p>
<p>&nbsp;</p>
<p><a href="https://codersee.com/newsletter/"><img loading="lazy" decoding="async" class="aligncenter wp-image-3002956 size-large" src="http://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-1024x576.png" alt="Image shows two ebooks people can get for free after joining newsletter" width="800" height="419" /></a></p>
<p>&nbsp;</p>
<h2 class="article-heading-main">3. Configure Keycloak</h2>
<p>Once the Keycloak server is<em> up and running</em>, let&#8217;s head to the administration console. All we need to do is to type <em><strong>http://localhost:8090</strong></em> in the address bar of the browser. We should see the following page:</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-1629 size-full" title="The image shows a home page of the Keycloak server" src="http://blog.codersee.com/wp-content/uploads/2020/11/1-home-view.png" alt="The image shows a home page of the Keycloak server" width="1129" height="659" srcset="https://blog.codersee.com/wp-content/uploads/2020/11/1-home-view.png 1129w, https://blog.codersee.com/wp-content/uploads/2020/11/1-home-view-300x175.png 300w, https://blog.codersee.com/wp-content/uploads/2020/11/1-home-view-1024x598.png 1024w, https://blog.codersee.com/wp-content/uploads/2020/11/1-home-view-768x448.png 768w" sizes="auto, (max-width: 1129px) 100vw, 1129px" /></p>
<p>On the next page, we should see the login form. Let&#8217;s enter the credentials for the <em>admin</em> account we&#8217;ve already configured:</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-1630 size-full" title="The image shows the Keycloak login page" src="http://blog.codersee.com/wp-content/uploads/2020/11/2-login-page.png" alt="The image shows the Keycloak login page" width="666" height="587" srcset="https://blog.codersee.com/wp-content/uploads/2020/11/2-login-page.png 666w, https://blog.codersee.com/wp-content/uploads/2020/11/2-login-page-300x264.png 300w" sizes="auto, (max-width: 666px) 100vw, 666px" /></p>
<h3 class="article-heading-sub">3.1. Create Realm</h3>
<p>After we&#8217;ve authenticated successfully, we can create a new <strong>realm</strong>. In Keycloak,<strong> realms</strong> manage a set of users, credentials, roles, and groups. Each user belongs to and logs into some realm. Moreover, they are completely isolated environments, which introduce an additional layer of security to our application.</p>
<p>As the next step, let&#8217;s navigate to the <em><strong>Add realm</strong></em> button:</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-1631 size-full" title="The image shows the location of the Create Realm button inside the Keycloak admin panel" src="http://blog.codersee.com/wp-content/uploads/2020/11/3-create-realm-button.png" alt="The image shows the location of the Create Realm button inside the Keycloak admin panel" width="647" height="508" srcset="https://blog.codersee.com/wp-content/uploads/2020/11/3-create-realm-button.png 647w, https://blog.codersee.com/wp-content/uploads/2020/11/3-create-realm-button-300x236.png 300w" sizes="auto, (max-width: 647px) 100vw, 647px" /></p>
<p>On the next page, let&#8217;s type the name for the new realm and hit the create button:</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-1632 size-full" title="Image shows the add Keycloak realm form" src="http://blog.codersee.com/wp-content/uploads/2020/11/4-add-realm-form.png" alt="Image shows the add Keycloak realm form" width="541" height="245" srcset="https://blog.codersee.com/wp-content/uploads/2020/11/4-add-realm-form.png 541w, https://blog.codersee.com/wp-content/uploads/2020/11/4-add-realm-form-300x136.png 300w" sizes="auto, (max-width: 541px) 100vw, 541px" /></p>
<p>Please remember this name, we will need it to<strong> configure the Spring Boot application later</strong>.</p>
<h3 class="article-heading-sub">3.2. Create Roles</h3>
<p>Nextly, let&#8217;s create some <strong>roles</strong> for our future users. Let&#8217;s navigate to the <em><strong>Roles</strong></em> tab on the left sidebar. On the next screen, we should see the list of already existing roles. Let&#8217;s click the <em><strong>Add role</strong> </em>button placed in the right part of the screen.</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-1633 size-full" title="Image shows Keycloak realm roles list" src="http://blog.codersee.com/wp-content/uploads/2020/11/5-roles-list.png" alt="Image shows Keycloak realm roles list" width="905" height="273" srcset="https://blog.codersee.com/wp-content/uploads/2020/11/5-roles-list.png 905w, https://blog.codersee.com/wp-content/uploads/2020/11/5-roles-list-300x90.png 300w, https://blog.codersee.com/wp-content/uploads/2020/11/5-roles-list-768x232.png 768w" sizes="auto, (max-width: 905px) 100vw, 905px" /></p>
<p>Firstly, let&#8217;s type the <strong>ROLE_USER</strong> and click save. After that, let&#8217;s add the <strong>ROLE_ADMIN </strong>which will be a composite role associated with the previous one:</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-1634 size-full" title="Image shows how to set up the admin role in Keycloak" src="http://blog.codersee.com/wp-content/uploads/2020/11/6-role-admin-setup.png" alt="Image shows how to set up the admin role in Keycloak" width="1144" height="648" srcset="https://blog.codersee.com/wp-content/uploads/2020/11/6-role-admin-setup.png 1144w, https://blog.codersee.com/wp-content/uploads/2020/11/6-role-admin-setup-300x170.png 300w, https://blog.codersee.com/wp-content/uploads/2020/11/6-role-admin-setup-1024x580.png 1024w, https://blog.codersee.com/wp-content/uploads/2020/11/6-role-admin-setup-768x435.png 768w" sizes="auto, (max-width: 1144px) 100vw, 1144px" /></p>
<p>Please notice <strong>two important things</strong> here:</p>
<ul>
<li>A <strong>composite role</strong> combines together multiple roles. To put it simply- a user with the <strong>ROLE_ADMIN</strong> will be able to access all endpoints restricted for <strong>ROLE_USER</strong>. It helps to clean the architecture of the security a lot, however, please be careful not to introduce an additional gap to the system when using it.</li>
<li>Both roles have been prefixed with a <em><strong>ROLE_</strong></em>. Spring Security will automatically prefix <em><strong>hasRole</strong></em> checks with the same value.</li>
</ul>
<h3 class="article-heading-sub">3.3. Create Clients</h3>
<p>As the next step, let&#8217;s create <strong>clients</strong>. Most often, <strong>Keycloak clients</strong> are the applications and services we would like to secure, or which are obtaining tokens to access other applications.</p>
<p>For the purpose of this tutorial, we will create two clients. Let&#8217;s start the one, we will use to configure our<strong> Spring Boot</strong> application:</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-1635 size-full" title="Image shows how to set up the Keycloak client for the Spring Boot application" src="http://blog.codersee.com/wp-content/uploads/2020/11/7-create-spring-boot-client.png" alt="Image shows how to set up the Keycloak client for the Spring Boot application" width="1014" height="767" srcset="https://blog.codersee.com/wp-content/uploads/2020/11/7-create-spring-boot-client.png 1014w, https://blog.codersee.com/wp-content/uploads/2020/11/7-create-spring-boot-client-300x227.png 300w, https://blog.codersee.com/wp-content/uploads/2020/11/7-create-spring-boot-client-768x581.png 768w" sizes="auto, (max-width: 1014px) 100vw, 1014px" /></p>
<p>Please notice, that we&#8217;ve set the Access Type to <em><strong>bearer-only</strong></em>. It means, that our application will not initiate a login, it&#8217;ll just pass received tokens to verify them with Keycloak. Most of the time, you will set this type of access type for the <strong>Spring Boot backends</strong>, which don&#8217;t need to call another microservices secured by Keycloak, or to use a service account.</p>
<p>Nextly, let&#8217;s create the <em><strong>public</strong></em> client, which will be used to get access tokens on behalf of the user:</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-1636 " title="Image shows how to create public client in Keycloak" src="http://blog.codersee.com/wp-content/uploads/2020/11/8-create-public-client.png" alt="Image shows how to create public client in Keycloak" width="1065" height="758" srcset="https://blog.codersee.com/wp-content/uploads/2020/11/8-create-public-client.png 1065w, https://blog.codersee.com/wp-content/uploads/2020/11/8-create-public-client-300x214.png 300w, https://blog.codersee.com/wp-content/uploads/2020/11/8-create-public-client-1024x729.png 1024w, https://blog.codersee.com/wp-content/uploads/2020/11/8-create-public-client-768x547.png 768w" sizes="auto, (max-width: 1065px) 100vw, 1065px" /></p>
<p>This time, only the<strong> Direct Access Grants flow</strong> is <strong>enabled</strong> and the Access Type is set to <strong>public</strong>.<br />
The <strong>public</strong> access type is used for clients, which cannot keep secrets securely, like frontend applications for instance.</p>
<h3 class="article-heading-sub">3.4. Create Users</h3>
<p>As the last step for Keycloak configuration, we will create users. Let&#8217;s head to the <em><strong>Users</strong></em> page, and create a user names <em><strong>example_user</strong></em>:</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-1637 size-full" title="Image shows how to craete a new user in Keycloak" src="http://blog.codersee.com/wp-content/uploads/2020/11/9-add-user-form.png" alt="Image shows how to craete a new user in Keycloak" width="1049" height="543" srcset="https://blog.codersee.com/wp-content/uploads/2020/11/9-add-user-form.png 1049w, https://blog.codersee.com/wp-content/uploads/2020/11/9-add-user-form-300x155.png 300w, https://blog.codersee.com/wp-content/uploads/2020/11/9-add-user-form-1024x530.png 1024w, https://blog.codersee.com/wp-content/uploads/2020/11/9-add-user-form-768x398.png 768w" sizes="auto, (max-width: 1049px) 100vw, 1049px" /></p>
<p>Let&#8217;s mark him <strong>enabled</strong> and his email <strong>verified </strong>as well.</p>
<p>The next important thing we need to do is to <strong>set the password</strong> for the user in the <em><strong>Credentials</strong></em> tab:</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-1638 size-full" title="Image shows how to set the password for user in Keycloak" src="http://blog.codersee.com/wp-content/uploads/2020/11/10-set-password-for-user.png" alt="Image shows how to set the password for user in Keycloak" width="820" height="482" srcset="https://blog.codersee.com/wp-content/uploads/2020/11/10-set-password-for-user.png 820w, https://blog.codersee.com/wp-content/uploads/2020/11/10-set-password-for-user-300x176.png 300w, https://blog.codersee.com/wp-content/uploads/2020/11/10-set-password-for-user-768x451.png 768w" sizes="auto, (max-width: 820px) 100vw, 820px" /></p>
<p>And as the last step, let&#8217;s add the desired role to the user. Let&#8217;s select the <strong>ROLE_USER</strong> and hit the<em><strong> Add selected</strong></em> button, just like below:</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-1639 size-full" title="Image shows how to add the role to the user in Keycloak" src="http://blog.codersee.com/wp-content/uploads/2020/11/11-Add-role-to-user.png" alt="Image shows how to add the role to the user in Keycloak" width="890" height="424" srcset="https://blog.codersee.com/wp-content/uploads/2020/11/11-Add-role-to-user.png 890w, https://blog.codersee.com/wp-content/uploads/2020/11/11-Add-role-to-user-300x143.png 300w, https://blog.codersee.com/wp-content/uploads/2020/11/11-Add-role-to-user-768x366.png 768w" sizes="auto, (max-width: 890px) 100vw, 890px" /></p>
<p>Please repeat the same steps for the <strong>admin user</strong>. The only difference will be the selection of the <strong>ROLE_ADMIN</strong> instead.</p>
<h2 class="article-heading-main">4. Configure Spring Boot App</h2>
<h3 class="article-heading-sub">4.1. Imports</h3>
<p>As usual, we will start the configuration of our Spring Boot application with imports. Let&#8217;s head to the <em><strong>build.gradle.kts</strong></em> file and add the following dependencies:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="groovy">implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.keycloak:keycloak-spring-boot-starter:11.0.3")
implementation("org.springframework.boot:spring-boot-starter-security:2.4.0")
</pre>
<h3 class="article-heading-sub">4.2. Configure Application Properties</h3>
<p>As the next step, let&#8217;s open the <em><strong>/resources/application.yaml</strong></em> file and paste the following config:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="yaml">keycloak:
  realm: example
  resource: spring-boot
  bearer-only: true
  auth-server-url: http://localhost:8090/auth
</pre>
<p>Let&#8217;s see what each of the above configs mean:</p>
<ul>
<li><strong>realm</strong>&#8211; as the name suggests, it is the name of the created realm,</li>
<li><strong>resource</strong>&#8211; matches the name of the created client,</li>
<li><strong>bearer-only</strong>&#8211; this flag indicates, that our service has been created as a bearer-only, by default it is set to false,</li>
<li><strong>auth-server-url</strong>&#8211; it is the URL of our Keycloak server</li>
</ul>
<h3 class="article-heading-sub">4.3. Configure Spring Security</h3>
<p>As the next step, let&#8217;s create the <strong>WebSecurityConfig</strong> class:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@KeycloakConfiguration
@EnableGlobalMethodSecurity(prePostEnabled = true)
class WebSecurityConfig : KeycloakWebSecurityConfigurerAdapter() {

    @Autowired
    fun configureGlobal(auth: AuthenticationManagerBuilder) {
        val keycloakAuthenticationProvider = keycloakAuthenticationProvider()
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(SimpleAuthorityMapper())
        auth.authenticationProvider(keycloakAuthenticationProvider)
    }

    @Bean
    override fun sessionAuthenticationStrategy(): SessionAuthenticationStrategy {
        return NullAuthenticatedSessionStrategy()
    }

    @Bean
    fun keycloakConfigResolver(): KeycloakConfigResolver {
        return KeycloakSpringBootConfigResolver()
    }

    override fun configure(http: HttpSecurity) {
        super.configure(http)
        http
            .authorizeRequests()
            .antMatchers("/api/public/**").permitAll()
            .anyRequest().fullyAuthenticated()
    }
}
</pre>
<p>I totally understand that this config might be a little overwhelming, so let&#8217;s just break it down:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@KeycloakConfiguration
@EnableGlobalMethodSecurity(prePostEnabled = true)
class WebSecurityConfig : KeycloakWebSecurityConfigurerAdapter()
</pre>
<p><em><strong>@KeycloakConfiguration</strong></em> annotation provides a Keycloak based Spring security configuration. It is used with classes extending <strong>KeycloakWebSecurityConfigurerAdapter</strong>, just like in our case. The <em><strong>@EnableGlobalMethodSecurity</strong></em> with <em><strong>prePostEnabled </strong></em>set to true enables the Spring Security global method security and <em><strong>@PreAuthorize</strong></em> annotations.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Autowired
fun configureGlobal(auth: AuthenticationManagerBuilder) {
    val keycloakAuthenticationProvider = keycloakAuthenticationProvider()
    keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(SimpleAuthorityMapper())
    auth.authenticationProvider(keycloakAuthenticationProvider)
}</pre>
<p>The <em><strong>configureGlobal</strong></em> function takes the <strong>AuthenticationManagerBuilder</strong> bean as a parameter and sets the <strong>KeycloakAuthenticationProvider</strong> as the authentication provider. Additionally, it sets the <strong>SimpleAuthorityMapper</strong> as the authority mapper.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Bean
override fun sessionAuthenticationStrategy(): SessionAuthenticationStrategy {
    return NullAuthenticatedSessionStrategy()
}

@Bean
fun keycloakConfigResolver(): KeycloakConfigResolver {
    return KeycloakSpringBootConfigResolver()
}</pre>
<p>With these two functions, we declare the <strong>KeycloakSpringBootConfigResolver</strong> bean and that we do not want the session to be created, as well.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">override fun configure(http: HttpSecurity) {
    super.configure(http)
    http
        .authorizeRequests()
        .antMatchers("/api/public/**").permitAll()
        .anyRequest().fullyAuthenticated()
}</pre>
<p>Finally, we override the configure method derived from the <strong>KeycloakWebSecurityConfigurerAdapter</strong>. With this config, all requests except the <em><strong>/api/public/**</strong></em> need to be fully authenticated.</p>
<h2 class="article-heading-main">5. Create Rest Controller</h2>
<p>With all the above being done, we can create an <strong>ExampleController</strong> class, which consists of three endpoints:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@RestController
@RequestMapping("/api")
class ExampleController {

    @GetMapping("/example")
    @PreAuthorize("hasRole('USER')")
    fun getUserInfo(): ResponseEntity&lt;String&gt; =
        ResponseEntity.ok("User info")

    @GetMapping("/example/admin")
    @PreAuthorize("hasRole('ADMIN')")
    fun getAdminInfo(): ResponseEntity&lt;String&gt; =
        ResponseEntity.ok("Admin info")

    @GetMapping("/public/example")
    fun getPublicInfo(): ResponseEntity&lt;String&gt; =
        ResponseEntity.ok("Public info")
}
</pre>
<p>The first two of them require the user to either have the <strong>ROLE_USER</strong> or <strong>ROLE_ADMIN</strong> assigned to him. Nevertheless, the last endpoint permits anyone, even without the token.</p>
<h2 class="article-heading-main">6. Testing with cURL</h2>
<p>Finally, we can validate if everything is working as expected.</p>
<p>Let&#8217;s start by obtaining the access token:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">curl --location --request POST 'http://localhost:8090/auth/realms/example/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'username=some_user' \
--data-urlencode 'password=password' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'client_id=public'
</pre>
<p>As the result we should get the following JSON object:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">{
    "access_token":  [TOKEN],
    "expires_in": 300,
    "refresh_expires_in": 1800,
    "refresh_token": [TOKEN],
    "token_type": "bearer",
    "not-before-policy": 0,
    "session_state": "7981f2e1-4e50-44d6-ac81-2ba508a59680",
    "scope": "email profile"
}
</pre>
<p>The most important part of the response for us is the <strong>JWT access token</strong>, which we will use to query our endpoints. If you would like to see, how many informations are encoded within it, I highly recommend to visit <a href="http://jwt.io/" target="_blank" rel="noopener noreferrer">jwt.io</a> and check it out.</p>
<p>To fully validate our endpoints, let&#8217;s query all of them with different tokens and see if results <span class="VIiyi" lang="en"><span class="JLqJ4b ChMk0b" data-language-for-alternatives="en" data-language-to-translate-into="pl" data-phrase-index="0">correspond to our assumptions</span></span>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">curl --location --request GET 'http://localhost:8080/api/example' \
--header 'Authorization: Bearer [TOKEN]' 

## Result for both admin and user:
User info

curl --location --request GET 'http://localhost:8080/api/example/admin' \
--header 'Authorization: Bearer [TOKEN]' 

## Result only for admin:
Admin info

curl --location --request GET 'http://localhost:8080/api/public/example' 

## Result for any request:
Public info
</pre>
<h2 class="article-heading-main">7. Export Realm Configuration</h2>
<p>In the beginning, I&#8217;ve promised to show you how to export the realm configuration. The only thing we need to do is to click the <strong><em>Export</em></strong> tab on the left sidebar and nextly the <em><strong>Export</strong></em> button on the next page:</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-1641 size-full" title="Image shows how to export the Keycloak configuration to a JSON file." src="http://blog.codersee.com/wp-content/uploads/2020/11/12-export-configuration.png" alt="Image shows how to export the Keycloak configuration to a JSON file." width="390" height="200" srcset="https://blog.codersee.com/wp-content/uploads/2020/11/12-export-configuration.png 390w, https://blog.codersee.com/wp-content/uploads/2020/11/12-export-configuration-300x154.png 300w" sizes="auto, (max-width: 390px) 100vw, 390px" /></p>
<p>As a result, a <a href="https://github.com/codersee-blog/kotlin-spring-boot-keycloak-intro/blob/master/src/main/resources/realm-export.json" target="_blank" rel="noopener noreferrer">JSON file</a> will be generated.</p>
<p>If we would like to import the configuration, all we need is to head to the <em><strong>Import</strong> </em>section:</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-1642 size-full" title="Image shows how to import the realm config from a JSON file" src="http://blog.codersee.com/wp-content/uploads/2020/11/13-import-config.png" alt="Image shows how to import the realm config from a JSON file" width="585" height="364" srcset="https://blog.codersee.com/wp-content/uploads/2020/11/13-import-config.png 585w, https://blog.codersee.com/wp-content/uploads/2020/11/13-import-config-300x187.png 300w" sizes="auto, (max-width: 585px) 100vw, 585px" /></p>
<h2 class="article-heading-main">8. Conclusion</h2>
<p>And that would be all for this article. We&#8217;ve covered step by step, how to set up the <strong>Keycloak server with Spring Boot and Kotlin</strong>. Moreover, we&#8217;ve had a chance to see how to use it to secure <strong>Spring Boot REST endpoints with</strong> <em><strong>@PreAuthorize</strong></em>.</p>
<p>As I have written in the beginning, this is the first article in a series. If you would like me to cover some special aspects of the <strong>Keycloak/Spring Boot integration</strong>, please let me know in the comments, or by the <strong><a href="https://codersee.com/contact/" target="_blank" rel="noopener noreferrer">contact</a> </strong>form.</p>
<p>Also, to see the fully working project, please refer to this <a href="https://github.com/codersee-blog/kotlin-spring-boot-keycloak-intro" target="_blank" rel="noopener noreferrer">GitHub repository</a>.</p>
<p>The post <a href="https://blog.codersee.com/keycloak-with-spring-boot-and-kotlin-introduction/">Keycloak with Spring Boot and Kotlin- Introduction</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/keycloak-with-spring-boot-and-kotlin-introduction/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 

Served from: blog.codersee.com @ 2026-05-13 22:10:50 by W3 Total Cache
-->