Dynamic Authorization Policies in Keycloak: How Context-Aware Access Works

Guilliano Molaire Guilliano Molaire Updated October 9, 2026 14 min read

What is dynamic authorization?

Dynamic authorization means deciding whether a request is allowed at the moment it is made, using current facts about the user, the resource and the request, instead of relying only on roles assigned in advance. In Keycloak you build it with Authorization Services: you describe resources and scopes, write policies with conditions, and Keycloak evaluates them on every authorization request.

A role check such as “is this user a finance-analyst?” gives the same answer all day, which is why it is called static. A dynamic decision can also take into account the time of the request, the IP address it came from, a claim in the user’s token, or an attribute on the resource being opened, so the same user can be allowed in one situation and refused in another. This idea is usually described as attribute-based access control (ABAC), and NIST’s guide to ABAC (SP 800-162) defines it as granting or denying requests based on attributes of the subject, the object and the environment, checked against policy.

Keycloak supports this pattern without extra software through Authorization Services, which is enabled by default in the 26.x line. This guide explains the building blocks, the context Keycloak can actually see when it evaluates a policy, which policy type to use for which condition, and how to keep a growing set of policies manageable. If you want a reference for every policy type and its settings, our companion post on Keycloak Authorization Services policy types covers each one in more depth.

Core components of Keycloak authorization

Keycloak’s authorization model has four parts that you configure on a client, which then acts as a resource server (the application or API whose objects you want to protect). Resources say what is protected, scopes say which actions exist, policies hold the conditions, and permissions connect the three. Keeping conditions in policies and the “what” in permissions is what lets you change a rule in one place and have every permission that uses it pick up the change.

Resources, scopes, policies and permissions

