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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)lists.jboss.org
>>>>>> >
https://lists.jboss.org/mailman/listinfo/keycloak-user
>>>>>>
>>>>>>
>>>>>> _______________________________________________
>>>>>> keycloak-user mailing list
>>>>>> keycloak-user(a)lists.jboss.org
>>>>>>
https://lists.jboss.org/mailman/listinfo/keycloak-user
>>>>>>
>>>>>
>>>>>
>>>
>>
>