<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ronyeri Marinho</title>
    <description>The latest articles on DEV Community by Ronyeri Marinho (@ronyeri_marinho).</description>
    <link>https://dev.to/ronyeri_marinho</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3613644%2F8d7d13fd-7826-4333-9faf-cc3933ceaaf5.jpg</url>
      <title>DEV Community: Ronyeri Marinho</title>
      <link>https://dev.to/ronyeri_marinho</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ronyeri_marinho"/>
    <language>en</language>
    <item>
      <title>Keycloak + Spring: A Practical Guide to Securing Your APIs</title>
      <dc:creator>Ronyeri Marinho</dc:creator>
      <pubDate>Fri, 14 Aug 2026 02:31:46 +0000</pubDate>
      <link>https://dev.to/ronyeri_marinho/keycloak-spring-a-practical-guide-to-securing-your-apis-23j0</link>
      <guid>https://dev.to/ronyeri_marinho/keycloak-spring-a-practical-guide-to-securing-your-apis-23j0</guid>
      <description>&lt;p&gt;In modern applications, sooner or later you will face authentication and authorization: login, access control, tokens, roles, refresh tokens, password security… the list is long. Implementing all of this from scratch in every project is not only time-consuming, but also significantly increases the risk of security flaws and inconsistencies between systems. And this is exactly where &lt;a href="https://www.keycloak.org/" rel="noopener noreferrer"&gt;Keycloak&lt;/a&gt; comes in.&lt;/p&gt;

