This keycloak extension (currenty developed and tested against versions 6, 8-15, 18) aims to create the possibility to assign additional password rules to user groups, extending the rules attached to the realm, not replacing them.
The extension registers a class implementing org.keycloak.policy.PasswordPolicyProviderFactory. It is available as a new type of password policy on the realm's password policy sub-page.
The extension can be installed just like any keycloak extension. Either copy it to the
keycloak/standalone/deployments
folder for JBoss deployments or in keycloak/deployments
for quarkus distributions, or load it via the jboss command line tool.
There are multiple steps you will want to take to use this plugin. First, you need to determine
what password policies you will want for all users and for each group of users. Once you have
that, you will need to come up with an ID where you will specify group password policies. For
the purposes of this documentation we will use the ID passwordPolicy
.
Go to the realm's password policy page. In the latest versions of Keycloak, this can be found by navigating to the "Authentication" menu item in the vertical menu on the left side of the realm's user interface. You will then need to navigate to the "Password Policy" tab along the menu of tabs on the top of the page.
This interface provides you the OOTB ability to specify password policies for all users.
This is still true with the plugin installed. You will now have an additional option: Group
Policy. To use the plugin, you must add that password policy. The "Policy Value" should be
set to the ID we came up with earlier: passwordPolicy
.
If you intend to use group-specific password expiration (forceExpiredPasswordChange
), you will
need to perform an additional step in the configuration. In the same "Authentication" section,
navigate to the "Required Actions" tab along the menu of tabs on the top of the page. Use the
"Register" button to add the "Group-based Expired Password" action. Move it up in the order to
after the "Update Password" action.
At this point, you will need to add an attribute (with key passwordPolicy
) to each group you
want to have additional password policies. The format of that text is defined by Keycloak
documentation and covered in the section below.
All policies are represented by a short string immediately followed by parenthesis, optionally
containing configuration data. All policies are then concatenated using the fixed string " and "
.
For example:
length(8) and digits(2) and lowerCase(2) and upperCase(2) and specialChars(2) and notUsername()
The policies provided with KeyCloak are:
Identifier | Parameter description | Tested |
---|---|---|
length(int) |
minimum number of unicode characters | ✓ |
digits(int) |
minimum number of digits | ✓ |
lowerCase(int) |
minimum number of lower case unicode characters | ✓ |
upperCase(int) |
minimum number of upper case unicode characters | ✓ |
specialChars(int) |
minumum number of special characters | ✓ |
regexPattern(string) |
regular expression | ✓ |
notUsername() |
✓ | |
passwordBlacklist(string) |
file name | - |
passwordHistory(int) |
number of last used passwords to disallow | ✓ |
forceExpiredPasswordChange(string) |
number of days to expire password after | ✓ |
On the realm model the password policy attribute is also used for other purposes. There are some registered "policies", that do not actually implement a policy that a password is checked against, but when it has to be changed and how it is stored.
If these currently work is completely untested.
Identifier | Description | Tested |
---|---|---|
hashAlgorithm(string) |
hash algorithm to use when hashing the password | - |
hashIterations(int) |
number of hash iterations | - |
To minimize code duplication the extension uses as much of the built-in KeyCloak code as possible. The parsing and instantiation of the policy provider classes is used as-is.
As all policies are configured via the RealmModel, a custom implementation of the RealmModel
interface (FakeRealm
) is used to inject the configuration into the classes.
For details please have a look at the source code.