1. Mastering API Access Control with Keycloak

This article builds upon the insightful work of my colleague Sebastian Fulga; you can read his original piece here
 

When it comes to securing Web APIs, authentication is only half the story. Authorization — knowing who can do what is just as critical, yet often receives less attention. In this follow-up, I’ll walk you through implementing fine-grained authorization using Keycloak, empowering you to manage access precisely. Whether you're just starting with authorization or already working with Keycloak, these steps are clear, practical, and designed with developers in mind. 
 

Keycloak is an open-source single sign-on (SSO) solution licensed under Apache-2.0. Its implementation is publicly available on GitHub. Keycloak offers robust features including strong authentication, user management, identity provider integration, user federation, session control, event logging, and more. It’s highly configurable and has detailed official documentation on its website

Keycloak_Logo

2. Setting Up Keycloak for Fine-Grained Authorization

If you’ve already installed and configured Keycloak using the Docker file mentioned in Sebastian Fulga’s original article, you can begin working with your Keycloak server.

In this guide, we'll use the default master realm for simplicity. However, you’re free to create a new realm if preferred, this won’t impact the core behavior, but it will result in URL paths different from those shown here.

Once deployed, Keycloak should be accessible at: http://localhost:7011/

You can log in using the admin credentials defined in your Docker Compose file.

 

Important Version Note: 
At the time of writing, the current Keycloak version is 26.2.5. Future versions may introduce interface changes or new features, so always refer to the official Keycloak documentation and release notes to stay updated. It's best practice to use the latest stable version to take advantage of ongoing improvements from the development team. 

2.1. Create New User

Create_new_userCreate_New_User_2

After creating a new user in Keycloak, the next step is to set up user credentials. To do this, navigate to the "Credentials" tab within the user's detail page in the Keycloak admin console. 

create_credentials

2.2. Creating a New Client and Enabling Authorization in Keycloak

 

Now that you’ve created a user, the next step is to create a new client in Keycloak and enable the Authorization feature. Creating a client is very similar to creating a user.

To get started, navigate to the “Clients” menu in the Keycloak admin console and click the “Create Client” button.

 

This client represents the application or backend service that will authenticate via the Keycloak API. In our example, we’ve named the client MyApp, but you can choose any name that fits your project.

 

Here are the essential settings to configure for the client:

  • Client Authentication: ON
  • Authorization: ON
  • Direct Access Grant: Enabled

 

These settings ensure your client can handle secure authentication and fine-grained authorization using Keycloak. 

create_new_client

2.3. Creating User Roles for Your Keycloak Client 

 

For our MyApp client, we need to define user roles. Keycloak has two role types: realm roles and client roles.

  • Realm roles can be assigned across the entire realm and all clients.
  • Client roles are limited to the specific client where they are created.

 

In this tutorial, we’ll use client roles. To create them, go to the Roles tab under your client’s details page.

We’ll apply the Role-Based Access Control (RBAC) approach to show Keycloak’s flexibility in handling fine-grained permissions.

 

Create the following roles, which will later be used to control access to backend APIs:

Create_roles

2.4. Configure client Authorization 

 

To enable fine-grained access control, navigate to the Authorization tab under the MyApp client page, just as you did for roles.

 

Keycloak may auto-generate default resources, policies, and permissions. These can be safely deleted to keep your configuration clean. While setting up the Authorization module may take some time, this guide will walk you through each step.

 

Configure Scopes

 

Scopes define a user's specific actions and are essential for fine-grained authorization in Keycloak. In this example, we’ll use standard scopes like:

  • manage
  • query
  • view

 

You can also define custom scopes to match your application's needs. 

Configure_scopes

Configure Resources

Resources represent APIs or features you want to protect. These can range from single endpoints to entire application modules.

 

In this setup, we’ll create two resources:

  • users – linked to the query, view, and manage scopes
  • fetch-weather – without any scope, to demonstrate resource-based permissions

 