&lt;p&gt;Keycloak is an &lt;strong&gt;Identity and Access Management (IAM)&lt;/strong&gt; server that centralizes the entire authentication and authorization layer of your application, solving many common problems such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User management&lt;/li&gt;
&lt;li&gt;Login and logout&lt;/li&gt;
&lt;li&gt;Permissions and roles management&lt;/li&gt;
&lt;li&gt;Token issuance and validation (OAuth 2.0 / OpenID Connect)&lt;/li&gt;
&lt;li&gt;Integration with multiple applications using the same identity base&lt;/li&gt;
&lt;li&gt;Easy integration with social login providers (Google, GitHub, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;But why integrate it with Spring?&lt;/strong&gt; The answer lies in the combination of productivity and security. By integrating Keycloak with your API, you drastically reduce the amount of manual configuration required for these flows and &lt;strong&gt;delegate the responsibility to a specialized tool&lt;/strong&gt;, consuming only already validated JWT tokens in your application.&lt;/p&gt;

&lt;p&gt;In addition, centralizing the identity provider fits perfectly into microservices architectures, allowing multiple applications to share the same authentication system and enabling a true single sign-on experience for users.&lt;/p&gt;

&lt;p&gt;If you have ever struggled to build all this “by hand”, this guide will save you a significant amount of time.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;1. A brief tour of Keycloak’s main features&lt;/strong&gt;&lt;br&gt;
The goal here is to stay as hands-on as possible, but some concepts need to be introduced first so that the configuration makes sense and does not turn into a purely mechanical sequence of steps without context.&lt;/p&gt;

&lt;p&gt;Keycloak is an open-source &lt;strong&gt;Identity and Access Management (IAM)&lt;/strong&gt; platform that centralizes authentication (proving who the user is) and authorization (defining what they are allowed to do). Instead of each application implementing these responsibilities individually, they delegate this work to an identity server.&lt;/p&gt;

&lt;p&gt;Compatible with widely adopted standards such as &lt;a href="https://oauth.net/2/" rel="noopener noreferrer"&gt;OAuth 2.0&lt;/a&gt;, &lt;a href="https://openid.net/developers/how-connect-works/" rel="noopener noreferrer"&gt;OpenID Connect&lt;/a&gt;, and &lt;a href="https://auth0.com/pt/intro-to-iam/what-is-saml" rel="noopener noreferrer"&gt;SAML 2.0&lt;/a&gt;, Keycloak integrates easily with web applications, mobile apps, and REST APIs, regardless of the backend technology.&lt;/p&gt;

&lt;p&gt;In practice, it provides a built-in, ready-to-use, and fully customizable login page, along with several endpoints for authentication, token refresh, and logout. This allows applications to use either a standard login interface or purely API-based authentication flows, without being tied to a specific UI model.&lt;/p&gt;

&lt;p&gt;In addition, Keycloak offers a complete administrative interface for managing users, roles, and permissions, as well as integration with external identity providers such as Google, GitHub, and others.&lt;/p&gt;

&lt;p&gt;It is worth noting that Keycloak is a very robust and feature-rich tool. In this guide, we will focus on the essentials to get you started, but for a deeper understanding, the official documentation and complementary materials are excellent next steps.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;2. Running Keycloak and PostgreSQL with Docker&lt;/strong&gt;&lt;br&gt;
In this step, we will run &lt;strong&gt;Keycloak&lt;/strong&gt; inside &lt;strong&gt;Docker&lt;/strong&gt; containers and use &lt;strong&gt;PostgreSQL&lt;/strong&gt; as the database.&lt;/p&gt;

&lt;p&gt;To simplify the setup, we will use a &lt;code&gt;docker-compose.yml&lt;/code&gt; file containing only the essentials: the Keycloak service, the PostgreSQL service, and the configuration required for both to communicate properly.&lt;/p&gt;

&lt;p&gt;Below is the content of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;psql-db-demo&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_USER=postgres&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_PASSWORD=root&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pgdata:/var/lib/postgresql/data&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./init-keycloak-database.sql:/docker-entrypoint-initdb.d/init-keycloak-db.sql&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5432:5432&lt;/span&gt;
  &lt;span class="na"&gt;keycloak&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak-demo&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;quay.io/keycloak/keycloak:26.1.0&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;KC_BOOTSTRAP_ADMIN_USERNAME=admin&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;KC_BOOTSTRAP_ADMIN_PASSWORD=admin&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;KC_DB=postgres&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;KC_DB_URL=jdbc:postgresql://db:5432/keycloak&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;KC_DB_USERNAME=postgres&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;KC_DB_PASSWORD=root&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8181:8080&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;start-dev&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pg_isready"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-U"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;root"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pgdata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A PostgreSQL container, with user, password, exposed port, and a volume for data persistence;&lt;/li&gt;
&lt;li&gt;A Keycloak container, with a property configured to run in development mode (&lt;code&gt;start-dev&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;The necessary environment variables for Keycloak to automatically connect to the database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition, we include a database initialization script named &lt;code&gt;init-keycloak-database.sql&lt;/code&gt;, located at the root of the project, with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;keycloak&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="k"&gt;ENCODING&lt;/span&gt; &lt;span class="s1"&gt;'UTF8'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script ensures that the database used by Keycloak is automatically created when the PostgreSQL container starts for the first time.&lt;/p&gt;

&lt;p&gt;Pay special attention to the following variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;KC_DB_URL=jdbc:postgresql://db:5432/keycloak&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;db&lt;/code&gt; is the name of the PostgreSQL service defined in &lt;code&gt;docker-compose.yml&lt;/code&gt;, allowing the containers to find each other through Docker’s internal network without the need for manual IP configuration. With everything configured, simply run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In just a few seconds, your Keycloak and PostgreSQL instances will be up and running. I used simple usernames, passwords, and ports to make the example easier to follow, but feel free to customize them according to your needs.&lt;/p&gt;

&lt;p&gt;The Keycloak admin console will be available at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8181
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fssnkybd5wtpg2bvg6g8k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fssnkybd5wtpg2bvg6g8k.png" alt="Figure 1 — Keycloak Admin Console login screen." width="720" height="325"&gt;&lt;/a&gt; &lt;em&gt;Figure 1 — Keycloak Admin Console login screen.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To access it, use the username and password defined in the environment variables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;KC_BOOTSTRAP_ADMIN_USERNAME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;KC_BOOTSTRAP_ADMIN_PASSWORD&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;3. What needs to be configured in Keycloak?&lt;/strong&gt;&lt;br&gt;
After logging into the admin console, you will quickly notice the number of features and configuration options available. Let’s start with the most important concept of all: the &lt;strong&gt;Realm&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.1 Configuring the Realm&lt;/strong&gt;&lt;br&gt;
A &lt;strong&gt;realm&lt;/strong&gt; in Keycloak works as an isolated space that contains all the security configuration for one or more applications. It is inside a realm that we define users, credentials, roles, permissions, and all the other elements we will see next.&lt;/p&gt;

&lt;p&gt;Think of a realm as the &lt;em&gt;security universe&lt;/em&gt; of your project. Each realm is completely independent from the others, which makes it perfect for separating different projects or even environments such as development, staging, and production.&lt;/p&gt;

&lt;p&gt;At this point, you have probably noticed, in the top-left corner of the screen, the existence of a realm called &lt;strong&gt;master&lt;/strong&gt;. This realm is responsible for managing the Keycloak instance itself — including the user we are currently using to access the admin console — &lt;strong&gt;and for this reason, it is not recommended to use it for applications.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So let’s create our own.&lt;/p&gt;

&lt;p&gt;Click on &lt;strong&gt;Create realm&lt;/strong&gt;, choose a name that makes sense for your context, and complete the creation. With that, we now have our isolated security environment ready to receive the next configurations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F11j1yz5or8lnzjrd2jxz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F11j1yz5or8lnzjrd2jxz.png" alt="Figure 2 — Keycloak home screen with the master realm." width="720" height="310"&gt;&lt;/a&gt;&lt;em&gt;Figure 2 — Keycloak home screen with the master realm.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.2 Configuring the Client&lt;/strong&gt;&lt;br&gt;
A &lt;strong&gt;client&lt;/strong&gt; represents an application or service that will use Keycloak to authenticate users or validate access. In other words, &lt;strong&gt;the client is how your application &lt;em&gt;exists&lt;/em&gt; inside Keycloak&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is where we define:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how the application communicates with Keycloak,&lt;/li&gt;
&lt;li&gt;which authentication flows are enabled,&lt;/li&gt;
&lt;li&gt;and which security behaviors are allowed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the menu of your new realm, go to the &lt;strong&gt;Clients&lt;/strong&gt; section and create a new one. Some default clients will already exist and can be ignored.&lt;/p&gt;

&lt;p&gt;When creating it, fill in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Client ID:&lt;/strong&gt; the application identifier (in my case: keycloak-spring-xp)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name e Description:&lt;/strong&gt; optional, but useful for organization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0we62e6brvkjs9d0ajst.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0we62e6brvkjs9d0ajst.png" alt="Figure 3 — Client general configuration form." width="720" height="320"&gt;&lt;/a&gt;&lt;em&gt;Figure 3 — Client general configuration form.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.2.1 Client Types&lt;/strong&gt;&lt;br&gt;
The client type defines how the application authenticates with Keycloak:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Public:&lt;/strong&gt; used by applications that cannot securely store secrets, such as SPAs (e.g., Angular, React). &lt;strong&gt;This type does not require a client secret for authentication.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confidential (private):&lt;/strong&gt; used by applications running on secure servers, such as backends and APIs. &lt;strong&gt;These applications use a &lt;em&gt;client secret&lt;/em&gt; for authentication&lt;/strong&gt;, ensuring more secure communication with the identity server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since we are working with an API, we choose the &lt;strong&gt;Confidential&lt;/strong&gt; type.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.2.2 Authentication Flows&lt;/strong&gt;&lt;br&gt;
Keycloak offers several flows. The main ones are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standard Flow:&lt;/strong&gt; the most recommended flow from a security standpoint. It is used in applications that have a frontend and support redirection. In this model, the user does not receive the access token directly, but rather an authorization code, which is later exchanged for tokens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implicit Flow (not recommended):&lt;/strong&gt; similar to the Standard Flow, but returns the token directly after authentication. Because it exposes the token and introduces security risks, its use is discouraged.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct Access Grants:&lt;/strong&gt; recommended only for specific scenarios, like ours, where there is only an API and no frontend. This flow allows direct authentication via API using &lt;strong&gt;username and password&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service Accounts Roles:&lt;/strong&gt; used for system-to-system authentication, allowing a service to authenticate with another without the involvement of an end user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpk7026buefnr5jdm2doc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpk7026buefnr5jdm2doc.png" alt="Figure 4 — Client flow configuration form." width="720" height="329"&gt;&lt;/a&gt;&lt;em&gt;Figure 4 — Client flow configuration form.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Redirect configuration (URLs) is important for applications with a frontend, as it defines where Keycloak should redirect the user after events such as login, logout, and others. In our scenario, this configuration does not directly affect our authentication flow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frh0he7qfjhdxlmf8y2u4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frh0he7qfjhdxlmf8y2u4.png" alt="Figure 5 — Client login configuration form." width="720" height="326"&gt;&lt;/a&gt;&lt;em&gt;Figure 5 — Client login configuration form.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.3 Access Token Lifetime&lt;/strong&gt;&lt;br&gt;
At this point, we have created the &lt;strong&gt;isolated security space (Realm)&lt;/strong&gt; and the &lt;strong&gt;resource that represents our application&lt;/strong&gt; and defines how it authenticates with Keycloak (&lt;strong&gt;Client&lt;/strong&gt;). With this, we already have the essentials needed to integrate the Spring application and protect it with Keycloak.&lt;/p&gt;

&lt;p&gt;By default, the access token lifetime is &lt;strong&gt;5 minutes&lt;/strong&gt;. This value can be adjusted in the Realm settings by navigating to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Realm Settings → Tokens → Access Token Lifespan.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6fh4m5ww0cmw866h712k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6fh4m5ww0cmw866h712k.png" alt="Figure 6 — Token-related configuration screen." width="720" height="313"&gt;&lt;/a&gt;&lt;em&gt;Figure 6 — Token-related configuration screen.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;These are the minimum configurations required to start protecting your API. Keycloak offers many more features, and my recommendation is to explore the official documentation and &lt;a href="https://www.keycloak.org/guides" rel="noopener noreferrer"&gt;additional guides&lt;/a&gt; at your own pace.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;4. How does the API connect to Keycloak?&lt;/strong&gt;&lt;br&gt;
The first step was to create the API using Spring Initializr and add the required dependencies. For our context, the most important ones are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spring Security:&lt;/strong&gt; responsible for intercepting HTTP requests, protecting the application’s endpoints, and enforcing authentication and authorization rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OAuth2 Resource Server:&lt;/strong&gt; pallows the API to act as a Resource Server, trusting Keycloak as the Authorization Server and only validating the JWT tokens already issued by it, without implementing any custom authentication logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgh8h8icv4obzi7pk94sx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgh8h8icv4obzi7pk94sx.png" alt="Figure 7 — Project configuration screen in Spring Initializr." width="720" height="309"&gt;&lt;/a&gt;&lt;em&gt;Figure 7 — Project configuration screen in Spring Initializr.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To keep the example focused and simple, a basic Hello World endpoint was created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// imports omitted...&lt;/span&gt;

&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloWorldController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello-world"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;saysHelloWorld&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Hello World!"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, the response of this endpoint will be &lt;strong&gt;401 (Unauthorized)&lt;/strong&gt; if you try to access it. This behavior is expected and is part of the &lt;em&gt;secure by default&lt;/em&gt; principle (&lt;em&gt;deny by default&lt;/em&gt;): once Spring Security is added, all requests are automatically protected and require a valid token.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.1 Configuring &lt;code&gt;application.yml&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
For the API to accept tokens issued by Keycloak, we need to &lt;strong&gt;inform who the token issuer is&lt;/strong&gt; and &lt;strong&gt;how to validate its signature&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;issuer-uri&lt;/code&gt; property represents the entity that issues the tokens — in our case, the Keycloak Realm. With this information, Spring uses the OpenID Connect Discovery mechanism to automatically discover the endpoints and keys required for validation.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;jwk-set-uri&lt;/code&gt; property points directly to the endpoint that exposes the public keys used to verify the signature of JWT tokens.&lt;/p&gt;

&lt;p&gt;These configurations are done in the &lt;code&gt;application.yml&lt;/code&gt; (or &lt;code&gt;application.properties&lt;/code&gt;, depending on your project setup):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nl"&gt;spring:&lt;/span&gt;
  &lt;span class="nl"&gt;application:&lt;/span&gt;
    &lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="n"&gt;keycloak&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;spring&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;xp&lt;/span&gt;
  &lt;span class="nl"&gt;security:&lt;/span&gt;
    &lt;span class="nl"&gt;oauth2:&lt;/span&gt;
      &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nl"&gt;server:&lt;/span&gt;
        &lt;span class="nl"&gt;jwt:&lt;/span&gt;
          &lt;span class="n"&gt;issuer&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nl"&gt;uri:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;APP_OAUTH2_JWK_ISSUER_URI:http:&lt;/span&gt;&lt;span class="c1"&gt;//localhost:8181/realms/keycloak-demo}&lt;/span&gt;
          &lt;span class="n"&gt;jwk&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nl"&gt;uri:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="n"&gt;spring&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;security&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;oauth2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;jwt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;issuer&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="o"&gt;}/&lt;/span&gt;&lt;span class="n"&gt;protocol&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;openid&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;certs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to configure application security to behave as a &lt;strong&gt;stateless Resource Server&lt;/strong&gt;, requiring authentication for all requests and automatically validating JWT tokens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// imports omitted...&lt;/span&gt;

&lt;span class="nd"&gt;@Configuration&lt;/span&gt;
&lt;span class="nd"&gt;@EnableWebSecurity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SecurityConfiguration&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="nc"&gt;SecurityFilterChain&lt;/span&gt; &lt;span class="nf"&gt;filterChain&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpSecurity&lt;/span&gt; &lt;span class="n"&gt;httpSecurity&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;httpSecurity&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cors&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Customizer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withDefaults&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;csrf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;CsrfConfigurer:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;disable&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;authorizeHttpRequests&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authorizationManagerRequestMatcherRegistry&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                        &lt;span class="n"&gt;authorizationManagerRequestMatcherRegistry&lt;/span&gt;
                                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;anyRequest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;authenticated&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;oauth2ResourceServer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;httpSecurityOAuth2ResourceServerConfigurer&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                        &lt;span class="n"&gt;httpSecurityOAuth2ResourceServerConfigurer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;jwt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Customizer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withDefaults&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sessionManagement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;httpSecuritySessionManagementConfigurer&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                        &lt;span class="n"&gt;httpSecuritySessionManagementConfigurer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sessionCreationPolicy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SessionCreationPolicy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;STATELESS&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s it.&lt;/p&gt;

&lt;p&gt;At this point, Spring already trusts Keycloak as the identity provider, and every request must present a valid token to access any API endpoint. But a few questions remain:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;How do we authenticate?&lt;/em&gt;&lt;br&gt;
&lt;em&gt;With which user?&lt;/em&gt;&lt;br&gt;
&lt;em&gt;And what is the authentication endpoint?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is exactly what we will cover in the next chapter.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;5. Obtaining and using the access token&lt;/strong&gt;&lt;br&gt;
Inside the Keycloak instance, go to &lt;strong&gt;Realm Settings&lt;/strong&gt; and, in the &lt;strong&gt;General&lt;/strong&gt; tab, scroll down to the &lt;strong&gt;Endpoints&lt;/strong&gt; section. There you will find two important links — the first one is the &lt;strong&gt;OpenID Endpoint Configuration&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When you click this link, Keycloak will display a document containing all the URLs related to the OpenID Connect protocol. Among them is the endpoint responsible for issuing tokens. Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8181/realms/keycloak-demo/protocol/openid-connect/token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is through this endpoint that we authenticate with Keycloak and obtain the &lt;strong&gt;access token&lt;/strong&gt;, which will then be sent in requests to our Spring API.&lt;/p&gt;

&lt;p&gt;In our scenario, we use this endpoint to authenticate via &lt;strong&gt;Direct Access Grants&lt;/strong&gt;. The complete structure of the request in &lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt; can be seen below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyqnrfmfqw2k8k9f86ojc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyqnrfmfqw2k8k9f86ojc.png" alt="Figure 8 — HTTP request for authentication in Keycloak using Postman." width="720" height="194"&gt;&lt;/a&gt;&lt;em&gt;Figure 8 — HTTP request for authentication in Keycloak using Postman.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.1 Authentication request parameters&lt;/strong&gt;&lt;br&gt;
To authenticate against Keycloak’s token endpoint, we need to send a few parameters in the request body. Each one plays a specific role in the process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Grant Type:&lt;/strong&gt; defines which authentication flow is being used. In our case, we use password, which indicates the Direct Access Grants flow, where the user authenticates directly with username and password. This parameter is essential for Keycloak to understand how to process the request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client ID:&lt;/strong&gt; identifies which Client is requesting the token. It represents the application (our API, for example) previously registered in Keycloak and determines which flows, permissions, and security configurations will be applied.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Username:&lt;/strong&gt; the username registered in Keycloak that is attempting to authenticate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password:&lt;/strong&gt; the password of the specified user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client Secret:&lt;/strong&gt; used only when the client is of type confidential. It acts as the application’s own credential, ensuring that only authorized clients can request tokens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, these parameters allow Keycloak to validate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;who the user is,&lt;/li&gt;
&lt;li&gt;which application is requesting access,&lt;/li&gt;
&lt;li&gt;and which authentication flow is being used,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and then return an access token that can be used to access the protected API endpoints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.2 Creating a user in Keycloak&lt;/strong&gt;&lt;br&gt;
If we were using Keycloak’s default frontend, we could enable the registration screen and allow users to sign up themselves. Since we are only testing the API, we will create a user manually.&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Users&lt;/strong&gt; menu within the Realm, create a new user by filling in the fields as shown below. Although only the username is mandatory, Keycloak will require the remaining basic information during authentication.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp9zqtogiakjjssii28hr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp9zqtogiakjjssii28hr.png" alt="Figure 9 — User creation form in Keycloak." width="720" height="376"&gt;&lt;/a&gt;&lt;em&gt;Figure 9 — User creation form in Keycloak.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The password is defined in the &lt;strong&gt;Credentials&lt;/strong&gt; tab of the created user. After that, simply fill in the request in Postman with the user and client data, and the access token will be generated:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fak5sziomxg0x7xd932cm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fak5sziomxg0x7xd932cm.png" alt="Figure 10 — Authentication response from Keycloak." width="720" height="225"&gt;&lt;/a&gt;&lt;em&gt;Figure 10 — Authentication response from Keycloak.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Finally, copy the returned access token and send it in the &lt;code&gt;Authorization&lt;/code&gt; header of the request to the API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Authorization: Bearer &amp;lt;access_token&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Access will now be granted, and the endpoint will respond correctly:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4l7v80h4q8sox2ks8lop.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4l7v80h4q8sox2ks8lop.png" alt="Figure 11 — API request with a validated access token." width="720" height="299"&gt;&lt;/a&gt;&lt;em&gt;Figure 11 — API request with a validated access token.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This chapter now completes the full cycle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keycloak → token → protected API → validated access.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Final considerations&lt;/strong&gt;&lt;br&gt;
The journey was long, but if you made it this far, there is a very good chance that everything is already properly configured and working.&lt;/p&gt;

&lt;p&gt;Of course, this is not a complete guide to Keycloak. In fact, it represents only the beginning of a whole universe of possibilities that the tool offers. The goal here was to build a solid foundation so that you can move forward with more confidence and autonomy from this point on.&lt;/p&gt;

&lt;p&gt;I sincerely hope this content has been useful in some way in your daily work as a developer. If any questions arise, if you have suggestions, or if you simply want to exchange ideas about the topic, feel free to connect with me on &lt;strong&gt;&lt;a href="https://www.linkedin.com/in/ronyeri-marinho/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/strong&gt;. I also leave the link to the &lt;strong&gt;&lt;a href="https://github.com/ronyeri-marinho/keycloak-spring-xp" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/strong&gt; repository, where the example project used in this article is available.&lt;/p&gt;

&lt;p&gt;Thank you for following along until the end — and see you next time!&lt;/p&gt;

</description>
      <category>spring</category>
      <category>programming</category>
      <category>java</category>
      <category>api</category>
    </item>
  </channel>
</rss>
