4.5. Implementing a Custom CredentialHandler
In this section we'll dissect the
PasswordCredentialHandler
to learn how to create a custom credential handler. The AbstractCredentialHandler
abstract class is provided to simplify the process of creating a new credential handler, and is also used by PasswordCredentialHandler
as a base class:

Let's start by looking at the class declaration for
PasswordCredentialHandler
:
@SupportsCredentials( credentialClass = {UsernamePasswordCredentials.class, Password.class}, credentialStorage = EncodedPasswordStorage.class) public class PasswordCredentialHandler<S extends CredentialStore<?>, V extends UsernamePasswordCredentials, U extends Password> extends AbstractCredentialHandler<S, V, U> {
The
@SupportsCredentials
annotation is used to declare exactly which credential classes are supported by the credential handler (as indicated by the credentialClass
annotation member). The supported credential classes include both credentials used to authenticate via the validate()
method (i.e. a class that implements the org.picketlink.idm.credential.Credentials
interface, in this example UsernamePasswordCredentials
) and the actual encapsulated credential value (e.g. org.picketlink.idm.credential.Password
). These supported credential classes are also reflected in the generic type declaration of the class itself - the V
and U
types in the code above. The credentialStorage
annotation member declares the storage class used to persist the necessary state for the credential. The storage class must implement the org.picketlink.idm.credential.storage.CredentialStorage
interface.
The
setup()
method is executed only once and is used to perform any required initialization for the credential handler. In the case of PasswordCredentialHandler
, the setup()
method reads the configuration properties (made available from store.getConfig().getCredentialHandlerProperties()
) and uses those property values to initialize the state required for encoding password values.
@Override public void setup(S store) { super.setup(store); Map<String, Object> options = store.getConfig().getCredentialHandlerProperties(); if (options != null) { Object providedEncoder = options.get(PASSWORD_ENCODER); if (providedEncoder != null) { if (PasswordEncoder.class.isInstance(providedEncoder)) { this.passwordEncoder = (PasswordEncoder) providedEncoder; } else { throw new SecurityConfigurationException("The password encoder [" + providedEncoder + "] must be an instance of " + PasswordEncoder.class.getName()); } } Object renewRandomNumberGeneratorInterval = options.get( RENEW_RANDOM_NUMBER_GENERATOR_INTERVAL); if (renewRandomNumberGeneratorInterval != null) { this.renewRandomNumberGeneratorInterval = Integer.valueOf( renewRandomNumberGeneratorInterval.toString()); } Object secureRandomProvider = options.get(SECURE_RANDOM_PROVIDER); if (secureRandomProvider != null) { this.secureRandomProvider = (SecureRandomProvider) secureRandomProvider; } else { Object saltAlgorithm = options.get(ALGORITHM_RANDOM_NUMBER); if (saltAlgorithm == null) { saltAlgorithm = DEFAULT_SALT_ALGORITHM; } Object keyLengthRandomNumber = options.get(KEY_LENGTH_RANDOM_NUMBER); if (keyLengthRandomNumber == null) { keyLengthRandomNumber = Integer.valueOf(0); } this.secureRandomProvider = new DefaultSecureRandomProvider( saltAlgorithm.toString(), Integer.valueOf(keyLengthRandomNumber.toString())); } } this.secureRandom = createSecureRandom(); }
The credential validation logic is defined by the
validateCredential()
method. This method (which the parent AbstractCredentialHandler
class declares as an abstract method) checks the validity of the credential value passed in and either returns true
if the credential is valid or false
if it is not. The validateCredential()
method is a convenience method which delegates much of the boilerplate code required for credential validation to AbstractCredentialHandler
, allowing the subclass to simply define the bare minimum code required to validate the credential. If you were to implement a CredentialHandler
without using AbstractCredentialHandler
as a base class, you would instead need to implement the validate()
method which in general requires a fair bit more code.
@Override protected boolean validateCredential(final CredentialStorage storage, final V credentials) { EncodedPasswordStorage hash = (EncodedPasswordStorage) storage; if (hash != null) { String rawPassword = new String(credentials.getPassword().getValue()); return this.passwordEncoder.verify(saltPassword(rawPassword, hash.getSalt()), hash.getEncodedHash()); } return false; }
The
update()
method (in contrast to validateCredential
) is defined by the CredentialHandler
interface itself, and is used to persist a credential value to the backend identity store. In PasswordCredentialHandler
this method creates a new instance of EncodedPasswordStorage
, a CredentialStorage
implementation that represents a password's hash and salt values. The salt value in this implementation is randomly generated using the configured property values, and then used to encode the password hash. This value is then stored by calling the store.storeCredential()
method.
@Override public void update(IdentityContext context, Account account, U password, S store, Date effectiveDate, Date expiryDate) { EncodedPasswordStorage hash = new EncodedPasswordStorage(); if (password.getValue() == null || isNullOrEmpty(password.getValue().toString())) { throw MESSAGES.credentialInvalidPassword(); } String rawPassword = new String(password.getValue()); String passwordSalt = generateSalt(); hash.setSalt(passwordSalt); hash.setEncodedHash(this.passwordEncoder.encode(saltPassword(rawPassword, passwordSalt))); if (effectiveDate != null) { hash.setEffectiveDate(effectiveDate); } hash.setExpiryDate(expiryDate); store.storeCredential(context, account, hash); }