RequiredAction to fire on every login

870 views
Skip to first unread message

Marc L

unread,
May 18, 2021, 5:30:01 AM5/18/21
to Keycloak User
We have a RequiredAction that we need to prompt the user with on every login.
It asks a question (their current location), and we update a user attribute with the answer.

I use the "evaluateTriggers" method to add the required action.
Initially, I just had:

@Override
    public void evaluateTriggers(RequiredActionContext context) {
  context.getUser().addRequiredAction(SelectOrganisationRequiredAction.PROVIDER_ID);
    }

However, this meant when the answer was completed we were prompted to complete it again - it didn't allow us to progress through login. It just kept asking.

What is the best way to ensure the RequiredAction is only asked for only once on every login?

Garth

unread,
May 18, 2021, 5:43:54 AM5/18/21
to keyclo...@googlegroups.com
I had a similar problem that I solved using a custom Authenticator in the login flow.

- authenticate and action methods return context.attempted()
- configuredFor returns false
- setRequiredActions adds the required action using user.addRequiredAction

This will add the required action once, without the problem you had with using evaluateTriggers
> --
> You received this message because you are subscribed to the Google
> Groups "Keycloak User" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to keycloak-use...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/keycloak-user/2e03074b-7150-4303-a31f-aafba46ab68dn%40googlegroups.com <https://groups.google.com/d/msgid/keycloak-user/2e03074b-7150-4303-a31f-aafba46ab68dn%40googlegroups.com?utm_medium=email&utm_source=footer>.

Marc L

unread,
May 18, 2021, 9:36:12 AM5/18/21
to Keycloak User
Hi Garth,

AuthenticationFlowError = Java.type("org.keycloak.authentication.AuthenticationFlowError");
function authenticate(context) {
   LOG.info(script.name + " --> trace authenticate for: " + user.username);
   context.attempted();
 
}
function configuredFor(session, realm, user) {
    LOG.info(script.name + " --> trace configuredFor for: " + user.username);
    return false;
}
function setRequiredActions(session, realm, user) {
    LOG.info(script.name + " --> trace setRequiredActions for: " + user.username);
    user.addRequiredAction("select_organisation");
}
I have the script set up, and the provider added to the flow.

However, following username/pwd auth, the user gets an error about invalid username and password.

The logs show:

2021-05-18 13:17:56,492 ERROR [stderr] (default task-36) Warning: Nashorn engine is planned to be removed from a future JDK release
2021-05-18 13:17:56,938 INFO  [org.keycloak.authentication.authenticators.browser.ScriptBasedAuthenticator] (default task-36) Select Organisation Authenticator --> trace authenticate for: test
2021-05-18 13:17:56,947 WARN  [org.keycloak.services] (default task-36) KC-SERVICES0013: Failed authentication: org.keycloak.authentication.AuthenticationFlowException
        at org.keycloak.ke...@12.0.4//org.keycloak.authentication.AuthenticationProcessor.authenticationAction(AuthenticationProcessor.java:943)
        at org.keycloak.ke...@12.0.4//org.keycloak.services.resources.LoginActionsService.processFlow(LoginActionsService.java:311)
        at org.keycloak.ke...@12.0.4//org.keycloak.services.resources.LoginActionsService.processAuthentication(LoginActionsService.java:282)
        at org.keycloak.ke...@12.0.4//org.keycloak.services.resources.LoginActionsService.authenticate(LoginActionsService.java:266)
        at org.keycloak.ke...@12.0.4//org.keycloak.services.resources.LoginActionsService.authenticateForm(LoginActionsService.java:339)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)


So it looks like the script is getting hit, but I'm really struggling to work out why it is erroring.

Garth

unread,
May 18, 2021, 10:20:42 AM5/18/21
to keyclo...@googlegroups.com

Marc L

unread,
May 18, 2021, 10:45:53 AM5/18/21
to Keycloak User
I have had a play with this, and when I add it as an execution in the top level before "Browser With Org Forms" and below "Identity Provider Redirector", set as "Alternative", I get prompted for a username/password on login, but never get asked for the organisation.

alternative.PNG


My desired workflow is:

1) All users are asked for username/password
2) If OTP is configured, then they next get asked for OTP
3) All users are asked for their organisation

Is this going to be achievable do you think?

Garth

unread,
May 18, 2021, 10:53:45 AM5/18/21
to keyclo...@googlegroups.com
That should run your Authenticator. Can you confirm it is running the setRequiredActions method?

On Tue, May 18, 2021, at 4:45 PM, Marc L wrote:
> I have had a play with this, and when I add it as an execution in the
> top level before "Browser With Org Forms" and below "Identity Provider
> Redirector", set as "Alternative", I get prompted for a
> username/password on login, but never get asked for the organisation.
>
> https://groups.google.com/d/msgid/keycloak-user/d91e91d2-0781-4d12-a3c2-c0b7df76a609n%40googlegroups.com <https://groups.google.com/d/msgid/keycloak-user/d91e91d2-0781-4d12-a3c2-c0b7df76a609n%40googlegroups.com?utm_medium=email&utm_source=footer>.
> Attachments:
> * alternative.PNG

Garth

unread,
May 18, 2021, 11:02:33 AM5/18/21
to keyclo...@googlegroups.com
I see. It looks like you require the user in your action, so it will need to be under the browser forms. Let me prototype something quickly and return to you.
> https://groups.google.com/d/msgid/keycloak-user/2e2baec4-ebb0-48dd-b9cd-bdce41e4bd74%40www.fastmail.com.
>

Garth

unread,
May 18, 2021, 11:31:52 AM5/18/21
to keyclo...@googlegroups.com
Looks like the best way to do it in this context is to add it to the AuthenticationSessionModel in the authenticate() method. There is a good example in the keycloak code here:

https://github.com/keycloak/keycloak/blob/master/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetPassword.java
> https://groups.google.com/d/msgid/keycloak-user/646db45a-d8ac-43c1-8f1b-b76eaa6a3e31%40www.fastmail.com.
>

Marc L

unread,
May 18, 2021, 3:48:00 PM5/18/21
to Keycloak User
Hi,

I've tried that to mirror that example as best I can in script.

I've hit the same issue as before:- when it's Required I hit an error, and when Alternative it skips - no matter where I place it in the flow.

Appreciate the help

Garth

unread,
May 18, 2021, 4:37:43 PM5/18/21
to keyclo...@googlegroups.com
This was how I added mine (see screenshot).

Sorry I don't have any experience using js for writing these, only Java.

Your RequiredAction should override the initiatedActionSupport method for it to work properly:

@Override
public InitiatedActionSupport initiatedActionSupport() {
return InitiatedActionSupport.SUPPORTED;
}

I can try to post a full example, but not until tomorrow.
> https://groups.google.com/d/msgid/keycloak-user/c9ee426d-3f9c-462a-be62-3f6f268e24e8n%40googlegroups.com <https://groups.google.com/d/msgid/keycloak-user/c9ee426d-3f9c-462a-be62-3f6f268e24e8n%40googlegroups.com?utm_medium=email&utm_source=footer>.
Screen Shot 2021-05-18 at 10.35.11 PM.png
Reply all
Reply to author
Forward
0 new messages