Enforce default ACR values

We want to use a step-up mechanism to require different authentication levels (1FA, 2FA) for the different clients of a realm. For this we have set up the step-up flow as described in the Server Administration guide. Then we set default ACR values for each client as described in the guide. However, as is specified in various warning boxes throughout the linked sections, users can avoid having to provide their second factor for clients with default ACR 2 by specifying acr_values=1 in the URL.

Is there any way to enforce the default ACR values set in the client/realm options (e.g. the acr_values param would be ignored or rejected when authenticating a user)? That way we could manage what authentication level is required for which service centrally through Keycloak instead of having to tell all client developers that they have to double-check the ID token for the correct ACR value defined by us.

I have read the section about ACR claims in the OIDC core specification and I do not know if this would break the OIDC conformity. It sounds like the acr_values have to be considered if present in the URL. As far as I understand the default case for step-up is to provide different authentication levels within a client (like a higher level for payment related actions) in which case doing the requests for step-up and checks in the client code is necessary anyway. However, for our use case enforcing the default values would be preferable.

Hey @Vertganti,

we have exactly the same requirement (and issue).
Did you ever find a solution?

Well, just look around in the client settings details…

Unter tab ā€œAdvancedā€ in section ā€œAdvanced settingsā€ (yes, sigh naming is hard…) you’ll find this:

Here you can overwrite the realm ACR-to-LoA mapping and also set a default ACR value which will be used if the client doesn’t request a special ACR value claim.

Hey @dasniko,

I think both, the OP and I, saw these options, but Keycloaks docs explicitly state this:

Note that default ACR values are used as the default level, however it cannot be reliably used to enforce login with the particular level. For example, assume that you configure the Default ACR Values to level 2. Then by default, users will be required to authenticate with level 2. However when the user explicitly attaches the parameter into login request such as acr_values=1, then the level 1 will be used. As a result, if the client really requires level 2, the client is encouraged to check the presence of the acr claim inside ID Token and double-check that it contains the requested level 2.

https://www.keycloak.org/docs/latest/server_admin/#_mapping-acr-to-loa-client

Maybe we’re misunderstanding this, but it does not sound like it should be used if we want to enforce a minimum LoA in Keycloak for a specific client.

Assuming this is indeed not its intended purpose, do you have any suggestions?
I’m thinking of implementing a custom condition for auth flows, in order to use this setting in a non-overridable way.

Well, I donā€˜t know what anybody thinks or has read if not mentioned explicitly. :man_shrugging:
And the title of this thread asks for a default value, which is exactly what it is, a default, if nothing else is requested.
You are looking probably for a fixed and guaranteed value.
ACR values are meant to be requested/used and checked by the client-side.

1 Like

But the OP did mention it explicitly and even linked to the section? They also said that they want to enforce this value and they cannot due to the mentioned restrictions. I don’t know how much more explicit they can get.

Rather than discussing semantics of ā€œenforcing defaultsā€ vs ā€œguaranteed valuesā€, could we please discuss the issue instead, assuming the description is now sufficient? How would one go about enforcing LoA directly in Keycloak, versus contacting each client developer separately?

A custom authentication flow condition that reads out the desired LoA (set as default) seems appropriate to me, but I don’t know the specs and limitations as well as you or others here.

As dasniko mentioned before, the enforcement of the ACR is done at the app [1] or API level [2]. This allows the app or API to request step-up authentication based on their use cases.

In other IdPs, you can map the acr_value to a specific authentication flow, which provides more flexibility. However, in Keycloak, this is not supported so far.

But generally speaking, the enforcement and validation of the desired value of the authentication context reference are delegated to the app or API.

[1] Final: OpenID Connect Core 1.0 incorporating errata set 2
[2] RFC 9470 - OAuth 2.0 Step Up Authentication Challenge Protocol

1 Like

Thank you for clarifying @embesozzi !

Unfortunately, our environment is quite heterogenous. While I understand that the standard sees the app in the position to do much of the work, it is simply not possible for us to contact every 3rd party developer and ask them to extend their software. Keycloak is the only option for us to have any level of centralised control.

That being said, knowing that it is currently not possible in Keycloak is still good to know for planning our next steps, so thank you, once again.

With Keycloak customization, you can almost always achieve the desired use cases. For example, I believe this might be what you are looking for (provided by sventorben):

The code for the LoAEnforcerExecutor is there, but it has not yet been merged.

Nevertheless, in the authentication step, you always have many scenarios to consider, such as SSO, etc. This is why I recommend that the app must be in control of ensuring the desired level of assurance, which also enhances UX by requesting steps or adding more friction depending on the use case.

1 Like

Thanks @embesozzi for providing the link to that issue, I didn’t know that yet.

