Restrict user authentication for some group for SAML client

I have already configured and tested authorization via SAML with slack. It works.

Now I would like to understand how to restrict realm users to login with different clients in this realm.
Users are exported from LDAP (free ipa) with their groups. It would be great to restrict access based on the LDAP group, but it cannot be done easily.

I tried to solve it like this:

  • delete all mappers in client settings
  • create a role (for example, with the name Slack access)
  • create a client scope with the necessary mappers
  • link this client scope to the slack access role
  • add this scope to the client on the “client scopes” tab
  • link the “slack access” role to user group

It didn’t work. All users of the realm still can access the slack.
How can I do it?
I do not want to create a separate realm for each client and manipulate user import filters, because I need a “real” SSO.

Solved by this tutorial: https://janikvonrotz.ch/2020/04/30/role-based-access-control-for-multiple-keycloak-clients/

But usefull script is:
AuthenticationFlowError = Java.type(“org.keycloak.authentication.AuthenticationFlowError”);
FormMessage = Java.type(‘org.keycloak.models.utils.FormMessage’);

 function authenticate(context) {
    var MANDATORY_ROLE = 'feature:authenticate';
    var username = user ? user.username : "anonymous";

    var client = session.getContext().getClient();

    LOG.info("Checking access to authentication for client '" + client.getName() + "' through mandatory role '" + MANDATORY_ROLE + "' for user '" + username + "'");

    var mandatoryRole = client.getRole(MANDATORY_ROLE);

    if (mandatoryRole === null) {
        LOG.info("No mandatory role '" + MANDATORY_ROLE + "' for client '" + client.getName() + "'");
        return context.success();
    }

    if (user.hasRole(mandatoryRole)) {
        LOG.info("Successful authentication for user '" + username + "' with mandatory role '" + MANDATORY_ROLE + "' for client '" + client.getName() + "'");
        return context.success();
    }

    LOG.info("Denied authentication for user '" + username + "' without mandatory role '" + MANDATORY_ROLE + "' for client '" + client.getName() + "'");
    return denyAccess(context, mandatoryRole);
 }

 function denyAccess(context, mandatoryRole) {
    context.forkWithErrorMessage(new FormMessage('label', 'User is not allowed to access this client.'));
    //var formBuilder = context.form();
    //var client = session.getContext().getClient();
    //var description = !mandatoryRole.getAttribute('deniedMessage').isEmpty() ? mandatoryRole.getAttribute('deniedMessage') : [''];
    //var form = formBuilder
    //    .setAttribute('clientUrl', client.getRootUrl())
    //    .setAttribute('clientName', client.getName())
    //    .setAttribute('description', description[0])
    //    .createForm('denied-auth.ftl');
    //return context.failure(AuthenticationFlowError.INVALID_USER, form);
 }

Note: for keycloak in docker you need add this envs: -Dkeycloak.profile.feature.scripts=enabled -Dkeycloak.profile.feature.upload_scripts=enabled -server -Djboss.modules.system.pkgs=org.jboss.byteman

But it is really hackish solution, because you are doung authorization in the authentication.

I couldn’t find a better way.
I will be glad if you share the correct solution.

Identity provider provides only identity after successful authentication and some user details (e.g. which groups has user assigned).Then it is service responsibility to evaluate and do authorization, e.g. deny users without predefined group).

I have already configured and tested authorization via SAML with slack. It works.

Is it possible to share your sample configuration file?

I have some problem to config Slack and KeyCLoak SSO SAML:

Thanks.

Yes, that’s right. In theory. But the real world does not care about theory.
We had this discussion a few weeks ago…

Sure, small and carefree implementations don’t care. Imagine enterprise env, where you have a few thousands clients and you will use a Keycloak. You will implement authorization in the Keycloak with Javascript based authenticator (because that is a the easiest solution, which you can find on the Stackoverflow/community forum). You will be happy. Until you realize that feature of the javascript in the Keycloak is unclear. So what will you do if javascript support is removed. You can code it in the Java as SPI probably. What will you do if you need to move from Keycloak to another IdP? Or will you introduce vendor lock? Yes, hacking is easy if you don’t care about SLA, CAPEX, OPEX, standards, future, responsibility, resources, …

1 Like

As I wrote before, I absolutely agree, authorisation should happen at the client level. But, what do you want to do when a service has no authorisation capability and may never get it? You can leave it unauthorised with its problem (because in therory, it has to solve it on its own), or try to help it with its practical problem. And if the only option is to use a great keycloak feature (which hopefully will never be removed, I know…), then so be it. It’s not about fine-grained authorisation functions (for which there are even special functions in Keycloak, as far as I know, which only work with special Keycloak adapters, so much for vendor-lock-in…;-)), but only about letting certain user groups into the service at all or locking them out. Many services can’t do that and never will lern to do it.

And please, don’t accuse anyone of not caring about standards, resources, etc. On the subject of cost: do you have any idea how much it costs to have an attribute-driven user filter implemented at and by gitlab? This is not a small and carefree service. And yet completely authorisation-free. Service levels and responsibilities can often be better managed by a central professionally run AAI than by a small client service.

And tell me about a company-wide used central AAI where you don’t have vendor-lock in…

If app doesn’t support any authorization, then I would say this is a problem of service provider (SP), not an identity provider (IDP). As SP admin I would place it behind authorization proxy (e.g. GitHub - gogatekeeper/gatekeeper: A OpenID / Proxy service), where I as SP admin have a full control. I will configure authorisation (for example based on delivered group claim). In this case as IDP admin I can migrate IDP to any IDP with OIDC standard support easily (with minimal config update of SP side). SP authorisation will still be based on the group claim, which is standard IDP feature.

We are using proxies, too, for example apache with mod_oidc, oauth2-proxy, etcpp… Whenever the client service can work with an auth proxy, the proxy’s filter options are flexible enough and when the operating person of the client service is willing to operate an additional proxy…then we use a proxy…