8000 AuthZen Evaluation API by ryanemerson · Pull Request #47632 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content

AuthZen Evaluation API - #47632

Merged
pedroigor merged 3 commits into
keycloak:mainfrom
ryanemerson:47629/authzen_evaluation_api
Apr 17, 2026
Merged

pedroigor merged 3 commits into
keycloak:mainfrom
ryanemerson:47629/authzen_evaluation_api

Conversation

@ryanemerson
@ryanemerson ryanemerson commented Mar 31, 2026 •
Copy link
Copy Markdown
Contributor

Closes #47629

The Evaluation API is exposed at /realms/{realm}/authzen/access/v1/evaluation.

Two subject.type values are supported:

  • client: Lookup the KC client identity based upon the passed subject.id
  • user: Lookup the KC user identiy based upon the passed subject.id. The ClientModel is retrieved using the client the request token is issued for.

For both client and user the request properties are merged with those associated with the looked up resource. Should we allow this or just allow the looked up client/user properties and ignore the subject.properties for these types?

Request resource.properties are currently ignored as there did not seem to be an obvious place where to integrate this with the existing Authorization APIs

@ryanemerson
ryanemerson force-pushed the 47629/authzen_evaluation_api branch from 4d1fe11 to f5d790f Compare March 31, 2026 11:46
@ryanemerson
ryanemerson requested a review from a team March 31, 2026 13:30
@ryanemerson
ryanemerson force-pushed the 47629/authzen_evaluation_api branch 3 times, most recently from fb41003 to 041f8fa Compare April 1, 2026 12:05
@ryanemerson
ryanemerson marked this pull request as ready for review April 1, 2026 14:24
@ryanemerson
ryanemerson requested review from a team as code owners April 1, 2026 14:24
@pedroigor pedroigor left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ryanemerson Only a few comments.

@ryanemerson
ryanemerson force-pushed the 47629/authzen_evaluation_api branch from 041f8fa to 742318a Compare April 3, 2026 15:41
@ryanemerson
ryanemerson commented Apr 3, 2026 •
Copy link
Copy Markdown
Contributor Author

Additional checks added so that we ensure that a client is enabled and that the token is associated with the client subject.id being evaluated.

I have moved all AuthZen functionality and tests from services/ and tests/base to a dedicated module authzen/. The diff is now significantly larger, but it is mostly just boilerplate and test code.

When trying my original solution with the AuthZen Interop tests, I found some issues with my implementation which I have also addressed. To help ensure interop works as expected, I have added an additional test AuthZenEvaluationInteropTest that loads the interop scenarios defined in the AuthZen project and executes them against a Keycloak server that has all of the required resources configured. The idea being that we can update or add additional json interop files in the future.

N.B. Unfortunately the interop tests utilise Base64 IDs for subject.id fields which are not compatible with Keycloak UUID Ids, so we have to transform them in the test. This is not us violating compliance with the spec, just an incompatibility between the interop test choices and Keycloak's model.

@ryanemerson
ryanemerson force-pushed the 47629/authzen_evaluation_api branch 2 times, most recently from 8e1574b to 71a3001 Compare April 7, 2026 09:47
Closes keycloak#47629

Signed-off-by: Ryan Emerson <remerson@ibm.com>
@ryanemerson
ryanemerson force-pushed the 47629/authzen_evaluation_api branch from 71a3001 to 28b52f1 Compare April 7, 2026 09:49
@thomasdarimont
Copy link
Copy Markdown
Contributor

Great to see AuthZen land in Keycloak!

The OpenID Foundation Conformance Testing Suite has some support for testing the AuthZen Evaluation API that you might want to checkout: https://gitlab.com/openid/conformance-suite/-/tree/master/src/main/java/net/openid/conformance/authzen?ref_type=heads

See also: https://gitlab.com/openid/conformance-suite/-/wikis/Developers/Build-&-Run

@ryanemerson
Copy link
Copy Markdown
Contributor Author

Thanks @thomasdarimont , I wasn't aware of that. I'll take a look 👍

@pedroigor pedroigor left a comment •
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @ryanemerson. LGTM. Only a single comment.

Not sure if you want to update the copyright (year) in some files. Not a blocker.