Answering the initial question by @sherzinger - No, we never found a way to enforce ACR values. Our own client applications check for the correct value returned by the token, all third party clients don’t use step-up. The only exception is XWiki, where i created a PR for the OIDC plugin to support checking the ACR claim on an instance level. Note that it only uses the ACR claim, not the acr_values parameter which I was referring to in my initial question (See the OpenID documentation for the difference between the two).

@dasniko Sorry if the title (or specifically the use of the word ā€˜default’ in this context) was misleading. However, I tried to state very clearly what I wanted to achieve and what resources I had already considered. Since I have worked a bit more with ACR by now I would probably word everything differently :slight_smile:

Anyway, it seems to me that @sherzinger wants exactly the same thing I intended to ask about: Enforcing a specific LoA for each client, so all clients can be kept in one realm for proper SSO. We currently have to use two realms for this, since that is the only (builtin) way we know about to enforce different levels of authentication for different clients. I know from the different resources I read that this is not how the ACR mechanism was intended to be used, but I just wanted to ask if it was possible in any way. From the issue linked by @embesozzi we can see that this has already been worked on. The PR has been stale for a year, but maybe it will gain a bit traction now that they linked this discussion in the issue.

1 Like

The inability to map ACRs is severely limiting to Keycloak as an IDP. Also the reliance on LOA as singular numeric calculations for access.

ACR should be more than just a single signal, it can be a collection of signals to meet a higher level condition. For example:

urn:gov:acr:public = Any Network, Any Device, Any MFA
urn:gov:acr:mod = Trusted Network, Compliant Device, Phishing-resistant MFA
urn:gov:acr:high.itar = Trust Network, Compliant Device, Government Issued Smartcard Only

AMR carries the specifics on what signals met the condition. Such as in the case of high.itar might have:

{
ā€œcredentialā€: {
ā€œtypeā€: ā€œcertificateā€,
ā€œcategoryā€: ā€œhardwareā€,
ā€œissuerā€: ā€œgovIssuingCAā€,
ā€œissuer-thumbprintā€: ā€œsomethumbrint:stuff:hereā€
},
ā€œnetworkā€: ā€œtrustedā€,
ā€œdeviceā€: ā€œcompliantā€
}

I don’t care about the details, and if the organization changes the top level rules there’s nothing to change (such as now allowing non-compliant but registered devices or additional credential types); the changes would solely take place at the authorization server minting the tokens.

And at the IDP side, I can go down the list of conditions to make the most logical user-friendly step-up behavior (such as if you already logged in with smartcard, but not on a compliant device then I simply say ā€œThis requires logging in from a registered compliant device, go hereā€). The App/API only cares that if I get ā€œurn:gov:acr:publicā€ it’s good for all public data and urn:gov:acr:high.itar is valid for ITAR data; and if the condition doesn’t meet the data tags then issue a new log in request with the right ACR.

So while the client/API may be involved, and should be, there’s still also going to be needs to overwrite/modify when sending to external IDPs or enforce specific ACR for any given client if they can’t specifically request one.

Edit: Here’s just one smaller example of translating ACRs across providers. For US Consumer to Government then Login.gov is supposed to be used. They have various ACRs that have to be converted from a common one that may already exist and are unknown to each app in the federation that agrees to other values/conditions.

Such as if I know the log in from Client → MyIDP requires an Identity proofed account, from a compliant device workstation, then the Identity Proofing (IAL) requirements only has to be conveyed across with urn:acr.login.gov:verified-facial-match-required.

Client – sends acr_values urn:gov:mod → MyIDP – sends acr_values urn:acr.login.gov:verified-facial-match-required → Login.gov in order to get the right condition for that singular signal to ensure the Identity was proofed (they do not own the device nor can give me a device compliance check).

Then combine that with my own device compliant validation, to ultimately return and urn:gov:mod as the final ā€œACRā€ to anyone participating in our ā€œfederation agreementā€.

SPIs are always your friends when it comes to customizing Keycloak functionality based on your use cases.

That may be true, but it’s also something that is solvable by an IDP and not rely on everyone coming up with their own solutions. It may also put Keycloak ahead of others if they do it well, although I’m not sure how broadly ACR is used currently outside of niche groups, it should be though.

ACR processing should be part of a core fine-grained authorization model at the application level in order to tie together all the various tags and spread across federated partners. And IDPs need to provide simple ways to map these to specific conditions.

Anyhow, new to Keycloak and hoping to see it grow without requiring so much customization.

@cbt If you have suggestions for improvements of Keycloak, you should got to the GitHub discussions page to discuss them there. This is a community forum to get help from/to the community. The maintainers barely read here, and if, they don’t accept suggestions from anywhere else than GitHub discussions/issues.