[keycloak-user] Restrict access to a client to a subset of Keycloak users

Shane Boulden shane.boulden at gmail.com
Mon Feb 27 02:20:40 EST 2017


Hey everyone,

I didn't manage to find the cause of my 'ClassNotFoundException', but
managed to work around it by passing the expected JSON directly to the
Response builder:

~~~
AuthenticationFlowError = Java.type("org.keycloak.authentication.
AuthenticationFlowError");
Response = Java.type("javax.ws.rs.core.Response");
MediaType = Java.Type("javax.ws.rs.core.MediaType");

function authenticate(context) {
  var groups = user.getGroups();
  var group_array = groups.toArray();

  for (var i in group_array) {
    var gn = group_array[i].getName();

    if (gn === "openshift-access") {
      context.success();
      return;
    }
  }

  response = Response.status(401).entity("{\"error\":\"invalid_grant\",\"
error_description\":\"invalid_user_credentials\"}").type(
MediaType.APPLICATION_JSON_TYPE).build();
  context.failure(AuthenticationFlowError.INVALID_CREDENTIALS, response);
  return;
}
~~~

Now when a user attempts to 'oc login' to OpenShift, and they're not in the
correct Keycloak group, they receive a '401 Unauthorized' rather than a 500
error.

Hope this helps someone else.

Shane



On Sat, Feb 25, 2017 at 8:47 AM, Shane Boulden <shane.boulden at gmail.com>
wrote:

> Awesome, thanks Thomas!
>
> Do you have any suggestions on my 'ClassNotFoundException' for
> org.keycloak.representations.idm.OAuth2ErrorRepresentation? This is on
> 2.5.1.
>
> Oauth2 = Java.type("org.keycloak.representations.idm.OAuth2ErrorRepre
> sentation");
>
> Shane
>
> On Sat, Feb 25, 2017 at 12:23 AM, Thomas Darimont <
> thomas.darimont at googlemail.com> wrote:
>
>> PR sent: https://issues.jboss.org/browse/KEYCLOAK-4505
>>
>> With that PR applied I can do the following:
>>
>> /*
>>  * Template for JavaScript based authenticator's.
>>  * See org.keycloak.authentication.authenticators.browser.ScriptBas
>> edAuthenticatorFactory
>>  */
>>
>> // import enum for error lookup
>> AuthenticationFlowError = Java.type("org.keycloak.authen
>> tication.AuthenticationFlowError");
>> OAuth2ErrorRepresentation = Java.type("org.keycloak.repres
>> entations.idm.OAuth2ErrorRepresentation");
>> Response = Java.type("javax.ws.rs.core.Response");
>> MediaType = Java.type("javax.ws.rs.core.MediaType");
>>
>> /**
>>  * An example authenticate function.
>>  *
>>  * The following variables are available for convenience:
>>  * user - current user {@see org.keycloak.models.UserModel}
>>  * realm - current realm {@see org.keycloak.models.RealmModel}
>>  * session - current KeycloakSession {@see org.keycloak.models.KeycloakSe
>> ssion}
>>  * clientSession - current client session {@see
>> org.keycloak.models.ClientSessionModel}
>>  * httpRequest - current HttpRequest {@see org.jboss.resteasy.spi.HttpReq
>> uest}
>>  * script - current script {@see org.keycloak.models.ScriptModel}
>>  * LOG - current logger {@see org.jboss.logging.Logger}
>>   * You one can extract current http request headers via:
>>  * httpRequest.getHttpHeaders().getHeaderString("Forwarded")
>>  *
>>  * @param context {@see org.keycloak.authentication.Au
>> thenticationFlowContext}
>>  */
>> function authenticate(context) {
>>
>>     var username = user ? user.username : "anonymous";
>>     LOG.info(script.name + " trace auth for: " + username);
>>     LOG.info(script.name + " client session for client: " +
>> clientSession.client.clientId);
>>
>>     var groups = user.getGroups();
>>     var group_array = groups.toArray();
>>
>>     var authShouldFail = true;
>>     for (var i in group_array) {
>>       var gn = group_array[i].getName();
>>       LOG.info(script.name + " group name: " + gn);
>>       if (gn === "account-access") {
>>         authShouldFail = false;
>>         break;
>>       }
>>     }
>>
>>     if (authShouldFail
>>      //&& clientSession.client.clientId === "dummy-account"
>>      ) {
>>
>>         var errorRep = new OAuth2ErrorRepresentation("inv
>> alid_grant","invalid_user_credentials");
>>         var response = Response.status(401).entity(er
>> rorRep).type(MediaType.APPLICATION_JSON_TYPE).build();
>>
>>         LOG.info(script.name + " failed auth for: " + username);
>>         context.failure(AuthenticationFlowError.INVALID_USER, response);
>>         return;
>>     }
>>
>>     context.success();
>> }
>>
>>
>>
>> 2017-02-24 12:19 GMT+01:00 Thomas Darimont <thomas.darimont at googlemail.co
>> m>:
>>
>>> FYI I just gave this a spin...
>>>
>>> It seems that the ScriptAuthenticator currently has no binding for
>>> clientSession in order to access the client id for authentication,
>>> e.g. this is missing in ScriptBasedAuthenticator
>>>             bindings.put("clientSession", context.getClientSession());
>>>
>>> I'll send a PR which adds that binding. This will then enable to provide
>>> client specific authentication behaviour.
>>>
>>> Chreers,
>>> Thomas
>>>
>>> 2017-02-24 11:33 GMT+01:00 Shane Boulden <shane.boulden at gmail.com>:
>>>
>>>> I got this working today with a custom auth flow, thanks heaps!
>>>>
>>>> Just one thing - I've copied the 'Direct Grant Flow', and added a JS
>>>> script at the end to only allow certain groups to authenticate using the
>>>> OpenShift 'oc login' command from a prompt.
>>>>
>>>> This works allowing/denying access based on a group, however when a
>>>> user does not belong to the correct group, the oc login prompt displays the
>>>> following error:
>>>>
>>>> "Error from server: Internal error: unexpected error: 500"
>>>>
>>>> Here's the code I used for my JS script:
>>>>
>>>> function authenticate(context){
>>>>   var groups = user.getGroups();
>>>>   var group_array = groups.toArray();
>>>>
>>>>   for (var i in group_array) {
>>>>     var gn = group_array[i].getName();
>>>>
>>>>     if (gn === "openshift-access") {
>>>>       context.success();
>>>>       return;
>>>>     }
>>>>   }
>>>>   context.failure(authenticationflowerror.INVALID_USER)
>>>>   return;
>>>> }
>>>>
>>>> I thought this may be because the OpenShift CLI tool can't interpret
>>>> the error message back from Keycloak. I've also tried the following, but I
>>>> get a "ClassNotFound" exception when I try to import the OAuth2 error
>>>> representation:
>>>>
>>>> Authenticationflowerror = Java.type("org.keycloak.authen
>>>> tication.AuthenticationFlowError");
>>>> // Throws 'ClassNotFoundException
>>>> Oauth2 = Java.type("org.keycloak.representations.idm.OAuth2ErrorRepre
>>>> sentation");
>>>> Response = Java.type("javax.ws.rs.core.Response");
>>>> MediaType = Java.Type("javax.ws.rs.core.MediaType");
>>>>
>>>> function authenticate(context) {
>>>>   var groups = user.getGroups();
>>>>   var group_array = groups.toArray();
>>>>
>>>>   for (var i in group_array) {
>>>>     var gn = group_array[i].getName();
>>>>
>>>>     if (gn === "openshift-access") {
>>>>       context.success();
>>>>       return;
>>>>     }
>>>>   }
>>>>   var errorRep = new Oauth2("invalid_grant","invali
>>>> d_user_credentials");
>>>>   response = Response.status(401).type(Medi
>>>> aType.APPLICATION_JSON_TYPE).build();
>>>>
>>>>   context.failure(AuthenticationFlowError.INVALID_CREDENTIALS,
>>>> response);
>>>>   return;
>>>> }
>>>>
>>>> Any ideas or assistance is appreciated.
>>>>
>>>> Shane
>>>>
>>>> On Fri, Feb 24, 2017 at 5:16 AM, Shane Boulden <shane.boulden at gmail.com
>>>> > wrote:
>>>>
>>>>> Thanks very much Marek and Thomas for taking the time to get back to
>>>>> me.
>>>>>
>>>>> I've found an example of a JS authenticator here:
>>>>>  http://www.lookatsrc.com/source/scripts/authenticator-templ
>>>>> ate.js?a=org.keycloak:keycloak-services
>>>>>
>>>>> Is this how I would build the custom authenticator, and extend it to
>>>>> check the user roles and clientID?
>>>>>
>>>>> Thanks
>>>>>
>>>>> Shane
>>>>>
>>>>> On 24 Feb. 2017 01:25, "Thomas Darimont" <
>>>>> thomas.darimont at googlemail.com> wrote:
>>>>>
>>>>>> Hello Shane,
>>>>>>
>>>>>> you could try to do that with the Javascript based Authenticator.
>>>>>>
>>>>>> Cheers,
>>>>>> Thomas
>>>>>>
>>>>>> 2017-02-23 14:07 GMT+01:00 Marek Posolda <mposolda at redhat.com>:
>>>>>>
>>>>>>> I can think of some workarounds. Like for example, create an
>>>>>>> Authenticator, which will be added to the bottom of the
>>>>>>> authentication
>>>>>>> flow. Authenticator will throw an exception in case that unpermitted
>>>>>>> user is trying to authenticate to the client corresponding to your
>>>>>>> openshift application. You have the user available (he is already
>>>>>>> authenticated) and you have also the client (can be determined based
>>>>>>> on
>>>>>>> clientId).
>>>>>>>
>>>>>>> Maybe even easier is to do that in custom RequiredActionProvider and
>>>>>>> do
>>>>>>> this check in "evaluateTriggers".
>>>>>>>
>>>>>>> This is workaround as it mixes authentication and authorization
>>>>>>> (among
>>>>>>> other issues). But hopefully it can suit your needs.
>>>>>>>
>>>>>>> Marek
>>>>>>>
>>>>>>> On 23/02/17 07:19, Shane Boulden wrote:
>>>>>>> > Hi everyone,
>>>>>>> >
>>>>>>> > I'm trying to figure out a fairly straight-forward problem set -
>>>>>>> >
>>>>>>> >     - I have a number of users in a Keycloak database, federated
>>>>>>> from an
>>>>>>> >     LDAP provider with a READ_ONLY policy (ie; I can't "disable"
>>>>>>> the users)
>>>>>>> >     - I want to limit access to a client to only certain Keycloak
>>>>>>> users
>>>>>>> >
>>>>>>> > I thought this would be possible with a role that is shared by the
>>>>>>> client
>>>>>>> > and the user. However, it looks like Keycloak lets the application
>>>>>>> itself
>>>>>>> > determine access via a role: http://lists.jboss.org/
>>>>>>> > pipermail/keycloak-user/2014-November/001205.html
>>>>>>> >
>>>>>>> > But what if I can't update the application's behaviour? Eg; if I
>>>>>>> want to
>>>>>>> > integrate Keycloak with OpenShift, and OpenShift doesn't consume
>>>>>>> any
>>>>>>> > information from the OIDC provider?
>>>>>>> >
>>>>>>> > In this particular example, I don't want to limit the users in the
>>>>>>> Keycloak
>>>>>>> > database - I want to sync all users from LDAP, but limit
>>>>>>> application access
>>>>>>> > to only a subset.
>>>>>>> >
>>>>>>> > Any assistance is greatly appreciated.
>>>>>>> >
>>>>>>> > Shane
>>>>>>> > _______________________________________________
>>>>>>> > keycloak-user mailing list
>>>>>>> > keycloak-user at lists.jboss.org
>>>>>>> > https://lists.jboss.org/mailman/listinfo/keycloak-user
>>>>>>>
>>>>>>>
>>>>>>> _______________________________________________
>>>>>>> keycloak-user mailing list
>>>>>>> keycloak-user at lists.jboss.org
>>>>>>> https://lists.jboss.org/mailman/listinfo/keycloak-user
>>>>>>>
>>>>>>
>>>>>>
>>>>
>>>
>>
>


More information about the keycloak-user mailing list