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.
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
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).
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, âŚ
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âŚ