Note: Resources in Keycloak can have one, many, or no scopes. 

create resourceslist resources

Configure Policies

Policies in Keycloak define the rules that must be satisfied to grant access to a resource. They are evaluated during the authorization process and determine whether a user can access a specific resource.

 

Policies can be based on various conditions, such as:

  • User roles
  • Client applications
  • User groups
  • Attributes or context (e.g., time, regex patterns)

 

This guide will create role-based policies for each client role (user, admin, manager) defined earlier.

These policies will form the foundation for assigning permissions to protected resources. 

create policies

There are a few key aspects to understand when configuring role-based policies in Keycloak:

 

  1. Multiple Roles: A single policy can include various roles. The "Required" checkbox determines whether a role must be present for access. In our case, since each policy includes just one role, it will be required by default. For multi-role policies, be sure to define which roles are mandatory. 
     
  2. Fetch Roles: This option is disabled by default. When enabled, Keycloak fetches roles in real time from the database rather than relying solely on the roles present in the access token. This can be powerful for dynamic role evaluation. If left disabled, users will retain the roles from their current token until a new token is issued. 
     
  3. Logic Setting: The "Logic" configuration controls how decisions are applied:
  • Positive: The policy behaves as defined, permits access, and denies blocks it.
  • Negative: The result is inverted, the permit becomes denied, and vice versa.

By the end of this section, you should have a complete list of role-based policies, each linked to one of the roles created earlier.

listing policies

Configure Permissions

 

The final step in setting up Keycloak fine-grained authorization is to configure permissions. This step must come last, as it depends on previously defined resources, scopes, and policies.

Permissions control what actions a user or client can perform on specific resources or scopes. They are a key part of Keycloak's authorization module, enabling precise access control by linking resources to the policies that govern access decisions.

 

Keycloak supports two types of permissions:

  1. Scope-based permissions – applied to specific actions or scopes
  2. Resource-based permissions – applied directly to a resource

 

For this demo, we’ll use both types:

  • Scope-Based Permissions:
  1. permission_query-users
  2. permission_view-users
  3. permission_manage-users

 

  • Resource-Based Permission:
  1. permission_fetch-weather
create_permissions

There are a few key points worth noting:

  1. Permissions can simultaneously support one or more 'Authorization scopes'.

     
  2. Similarly, multiple 'Policies' can be backed by Permissions at the same time.

     
  3. The 'Decision strategy' dictates how the policies associated with a given permission are evaluated and how a final decision is reached. Here are the options:
  • 'Affirmative' implies that at least one policy must be positive.
  • 'Unanimous' means that all policies must be favorable.
  • 'Consensus' means that the number of positive outcomes must outweigh the number of negative ones.

 

In the end, we will have the following permissions:

Listing_permissions

We observe that we have one permission of the type 'Resource-Based'. This permission is much like the others, except that the scope is not a prerequisite.

 

Now that everything is in place, we can delve into the code to investigate how we handle authorization. However, before we do that, roles must be assigned to our user; otherwise, we'll lack access to our APIs.

 

Navigate to the Users tab in Keycloak, and then proceed to our user to assign all the roles from our MyApp client, just as illustrated:

Assign_roles_to_user

3. How to Implement Fine-Grained Access Control with Keycloak

Let's start with the project structure, although this is not very important since you can create your own file structure. Since this is just a demo project, we will keep it simple.

Project_structure

Dependencies required from NuGet gallery:

- Microsoft.AspNetCore.Authentication.JwtBearer

Appsettings.json:

AppSettings_json_file

To validate JSON Web Tokens (JWT) in your ASP.NET Core application, you typically configure a JwtSettings section in appsettings.json. When using Keycloak, you don't need to set a secret key manually. Instead, Keycloak simplifies JWT signature verification by exposing a MetadataAddress endpoint.

 

