Prevent password reset for federated users

4,770 views
Skip to first unread message

l.sc...@gmail.com

unread,
Aug 1, 2020, 7:53:12 PM8/1/20
to Keycloak User
Hi,
I found out that federated users can invoke the password reset mechanism and set a password for their account, and afterwards they can login locally with their federated username and chosen password.
Is there a way to prevent this behavior? I need federated accounts to be completely independent from local accounts.

Thanks for your help!
--
Luca

Garth

unread,
Aug 3, 2020, 9:52:35 AM8/3/20
to keyclo...@googlegroups.com
I am not an expert here, but we had a similar requirement (federated users from a set of SAML IdPs cannot login locally), and we had to hack Keycloak itself to make this work.

I have also heard another suggestion, which isn't ideal, but worked for someone in the same situation:
"set up 2 realms, A and B. A contains all users that log in with username and password and is an identity provider to realm B" https://lists.jboss.org/pipermail/keycloak-user/2017-December/012599.html

Unless it's somehow already available that I just haven't seen, I'd love to see this use case solved somehow in Keycloak so we don't have to continue with our custom version. +1 to asking the Keycloak experts/maintainers to comment here.
> --
> 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/130b61de-8bb4-4a5a-8764-83603abedf39n%40googlegroups.com <https://groups.google.com/d/msgid/keycloak-user/130b61de-8bb4-4a5a-8764-83603abedf39n%40googlegroups.com?utm_medium=email&utm_source=footer>.

l.sc...@gmail.com

unread,
Aug 3, 2020, 7:38:41 PM8/3/20
to Keycloak User
Would you mind sharing your changes, or the general approach you used? If it could be conditionally enabled maybe a patch would be considered.
And thanks for the possible solution of introducing a second realm! I'll try to reason on it and see if it makes sense in my scenario.

Hope to see this feature officially supported!

Garth

unread,
Aug 5, 2020, 2:23:36 PM8/5/20
to keyclo...@googlegroups.com
Our hack is not suitable for a patch. We created a custom entity to store when a first login comes from an IdP, and then just hacked the forgot password and account management flows to throw an error if there is a row for that user.

However, I do think this is a common use case, and has been a consistent objection/complaint in many Keycloak installations I have done.
> https://groups.google.com/d/msgid/keycloak-user/e0e69506-9ede-4565-a5a6-f81ada1197e8n%40googlegroups.com <https://groups.google.com/d/msgid/keycloak-user/e0e69506-9ede-4565-a5a6-f81ada1197e8n%40googlegroups.com?utm_medium=email&utm_source=footer>.

Eric Peters

unread,
Jan 18, 2021, 3:09:05 PM1/18/21
to Keycloak User
I'm trying to explore this same issue. 

Garth, can you explain how you did the hack for the forgot password and account management flows in keycloak?

A few gist code examples would go a long way!

Thanks,

Eric

Thomas Darimont

unread,
Jan 18, 2021, 4:16:03 PM1/18/21
to Eric Peters, Keycloak User
Hello,

I think you need to guard two different operations:
1) Forgot Password / Password Reset
2) Password change via account console

1) Deny Forgot Password / Password Reset for brokered users
You can implement a custom ResetCredentialEmail Authenticator, that rejects
password recovery operations if the given user is a brokered user.

IdP brokered users could be marked with a broker specific realm-role (or a user attribute).

This could be done by assigning a hardcoded role / attribute to users that come in via IdP.
org.keycloak.authentication.authenticators.resetcred.ResetCredentialEmail

This custom authenticator can then be used in a custom Reset Credentials
Authentication flow binding.

Something like this might do the trick:
-
class CustomResetCredentialEmail extends ResetCredentialEmail {


    public void authenticate(AuthenticationFlowContext context) {
      // ... some additional checks here...
      if (userIsBrokeredUser(context.getUser())) {
        // reject reset password
        return;
      }

      super.authenticate(context);
    }
}

2) Deny Password change via account-console for brokered users
This can be done by removing the "manage-account" role from the "account" client
for IdP-Brokered users with a custom IdP broker mapper.
Without the "manage-account" role, users cannot change their account.

Unfortunately, there is no fine-grained role like "manage-account-password", "manage-account-credentials", "manage-account-mfa" at the moment.

I also agree that this is a common requirement in customer projects.

Perhaps the ResetCredentialEmail Authenticator in the reset credentials flow
could be guarded with a configurable condition like "require role" or "not in role".

Additionally, Identity Providers could provide a configurable option to control
whether default realm roles are applied or not. If no default realm roles are applied
it should be possible to explicitly define the realm roles for users brokered via a particular IdP.

Cheers,
Thomas


Geoffrey Cleaves

unread,
Jan 9, 2023, 5:44:43 AM1/9/23
to Keycloak User
I'll just add my two cents here to say that this is a sorely needed enhancement. 

As a SaaS vendor offering "SSO" functionality to enterprise customers, I see that these customers expect SSO to ensure that their employees can't access the SaaS platform once the employee is suspended. At the moment Keycloak makes it very hard to satisfy the requirement.