void index(BuildProducer<IndexDependencyBuildItem> indexDependencyBuildItemBuildProducer) {
indexDependencyBuildItemBuildProducer.produce(new IndexDependencyBuildItem("org.liquibase", "liquibase-core"));
indexDependencyBuildItemBuildProducer.produce(new IndexDependencyBuildItem("org.keycloak", "keycloak-services"));
indexDependencyBuildItemBuildProducer.produce(new IndexDependencyBuildItem("org.keycloak", "keycloak-authzen-services"));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't enough to add an empty META-INF/beans.xml file at authzen-services? IIRC, that was enough for SCIM and I remember trying the same change but later dropped it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That works! I have reverted the changes to KeycloakProcessor and added the beans.xml file.

StoreFactory storeFactory = authorization.getStoreFactory();

ClientModel client;
if (request.subject().type() == AuthZen.SubjectType.CLIENT) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it by design in this first MVP that the resource server used for policy evaluation is implicitly derived from the Bearer token claim?

This couples “who is calling the PDP” with “which policies to evaluate,” which breaks the gateway pattern when externalizing authorization: a gateway authenticating with its own token cannot evaluate user access against a different resource.

I’m not sure if I might be missing something.

@pedroigor pedroigor Apr 9, 2026 •
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the assumption here is that the PEP is running on the resource server side, checking permissions for protected resources within the resource server. In this case, the PEP will always authenticate using its own token (RS client credentials) and evaluate permissions based on a given (USER) subject.

When the PDP is running behind a gateway, there must be some way to resolve the resource server and then invoke the PDP with a proper token.

Or some other mechanism ...

In authz services, we allow fetching permissions from whatever resource server based on an audience parameter. But in AuthZen (specs), this is seems to be a deployment concern and up to the implementation to decide how to do it?

@embesozzi embesozzi Apr 10, 2026 •
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In relation to the authentication process between PDP and PEP, the standard use “SHOULD” which means it is up to the PDP to decide which mechanism to use. Options may include mutual TLS, OAuth-based authentication, or API keys.

The PDP SHOULD authenticate the calling PEP

In our case, being both an IdP and an AuthZEN PDP, it makes sense to simplify integration when the PEP is at the API or MCP level and functions as an OAuth Resource Server. In this scenario, token claims can be used when building the AuthZEN Subject-Action-Resource-Context (SARC) model. However, there is no strict relationship between how the PEP is authenticated and how the PDP parses the AuthZEN request.

Regarding authorization architecture, in my experience, FGA can be applied at the MCP/API level, while CGA is applied at the API or Agent/MCP Gateway level.

Since this is the first MVP, we can clarify these assumptions in the documentation, particularly the supported authentication mechanisms between PDP and PEP, as well as how the bearer token is used when building the SARC model.

Signed-off-by: Ryan Emerson <remerson@ibm.com>
Signed-off-by: Ryan Emerson <remerson@ibm.com>
< BD0 /div>
@ryanemerson
Copy link
Copy Markdown
Contributor Author

I have pushed two commits addressing the outstanding issue. I think we should be good to go now.

AuthZen.Subject subject = request.subject();
Identity identity = switch (subject.type()) {
case USER -> {
UserModel user = session.users().getUserById(realm, subject.id());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current implementation resolves users exclusively via session.users().getUserById(realm, subject.id()), which implicitly assumes that the PEP can provide the Keycloak internal user ID as subject.id.

In practice, however, a PEP typically only has access to an OAuth access token. Using the sub claim as subject.id may work when sub equals the internal user ID, but this breaks when pairwise subject identifiers are enabled, since sub then becomes client-scoped and no longer maps to a Keycloak user.

This also deviates from the non-normative AuthZen examples, where subject.id is illustrated using human-readable identifiers such as email addresses.

Would it make sense to consider alternative user-resolution strategies (e.g. username/email lookup) that better align with expected PEP inputs?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 I definitely think it would be useful to explore such strategies.

Possible solutions could be making use of the request's subject.properties or providing different subect.type values to control the lookup behaviour.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have created #48285 to track the work we want to complete in order for AuthZen support to be good enough for a MVP Experimental feature.

As part of this work, I propose that we make it so that the default lookup semantics for users is based upon the username field, not the UUID like it currently is #48287

We can then add additionally look up strategies based upon the subject.type value with an optional qualifier subject.type=user<.qualifier>, e.g.:

The intention would be to just allow username and UUID lookups at first.

@pedroigor
Copy link
Copy Markdown
Contributor

We are merging this core set of capabilities so that we can start working on follow-ups (some already being discussed here).

@pedroigor
pedroigor merged commit 3e415a4 into keycloak:main Apr 17, 2026
90 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement the AuthZen Evaluation API

6 participants

0