This metadata endpoint (/.well-known/openid-configuration) provides Keycloak’s OpenID Connect configuration. It includes essential information such as:

  • The issuer (iss)
  • Supported scopes and response types
  • Authorization and token endpoints
  • Public keys for JWT signature validation
  • Logout endpoints and supported algorithms

 

With this built-in support, Keycloak enables secure and standardized token validation with minimal manual setup.

 

Next, you’ll define a custom authorization requirement by creating a new class: PermissionRequirement.cs. This record should implement Microsoft's IAuthorizationRequirement interface.AspNetCore.Authorization namespace.

 

This class forms the foundation for a custom authorization policy, allowing you to enforce fine-grained access control based on your application’s logic, such as user roles, scopes, or specific permissions.

Permission_requirement_record

Then we need to create a new file named ApplicationPolicies.cs that will contain two classes:

defined_backend_policies

In our code, we have defined a static class known as DependencyInjection. The purpose of this class is to house our helper methods for dependency injection. These helper methods are designed as extension methods and these are being invoked in the Program.cs file.

DependencyInjection_helper_class

In this section, we're setting up Authentication and Authorization for our service. We're also adding http clients. As an extra step, we're setting up a reverse proxy, which we'll discuss in more detail later.

 

We need to specify the previously defined necessary policies within the AddAuthorization method.

 

For simplicity, we stick with minimal APIs, which are the examples in the Program.cs file:

Program_class

All API requests in this demo are GET requests, designed to return simple messages that confirm Keycloak authorization is functioning correctly.

 

Protected Endpoints and Required Policies:

  •  /api/users → Requires the QueryUsers policy
  • /api/users/{id} → Requires the ViewUsers policy
  • /api/users/manage → Requires the ManageUsers policy

 

We’ve implemented a custom authorization handler called RequestBasedHandler to enforce these policies. This handler sends a request to Keycloak on each API call to:

  •  Verify the user's authentication
  • Validate the applied authorization policy

In our setup, the client (we’re using Postman) sends API requests with the Keycloak-issued access token, retrieved from: http://localhost:7011/realms/master/protocol/openid-connect/token

 

This ensures each API request is checked in real time against the latest Keycloak roles and permissions.

 

RequestBasedHandler:

RequestBasedHandler

Let's now authenticate and get a new access token from Keycloak:

curl --location 'http://localhost:7011/realms/master/protocol/openid-connect/token/' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1dWIiOiIxIiA6ICJNbTJUZV96U3ZWDRFX0pBb2ZaUjdIN1RUVnRCaHo5STYya05GX0RLcndFIn0.eyJleHAiOjE3NTIwNjU3OD......' \ --data-urlencode 'client_id=MyApp' \ --data-urlencode 'audience=MyApp' \ --data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:uma-ticket' \ --data-urlencode 'response_mode=permissions'

This request returns a JSON object containing the list of all authorized resources and associated scopes for the authenticated user. This is useful for debugging, auditing, or dynamically adapting frontend behavior based on user permissions.

[ { "rsid": "0d7d72a8-8cc8-47b0-a38b-b973d9b197da", "rsname": "fetch weather" }, { "scopes": [ "view", "manage" ], "rsid": "38ffed86-d128-4eff-a1f9-261727c9548e", "rsname": "users" } ]

In this case we removed the ‘query-users’ role and the ‘query’ scope is no longer present.

5. Check Permissions From Access Token

To verify that the Keycloak access token includes the correct resources and scopes, you need to perform a specific API call after obtaining the token.

curl --location 'http://localhost:7011/realms/master/protocol/openid-connect/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9....' \ --data-urlencode 'client_id=MyApp' \ --data-urlencode 'audience=MyApp' \ --data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:uma-ticket'

By performing this API call, Keycloak will grant us a new access token that will contain a new claim named ‘authorization’:

"authorization": { "permissions": [ { "scopes": [ "view", "manage" ], "rsid": "38ffed86-d128-4eff-a1f9-261727c9548e", "rsname": "users" }, { "rsid": "0d7d72a8-8cc8-47b0-a38b-b973d9b197da", "rsname": "fetch weather" } ] }

 

TokenBasedHandler:

TokenBasedHandler_class

When comparing TokenBasedHandler and RequestBasedHandler, the key differences lie in API call frequency and permission update timing.

 

  • TokenBasedHandler retrieves permissions directly from the access token issued by Keycloak. This approach reduces API calls to the Keycloak server, making it more efficient for performance. However, any role or permission changes made in Keycloak will not take effect until the token is refreshed, since the access token stores static permission data.
  • RequestBasedHandler, on the other hand, performs real-time authorization checks by querying Keycloak on every API request. This ensures that any updates to roles or policies are applied immediately, offering dynamic and up-to-date access control, at the cost of increased API traffic.

 

Choosing between the two depends on whether your priority is performance optimization or real-time accuracy in authorization enforcement.

6 Yet Another Reverse Proxy (YARP) and Authorization policies

Earlier, we created the fetch-weather role along with its corresponding resource, policy, and permission, but haven’t yet demonstrated its use. In this step, we’ll leverage the fetch-weather role to access a weather backend service through YARP (Yet Another Reverse Proxy), avoiding direct calls to the actual API.

 

Our YARP configuration will include the required authorization policies, using the RequestBasedHandler to validate user access via Keycloak in real time. This approach highlights the flexibility of authorization policies in .NET, showing that you’re not limited to scopes—you can define custom resources per API endpoint for precise access control.

 

Yarp.json

{ "ReverseProxy": { "Routes": { "weather-service": { "ClusterId": "Weather", "AuthorizationPolicy": "FetchWeather", "Match": { "Path": "/api/weatherforecast/", "Method": [ "GET" ] } } }, "Clusters": { "Weather": { "Destinations": { "Primary": { "Address": "https://localhost:5001/" } } } } } }

In this YARP setup, the address under the primary destination within the weather cluster points to the weather service's base URL. The route for the /api/weatherforecast/ endpoint is defined with the FetchWeather authorization policy. When calling https://localhost:5000/api/weatherforecast/ using a Keycloak-issued access token, the request succeeds because the user holds the required fetch-weather role. This confirms that Keycloak authorization is working as expected through YARP.

Postman - weather API example using YARP

7. Recommendation

Securing Keycloak in Production Environments

 

When aiming to achieve the highest level of security for your applications, it's essential to consider layered protection strategies. While Keycloak is a powerful and trusted solution for authentication and authorization, additional precautions can help mitigate exposure to potential network threats.

 

One key recommendation is to avoid exposing Keycloak directly to the public internet. Instead, it should be accessible only through a secure backend service acting as a gateway. This architectural pattern adds a protective layer, helping shield Keycloak from direct attacks by malicious actors.

 

This approach does not suggest that Keycloak is insecure; quite the opposite. Keycloak provides robust security out of the box, including support for OpenID Connect, OAuth2, role-based access control (RBAC), and token management. However, following defensive programming principles and minimizing attack surfaces adds resiliency to your system and protects against unexpected vulnerabilities.

 

Combining this strategy with other security best practices, such as token validation, role enforcement, HTTPS enforcement, and logging, creates a stronger and more secure application infrastructure. In security, proactive defense is always better than reactive recovery.

8. Conclusion

We successfully configured Keycloak locally in this walkthrough as a powerful identity and access management solution. We explored its fine-grained authorization features, including resources, roles, policies, and permissions, enabling granular access control for your APIs and services.

 

Using Role-Based Access Control (RBAC) in Keycloak, we demonstrated how to control user access to various protected endpoints. We also compared two practical authorization approaches:

  • Token-based validation, where permissions are read directly from the access token.
  • Request-based validation, where permissions are checked in real time with Keycloak.

 

