[keycloak-user] How to dynamically trigger a custom required action in a flow ?

GESLIN Fabrice fabrice.geslin-prestataire at laposte.fr
Thu May 2 11:00:38 EDT 2019


Hi Sébastien,

JIRA https://issues.jboss.org/browse/KEYCLOAK-10182 created accordingly.

Regards,

Fabrice Geslin

Groupe La Poste
________________________________
De : Sebastien Blanc <sblanc at redhat.com>
Envoyé : mardi 30 avril 2019 10:19
À : GESLIN Fabrice
Cc : keycloak-user at lists.jboss.org
Objet : Re: [keycloak-user] How to dynamically trigger a custom required action in a flow ?

If you believe their is a inconsistency please open a JIRA, tbh I don't know that much about the implementation details of this part.
You probably have but worth asking : have you set the priority of your custom required action ? I can see that "Update password" has prio 30 , so your custom action should be >30 (0 is the highest priority) if you want it to be run after.
And regarding sort by names and priority, if I look at the implementation of the comparator it looks like it first sort by prio and after that by name (if they have the same prio).




On Tue, Apr 30, 2019 at 9:43 AM GESLIN Fabrice <fabrice.geslin-prestataire at laposte.fr<mailto:fabrice.geslin-prestataire at laposte.fr>> wrote:

Hi Sébastien,



We’ve finally found the addRequiredAction() function with the String parameter and it works.



But we felt into a new issue due to the fact that the required actions are sometime handled sorted and sometime not sorted.



For instance, at the end of the authentication flow processing, when the required actions are processed, they are treated in an arbitrary order that doesn’t even correspond to the order in which they were added.  In Keycloak release 4.8.3.FINAL, The call stack leads to line 893 of org.keycloak.services.managers.AuthenticationManager that is:



    public static String nextRequiredAction(final KeycloakSession session, final AuthenticationSessionModel authSession,

                                            final ClientConnection clientConnection,

                                            final HttpRequest request, final UriInfo uriInfo, final EventBuilder event) {

        final RealmModel realm = authSession.getRealm();

        final UserModel user = authSession.getAuthenticatedUser();

        final ClientModel client = authSession.getClient();



        evaluateRequiredActionTriggers(session, authSession, clientConnection, request, uriInfo, event, realm, user);



        if (!user.getRequiredActions().isEmpty()) {

            return user.getRequiredActions().iterator().next();

        }

        if (!authSession.getRequiredActions().isEmpty()) {

            return authSession.getRequiredActions().iterator().next();

        }



This causes the user to be redirected to the URI of the required action that has been arbitrarily selected. But when the browser GET the corresponding URI, the call stack reaches line 1045 of org.keycloak.services.managers.AuthenticationManager where the required actions are sorted according to their priorities or their names (?!?):



    protected static Response executionActions(KeycloakSession session, AuthenticationSessionModel authSession,

                                               HttpRequest request, EventBuilder event, RealmModel realm, UserModel user,

                                               Set<String> requiredActions) {



        List<RequiredActionProviderModel> sortedRequiredActions = sortRequiredActionsByPriority(realm, requiredActions);



        for (RequiredActionProviderModel model : sortedRequiredActions) {

            RequiredActionFactory factory = (RequiredActionFactory)session.getKeycloakSessionFactory().getProviderFactory(RequiredActionProvider.class, model.getProviderId());

            if (factory == null) {

                throw new RuntimeException("Unable to find factory for Required Action: " + model.getProviderId() + " did you forget to declare it in a META-INF/services file?");

            }

            RequiredActionContextResult context = new RequiredActionContextResult(authSession, realm, event, session, request, user, factory);

            RequiredActionProvider actionProvider = null;

            try {

                actionProvider = createRequiredAction(context);

            } catch (AuthenticationFlowException e) {

                if (e.getResponse() != null) {

                    return e.getResponse();

                }

                throw e;

            }

            actionProvider.requiredActionChallenge(context);



The side effect of this inconsistency is that in our case, where we’ve added a custom required action after the UPDATE_PASSWORD required action to the reset credential flow, the user is first redirected to the URI of our custom action (which is not what we expected) AND the form that is challenged to the user once redirected is the one from the UPDATE_PASSWORD action !



The question is how can we have the required actions that are added to a flow be processed according to the order in which they are added ?



Regards,



Fabrice Geslin



Groupe La Poste



De : Sebastien Blanc [mailto:sblanc at redhat.com<mailto:sblanc at redhat.com>]
Envoyé : lundi 29 avril 2019 14:35
À : GESLIN Fabrice <fabrice.geslin-prestataire at laposte.fr<mailto:fabrice.geslin-prestataire at laposte.fr>>
Cc : keycloak-user at lists.jboss.org<mailto:keycloak-user at lists.jboss.org>
Objet : Re: [keycloak-user] How to dynamically trigger a custom required action in a flow ?



Hi,



When you says it does not accept it, you have an exception at runtime ? Because you have addRequiredAction(String string)



On Mon, Apr 29, 2019 at 12:13 PM GESLIN Fabrice <fabrice.geslin-prestataire at laposte.fr<mailto:fabrice.geslin-prestataire at laposte.fr>> wrote:

Hi,

We're trying to trigger a custom required action  as part of the reset credential.

For this we plan to mimic the implementation of the authenticate method of the org.keycloak.authentication.authenticators.resetcred.ResetPassword.java :

    @Override
    public void authenticate(AuthenticationFlowContext context) {
        if (context.getExecution().isRequired() ||
                (context.getExecution().isOptional() &&
                        configuredFor(context))) {
            context.getAuthenticationSession().addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
        }
        context.success();
    }


But the question is what value should we pass to the addRequiredAction() ?

This method seems to only accept the predefined required actions mapped to the values from the  UserModel.RequiredAction enum.

Any help is welcome .

Fabrice Geslin

Groupe La Poste

Post-scriptum La Poste

Ce message est confidentiel. Sous reserve de tout accord conclu par
ecrit entre vous et La Poste, son contenu ne represente en aucun cas un
engagement de la part de La Poste. Toute publication, utilisation ou
diffusion, meme partielle, doit etre autorisee prealablement. Si vous
n'etes pas destinataire de ce message, merci d'en avertir immediatement l'expediteur.
_______________________________________________
keycloak-user mailing list
keycloak-user at lists.jboss.org<mailto:keycloak-user at lists.jboss.org>
https://lists.jboss.org/mailman/listinfo/keycloak-user

Post-scriptum La Poste

Ce message est confidentiel. Sous reserve de tout accord conclu par
ecrit entre vous et La Poste, son contenu ne represente en aucun cas un engagement de la part de La Poste. Toute publication, utilisation ou diffusion, meme partielle, doit etre autorisee prealablement. Si vous n'etes pas destinataire de ce message, merci d'en avertir immediatement
l'expediteur.

Post-scriptum La Poste

Ce message est confidentiel. Sous reserve de tout accord conclu par
ecrit entre vous et La Poste, son contenu ne represente en aucun cas un engagement de la part de La Poste. Toute publication, utilisation ou diffusion, meme partielle, doit etre autorisee prealablement. Si vous n'etes pas destinataire de ce message, merci d'en avertir immediatement
l'expediteur.


More information about the keycloak-user mailing list