Thomas has some good ideas, I hope they get implemented.

Geoffrey Cleaves

unread,
Jan 9, 2023, 7:28:48 AM1/9/23
to Schuster Sebastian (BD/PAU1), Keycloak User
Thanks Sebastion, this was useful information. Indeed disabling the “Update Password” required action removes the frontend option for a user to create herself a local username/password in the account console, or edit an existing one. However, I enabled “Update Password” just long enough to access the change password form (without submitting) and then disabled the required action “Update Password” before submitting the form, and finally clicked Submit. Unfortunately the password changed anyway. I would have hoped to receive an error from the backend indicating the password change was impossible. This all makes me suspect that even with the “Update Password” required action disabled, a malicious user might figure out a way.

The other weakness of using the “Update Password” required action is that it affects the whole realm. We only want to affect users coming from specific IdPs.

Perhaps an alternative to prevent password logins from select users is with either a custom password policy or custom authenticator. The password policy would check whether the user has a certain attribute added by the IDP mapper and reject all passwords. The custom authenticator would check for the same attribute and reject any login attempt when using local username/password. However I'm not sure whether the authenticator context is aware that a local username/password is being used instead of the federated IdP.

Geoff


On Mon, Jan 9, 2023 at 12:45 PM Schuster Sebastian (BD/PAU1) <Sebastian...@bosch.com> wrote:

AFAIK it is sufficient to just disable the “Update Password” required action on a realm. Then users cannot change their password (only using the old account console, but not the new one).

 

Best regards,

Sebastian

 

Mit freundlichen Grüßen / Best regards

Dr.-Ing. Sebastian Schuster
 

Product Area User Management (BD/PAU1)
Bosch.IO GmbH | Ullsteinstr. 128 | 12109 Berlin | GERMANY | www.bosch.io
Tel. +49 30 726112-485 | Mobil +49 152 02177668 | Telefax +49 30 726112-100Sebastian...@bosch.io


Sitz: Berlin, Registergericht: Amtsgericht Charlottenburg; HRB 148411 B
Aufsichtsratsvorsitzender: Stefan Koss; Geschäftsführung: Dr. Andreas Nauerz, Stephan Lampel 

Geoffrey Cleaves

unread,
Jan 10, 2023, 6:59:12 AM1/10/23
to Schuster Sebastian (BD/PAU1), Keycloak User
Great success. I created a simple Javascript provider ("My Authenticator" below) and inserted it into the Browser flow just after "Username Password Form".

Screenshot 2023-01-10 at 12.40.35.png

Contents of javascript file:

AuthenticationFlowError = Java.type("org.keycloak.authentication.AuthenticationFlowError");
function authenticate(context) {
  LOG.info(script.name + " --> trace auth for: " + user.username);
  if ( user.getAttributes().no_local ) {
      context.failure(AuthenticationFlowError.INVALID_USER);
      return;
  }
  context.success();
}

I've instructed Keycloak to automatically add the no_local attribute to users coming from an IDP. This attribute is checked by the JS provider and blocks anybody with that attribute coming from the Username/Password form. I don't need to worry about the Update Password Required Action or manage-account role. 

The real pain is getting JS to work in Keycloak but following the instructions found in Server Developer helped. Getting the provider into the correct place in the Browser flow is not easy, with Keycloak throwing all sorts of errors if you drag it to the wrong place, but eventually it worked. I suppose a Keycloak Java developer would have many fewer issues.

If anybody sees a problem with this approach, please let me know.

Geoff

Maxwell Famoriyo

unread,
Nov 10, 2023, 1:02:19 AM11/10/23
to Keycloak User
Hello everyone,

I tried my best to follow your advices. I'm still stuck on this case. On the old keycloak version (6.0.0) I duplicated and modified the reset credential flow. I added this custom script:

// Import the AuthenticationFlowError class from Java

AuthenticationFlowError = Java.type("org.keycloak.authentication.AuthenticationFlowError");

// Custom authentication function
function authenticate(context) {
    // Retrieve federated identities of the current user
    var myUsers = session.users().getFederatedIdentities(user, realm);

    // Variable to store the identity provider
    var idp;

    // Iterate over federated identities to extract the identity provider
    myUsers.forEach(extractSetElement);

    // Check the identity provider
    if (idp && idp !== 'toto' && idp !== 'tata') {
        // Log the denial of password reset due to the identity provider
        LOG.info("denying password reset due to identity provider : " + idp)

        // Trigger an authentication error
        context.failure(AuthenticationFlowError.IDENTITY_PROVIDER_DISABLED);
        return;
    }

    // Authentication successful
    context.success();

    // Function to extract the identity provider from a federated identity
    function extractSetElement(value) {
        idp = value.getIdentityProvider();
    }
}

This script doesn't work anymore with the keycloak version 21.0.x

If someone can help me on this case, it will make me smile again !

Thank you,
Reply all
Reply to author
Forward
0 new messages