To extend our scenario, we built an additional application that fetches weather data, using YARP (Yet Another Reverse Proxy). This allowed us to implement authorization policies while proxying requests securely, illustrating how to apply Keycloak authorization beyond a single service.

 

Throughout the process, we focused firmly on API security, showing how to prevent unauthorized access while keeping integration smooth and developer-friendly. This hands-on guide offers a practical foundation for building secure, scalable, and flexible authorization systems with Keycloak in .NET environments.

 

Whether you're new to identity management or expanding your understanding of access control in distributed systems, this guide aims to provide actionable insights you can apply in your development workflow.

 

Happy coding, and remember - safety doesn't happen by accident!

Share on:

* I read and understood the ASSIST Software website's terms of use and privacy policy.

Want to stay on top of everything?

Get updates on industry developments and the software solutions we can now create for a smooth digital transformation.

Frequently Asked Questions

1. Can you integrate AI into an existing software product?

Absolutely. Our team can assess your current system and recommend how artificial intelligence features, such as automation, recommendation engines, or predictive analytics, can be integrated effectively. Whether it's enhancing user experience or streamlining operations, we ensure AI is added where it delivers real value without disrupting your core functionality.

2. What types of AI projects has ASSIST Software delivered?

We’ve developed AI solutions across industries, from natural language processing in customer support platforms to computer vision in manufacturing and agriculture. Our expertise spans recommendation systems, intelligent automation, predictive analytics, and custom machine learning models tailored to specific business needs.

3. What is ASSIST Software's development process?  

The Software Development Life Cycle (SDLC) we employ defines the following stages for a software project. Our SDLC phases include planning, requirement gathering, product design, development, testing, deployment, and maintenance.

4. What software development methodology does ASSIST Software use?  

ASSIST Software primarily leverages Agile principles for flexibility and adaptability. This means we break down projects into smaller, manageable sprints, allowing continuous feedback and iteration throughout the development cycle. We also incorporate elements from other methodologies to increase efficiency as needed. For example, we use Scrum for project roles and collaboration, and Kanban boards to see workflow and manage tasks. As per the Waterfall approach, we emphasize precise planning and documentation during the initial stages.

5. I'm considering a custom application. Should I focus on a desktop, mobile or web app?  

We can offer software consultancy services to determine the type of software you need based on your specific requirements. Please explore what type of app development would suit your custom build product.   

  • A web application runs on a web browser and is accessible from any device with an internet connection. (e.g., online store, social media platform)   
  • Mobile app developers design applications mainly for smartphones and tablets, such as games and productivity tools. However, they can be extended to other devices, such as smartwatches.    
  • Desktop applications are installed directly on a computer (e.g., photo editing software, word processors).   
  • Enterprise software manages complex business functions within an organization (e.g., Customer Relationship Management (CRM), Enterprise Resource Planning (ERP)).

6. My software product is complex. Are you familiar with the Scaled Agile methodology?

We have been in the software engineering industry for 30 years. During this time, we have worked on bespoke software that needed creative thinking, innovation, and customized solutions. 

Scaled Agile refers to frameworks and practices that help large organizations adopt Agile methodologies. Traditional Agile is designed for small, self-organizing teams. Scaled Agile addresses the challenges of implementing Agile across multiple teams working on complex projects.  

SAFe provides a structured approach for aligning teams, coordinating work, and delivering value at scale. It focuses on collaboration, communication, and continuous delivery for optimal custom software development services. 

7. How do I choose the best collaboration model with ASSIST Software?  

We offer flexible models. Think about your project and see which models would be right for you.   

  • Dedicated Team: Ideal for complex, long-term projects requiring high continuity and collaboration.   
  • Team Augmentation: Perfect for short-term projects or existing teams needing additional expertise.   
  • Project-Based Model: Best for well-defined projects with clear deliverables and a fixed budget.   

Contact us to discuss the advantages and disadvantages of each model. 

ASSIST Software Team Members