Component What it represents Example
Resource An object or group of objects you protect, such as an API path, a document or a record type /api/financial-reports/*
Scope An action a caller can take on a resource view, edit, export
Policy A condition that must be true for access to be granted “User has the finance-analyst role”
Permission The link that says which policies apply to which resources or scopes “Exporting financial reports requires the finance role and business hours”

A resource can also carry a type and an owner, along with custom attributes. The type matters because Keycloak lets you write a typed resource permission that applies to every resource of the same type, so you can protect thousands of customer accounts with one permission instead of one permission per account. The owner matters for rules such as “only the owner can edit this record”. No built-in policy type compares the owner with the caller, so you either write that check in a JavaScript policy or mark resources as owner-managed, which lets owners grant access to other people themselves through User-Managed Access (UMA), a protocol for sharing resources between users.

Policy types in Keycloak

Keycloak 26.x ships these policy types. The ones that make a policy dynamic are the ones that read something about the request at evaluation time, which is mainly the time, regex and JavaScript types.

Policy type What it checks Typical dynamic use
Role Whether the identity has any of the listed realm or client roles, or all of those marked Required The stable baseline that other conditions build on
Group Whether the user belongs to given groups, optionally including child groups Department or team boundaries
User Whether the caller is one of a listed set of users Narrow exceptions, such as a named auditor
Client Whether the request comes from one of a listed set of clients Allowing a reporting service but not a mobile app
Client scope Whether the client was granted given client scopes Restricting an action to tokens issued with a particular scope
Time Whether the current date and time fall within a window Limiting access to a date range or to certain hours
Regex Whether a token claim, or optionally a context attribute, matches a pattern Matching an email domain, a department code or an IP range
JavaScript Any logic you write against the evaluation API Combining attributes, network and time in one rule
Aggregated The combined result of other policies Reusing small policies to build a stricter rule

Two details from the Keycloak time policy documentation are easy to miss. A time policy can restrict the day of the month, month, year, hour and minute, but it has no day-of-week setting, so “weekdays only” cannot be expressed with a time policy alone. The server also evaluates the window against its own clock and the JVM’s default time zone (see TimePolicyProvider in the Keycloak source), which means an hour range is server time, not the user’s local time. Ranges are inclusive, so an hour range of 8 to 17 allows requests from 08:00 until 17:59.

The regex policy reads a claim from the caller’s token, and you can reach nested claims with dot notation such as contact.address[0].country. This makes it a convenient way to use user attributes, as long as a protocol mapper (the client setting that copies a user attribute into a token claim) puts the attribute into the token. In Keycloak 26.x the regex policy also has a Target Context Attributes switch, and with it on the pattern is matched against the runtime context instead of the token, so a regex on kc.client.network.ip_address can restrict access to an office network without any custom code.

JavaScript policies are the most flexible type, but they have two operational requirements. Keycloak 26.x does not let you create or edit JavaScript policies in the admin console, so the script has to be packaged in a JAR and deployed to the server, and JavaScript providers depend on the scripts feature, which Keycloak 26.8 still lists as a technology preview.

How Keycloak evaluates a policy decision

When a client asks for access to a resource and scope, Keycloak finds every permission that covers that resource or scope, evaluates the policies attached to each permission, and combines the results. Each policy has a Logic setting of Positive or Negative, and Negative inverts the policy’s result, which is how you write rules such as “anyone who does not have the contractor role”.

Keycloak has two main kinds of permission:

  • Resource-based permissions apply to a specific resource, or to every resource of a given type.
  • Scope-based permissions apply to specific actions, optionally limited to particular resources, for example allowing edit on employee records but not delete.

How the results combine is controlled by a decision strategy, which you set on each permission, on aggregated policies and on the resource server as a whole.

Decision strategy Grants access when
Unanimous (the default) Every associated policy evaluates to a positive decision
Affirmative At least one associated policy evaluates to a positive decision
Consensus Positive decisions outnumber negative ones, and a tie is a denial

Unanimous behaves like a logical AND and Affirmative behaves like a logical OR, so a “finance role AND business hours” rule is a permission with both policies attached and the Unanimous strategy.

What context can a Keycloak policy use?

A Keycloak policy can use three kinds of information: the caller’s identity (the claims and roles from their token), the resource being requested (its type, owner and attributes), and a small set of runtime attributes that Keycloak attaches to every evaluation. Anything else, such as device posture or a risk score, has to reach Keycloak as a claim.

The policy evaluation API lists the built-in runtime attributes:

Attribute What it contains
kc.time.date_time Current date and time in server time, formatted yyyy-MM-dd HH:mm:ss by Keycloak 26.8 (the reference documentation still shows an older format)
kc.client.network.ip_address The client’s IP address, which can be null if no valid IP is provided
kc.client.network.host The client’s host name, or whatever proxy headers supply
kc.client.id The ID of the client the token was issued to
kc.client.user_agent The value of the User-Agent header
kc.realm.name The realm name

The IP address is the address of whatever connects to Keycloak, so behind a load balancer or reverse proxy it is the proxy’s address unless Keycloak is configured to trust forwarded headers with the proxy-headers option. Check this before you write any network rule, because a wrong setup either never matches or matches every request.

When you need information that is not in the token or this list, the client can push additional claims in its authorization request by sending a claim_token to the token endpoint, and those claims become available to your policies as context attributes rather than token claims. That is how an application can pass facts such as the tenant the user is working in or the sensitivity level of a document. Keycloak does not verify pushed claims, so rely on them only when the client sending them is a confidential backend you control, and note that claim names starting with kc. are reserved and dropped.

There is one security caveat that Keycloak calls out in its threat model guidance on read-only user attributes. If a policy trusts a user attribute, such as department or clearance-level, users must not be able to edit that attribute themselves, otherwise they can grant themselves access. Mark those attributes as read-only for users in the user profile configuration.

Creating and managing dynamic authorization policies

The process has five parts: turn on authorization for a client, describe what you protect, write small policies, connect them with permissions, and test the result before rolling it out. The example below protects a financial reporting API so that only the finance team can export reports, and only during business hours.

Setting up authorization in Keycloak

  1. In the admin console, open Clients and choose the client that represents your API. Under Capability config, turn on Client authentication and then Authorization. Keycloak only allows authorization on confidential clients (clients that hold a secret and authenticate to Keycloak), and switching it on also enables a service account, which lets the client call Keycloak as itself.
  2. Open the new Authorization tab and create resources, for example Financial reports with the URI /api/financial-reports/* and the type urn:reports:financial.
  3. Under Authorization scopes, create the actions you need, such as view and export. A healthcare application might instead use view-patient-data and edit-medical-records.
  4. Under Policies, create a role policy named Finance team that requires the finance-analyst role, and a time policy named Business hours with the hour range set to 8 to 17, which covers 08:00 to 17:59 server time.
  5. Under Permissions, create a scope-based permission named Export financial reports for the export scope on the Financial reports resource, attach both policies, and leave the decision strategy as Unanimous.
  6. Use the Evaluate tab to run test requests as different users and see which permissions granted or denied access before any application depends on them.

Your application then enforces these decisions, either by calling the token endpoint for a requesting party token (an access token that carries the permissions the caller was granted) or by using a policy enforcer, a library or proxy in front of the API that asks Keycloak for a decision on each request.

Combining RBAC and ABAC for detailed control

Most teams get the best results by keeping roles as the backbone and adding attribute conditions on top. Role-based access control (RBAC) describes what a person’s job allows in general, and the attribute conditions decide whether that access applies to the request in front of you.

Start with roles that match job functions, such as finance-analyst, hr-manager or security-officer. Then add conditions from user attributes such as department or employment-type, from the request such as the client’s IP address, and from resource attributes such as a confidentiality-level tag. For example, a policy can allow managers to view all documents in their department but reserve files tagged executive-only for senior leaders.

Since the built-in time policy cannot express weekdays, a “business hours on weekdays” rule needs a JavaScript policy. This sketch uses the server’s clock:

// Deployed in a JAR and listed under "policies" in META-INF/keycloak-scripts.json
const now = new Date();
const day = now.getDay();      // 0 is Sunday, 6 is Saturday
const hour = now.getHours();   // server time zone

const isWeekday = day >= 1 && day <= 5;
const isBusinessHours = hour >= 8 && hour < 18;

if (isWeekday && isBusinessHours) {
    $evaluation.grant();
}

Keep the role check in its own role policy and the office network check in a regex policy with Target Context Attributes on, then combine all three in an aggregated policy or a Unanimous permission, rather than putting everything into one script. Smaller policies are easier to test, and you can reuse the network rule in permissions that do not care about time. Our step-by-step ABAC guide for Keycloak walks through attribute policies in more detail.

Best practices for policy management

As the number of policies grows, the main risk is that nobody can explain why a user was allowed or denied. These habits keep the model readable:

  • Name policies after the business rule. Names such as HR-Employee-Records-Business-Hours tell an auditor what the rule is for, and a shared prefix per department makes the policy list easier to scan.
  • Keep policies small and reusable. One condition per policy, combined with aggregated policies or permissions, is easier to reason about than one large script.
  • Write down why each policy exists. Record the requirement it implements and who owns it, because audits and incident reviews will ask.
  • Test negative cases as well as positive ones. The Evaluate tab shows every permission’s result for a given user, so check that the wrong user is refused, not only that the right one gets in.
  • Watch the cost of complex policies. JavaScript policies run on every evaluation, so keep them free of slow work, and prefer passing facts in as claims over fetching them during evaluation.
  • Review the set regularly. Remove policies that no permission references any more and merge rules that overlap.

Scaling and automating policy management

Once you have more than a handful of permissions, clicking through the admin console stops being practical. Keycloak lets you treat the authorization configuration as data that you export, review in version control and apply through an API, which is how most teams keep environments consistent.

Automating policy updates through the Admin REST API

Every part of the authorization model is available through the Admin REST API under /admin/realms/{realm}/clients/{client-uuid}/authz/resource-server, with sub-paths for resource, scope, policy and permission, plus an import endpoint for a complete configuration. You can also export and import the configuration from the Export tab under the client’s Authorization tab, which produces a JSON file containing the resources, scopes, policies and permissions. Keeping that file in Git and applying it from a pipeline gives you reviewable changes and a quick way to roll back.

Auditing and compliance

Once admin events are turned on (they are off by default), Keycloak records them whenever someone creates, updates or deletes authorization resources, scopes, policies and permissions, and those events identify which admin made the change and when. Combined with your application’s own logs of access decisions, this gives auditors evidence of who could access what and when the rules changed. Our guide to Keycloak auditing and event logging explains how to turn admin events on and where to send them, and Skycloak keeps these events searchable in its audit logs.

Frameworks such as SOC 2 and HIPAA require you to restrict access to sensitive data and to be able to show those controls working. Dynamic policies help with the first part and admin events with the second, but no Keycloak setting makes an application compliant on its own.

Using a managed platform for simpler IAM

Running Authorization Services in production also means running Keycloak itself: upgrades, high availability, backups and monitoring. Skycloak runs managed Keycloak clusters so your team can spend its time on the authorization model rather than the servers. You can manage roles and access control from the same place, and if you need a JavaScript policy or another custom provider, uploading your own JAR is available on the Enterprise plan through extensions, with the Scripts preview feature switched on in your cluster’s feature settings for JavaScript policies.

Advanced use cases: multi-tenant access control

Multi-tenant applications need two things from authorization: each tenant’s data must stay isolated, and a user who works for several tenants must get the right permissions in each one. Keycloak supports several layouts, and the right one depends on how strict the isolation must be.

Approach Isolation Best for
One realm per tenant Strongest, since users, clients and policies are fully separate A small number of large tenants with strict regulatory needs
One realm with organizations Strong, with tenant membership managed by Keycloak B2B SaaS with many customer companies
One realm with tenant-scoped roles or groups Depends on your policies and claims Users who move between tenants often

With a shared realm, put the tenant into the token as a claim and make policies compare it with the resource being requested, so a token issued for tenant A is never accepted for tenant B’s data. Keycloak’s Organizations feature can add organization membership to tokens for you.

Some rules depend on relationships between objects rather than attributes, such as “editors of a folder can edit every document inside it”. This is called relationship-based access control (ReBAC), and Keycloak’s built-in policy types do not model relationship graphs. Teams that need it usually keep Keycloak for identity and tokens and call an external policy engine such as Open Policy Agent (OPA) or a decision point that speaks AuthZEN, the OpenID Foundation’s standard authorization API, as described in our posts on Keycloak as an AuthZEN policy decision point and Keycloak with OPA.

Conclusion

Dynamic authorization in Keycloak comes from combining a few small parts: resources and scopes that describe your application, policies that each check one condition against the identity, the resource or the request, and permissions with a decision strategy that ties them together. Start with role policies as the baseline, add time, regex or JavaScript conditions only where a rule really depends on context, keep user attributes that policies trust read-only, and store the exported configuration in version control so changes are reviewed and audited.

FAQs

What is the difference between RBAC and dynamic authorization?

RBAC grants access based on roles assigned ahead of time, so the answer only changes when an administrator changes the roles. Dynamic authorization evaluates conditions at request time, such as the time, the network, a token claim or a resource attribute. In Keycloak you usually combine them, with a role policy as the baseline and context policies added through a Unanimous permission.

Are Keycloak dynamic scopes the same as dynamic authorization policies?

No, they are separate features. Dynamic scopes, now called parameterized scopes and promoted from experimental to preview in Keycloak 26.8, let a client request a scope with a value, such as project:12345, so the value ends up in the token. Your policies or your API can then use that value, but the scope itself does not make an access decision.

What are client policies in Keycloak?

Client policies control how clients are configured and how they behave in OAuth flows, for example requiring PKCE (Proof Key for Code Exchange, which protects the authorization code from interception) or a particular client authentication method. They govern client behaviour rather than user access to resources, so they complement Authorization Services instead of replacing it.

Can Keycloak handle relationship-based permissions?

Keycloak handles some relationship rules but not all of them. Owner-managed resources, typed resource permissions and JavaScript policies that compare the owner with the caller cover “owner can edit” style rules, but Keycloak has no built-in way to evaluate a relationship graph such as folder and document inheritance. For that, most teams keep Keycloak as the identity provider and call an external policy engine such as OPA or an AuthZEN-compatible decision point.

Do Keycloak authorization policies work across multiple applications?

Policies belong to a single resource server, which is one client, so each API you protect has its own set. If several applications share one API, they can all ask that resource server for decisions, and client and client scope policies can treat callers differently. Exporting each resource server’s configuration to JSON is the usual way to keep shared rules consistent between them.

Managed identity, built on open source

Skycloak runs real upstream Keycloak for you, so you get SSO, MFA and SCIM without operating the infrastructure. Unlimited users on every plan, no fork, and you can export at any time.

Guilliano Molaire
Written by
Founder

Guilliano is the founder of Skycloak and a cloud infrastructure specialist with deep expertise in product development and scaling SaaS products. He discovered Keycloak while consulting on enterprise IAM and built Skycloak to make managed Keycloak accessible to teams of every size.

Start Free Trial Talk to Sales
© 2026 Skycloak. All Rights Reserved. Design by Yasser Soliman