Browser flow - conditional access denied for IdP login

Hi,

I want to restrict login to a service based on whether the user has a role or not.
I followed this SO answer: security - Keycloak: Role based client log-in access restriction for users - Stack Overflow and it works as expected for the form login. However, when I try to add exactly the same condition to the IdP login, it seems to have absolutely no effect.

Here is a screenshot of the flow I tried to create. I moved the top level actions into sub flows, and added a conditional role check to each of them. Only the one on the bottom works however.

I simplified the form a little bit, basically following any of the 3 alternative authentication steps up with the role check. However I still get the same behavior: Only the user/password authentication actually fails if the role is not present. If I use an IdP to log in, the role check is entirely ignored.

Small correction: it does work for username/password based auth and cookie auth. It does NOT work for IdP logins.

Of course not, as the authentication via IdP is not done via the browser flow.
Keycloak sends the users browser to the IdP, where the users authenticates itself. Afterwards, the user is being redirected back to Keycloak. In your IdP configuration in Keycloak, you can set flows which should happen, after a user is redirected back from the IdP to Keycloak. There’s one flow for the first broker login (this ist most likely already set to match existing accounts) and you can set a flow which should be executed on all post logins.

@dasniko Thank you very much for your response.
That’s actually a nice coincident. I watched one of your videos earlier which helped me understand the conditional executions :slight_smile:

Ok, so if I understand you correctly then the IdP redirect execution is always the end of the flow. I thought KC maybe keeps track of the process, i.e. via the session state in the URL, so a second step after this redirect made sense to me. But it seems that is not the case.

But back to my initial goal of denying access to a specific client. I don’t want to change the behavior for the entire realm/Idp, but change it on a per-client basis. The client config only allows me to override the browser flow which checks 2 out of 3 boxes (cookie + form login). How would I go about this?

I could probably write an SPI that checks if a client-alias named role is present for a user and use this in a post-login flow on all IdPs, but I want to make sure KC can’t do it before I add code.

Is there really no built-in way to deny access to certain clients based on roles?

If there is really no way to do the (recommended way of) authorization in/by the client, maybe this would help you:

But you should not enable the “upload_scripts” feature, (“scripts” feature (without “upload_”) is needed and sufficient) and then deploy your authenticator as jar file:
https://www.keycloak.org/docs/latest/server_development/index.html#_script_providers

Thank you very much for those links @mbonn. They summarize pretty well the issues we are facing (application we have no control over) but unfortunately I think both of those end up with the same issue than I have currently: Neither solution covers brokered IdP logins.

I don’t understand why this is such a complex task: Keycloak knows the client and it knows the user at the time of creating the ID token. Shouldn’t it be trivial to bind this process to some condition?

The fact that the browser flow always ends after a brokered login caused days of brain-twist in my case… As Niko wrote above, you can handle brokered IdP logins by defining a flow with your authorizers and then add this flow at the end of your browser flow AND use this flow a post idp flow (can be configured in the broker config menu). And you have to ensure that the authorizer is also executed after a cookie login, so that users never have any chanve to bypass it…

For my setup, I defined a config structure within the authorizers java-scripts to decide for which client an authorization condition is checked (group membership in my case) and which clients are always handled with a context.success() call. There I also configured (in case of user being unauthorized) which clients get a standard oidc redirect with error-message and for which clients keycloak shows an access-denied-page, depending on the clients capabilities to handle authentication-failures.

Mybe it is not the perfect solution, but it is the only way I found wich works well and flexible enough to cover my use cases.

@mbonn thank you for the response. I had a very similar idea actually but haven’t tried it yet.

I am a bit concerned about how secure it is. Are these 3 options (username/password, idp broker, cookie) the only ways to log into your Keycloak account or are there others? What about direct access grants for example? They could be considered a login, but initiated by the client.

Kerberos my be a 4th option. If you group the default authenticators (all of them marked “optional”) cookie, IDP-redirect, kerberos … to a “required” subflow and attach your authorizator on layer obove (also marked as “required”), the login should be secure.

Direct access grants should be disabled for every end-user clients, it’s a deprecated grant and will probably removed from oidc specification. Allowing direct grant tends to apps where users can enter their password in the app instead of keycloak’s login page.

We’re not using Kerberos at the moment, so that’s fine. So is essence you are saying that the browser flow defines all possible login options from a user perspective? If so, then a custom client aware condition used in IdP post-login alongside a custom browser flow should indeed do the trick.

If you’re certain about this, then that is a huge help. Thank you very much for responding to my questions. I very much appreciate it!