JBoss.orgCommunity Documentation

Chapter 4. Identity Management - Credential Validation and Management

Table of Contents

4.1. Authentication
4.2. Managing Credentials
4.3. Credential Handlers
4.3.1. The CredentialStore interface
4.3.2. The CredentialStorage interface
4.4. Built-in Credential Handlers
4.4.1. Username/Password-based Credential Handler
4.4.2. DIGEST-based Credential Handler
4.4.3. X509-based Credential Handler
4.4.4. Time-based One Time Password Credential Handler
4.5. Implementing a Custom CredentialHandler

PicketLink IDM provides an authentication subsystem that allows user credentials to be validated thereby confirming that an authenticating user is who they claim to be. The IdentityManager interface provides a single method for performing credential validation, as follows:

void validateCredentials(Credentials credentials);

The validateCredentials() method accepts a single Credentials parameter, which should contain all of the state required to determine who is attempting to authenticate, and the credential (such as a password, certificate, etc) that they are authenticating with. Let's take a look at the Credentials interface:

public interface Credentials {

    public enum Status {
        UNVALIDATED, IN_PROGRESS, INVALID, VALID, EXPIRED
    };
   Account getValidatedAccount();
   
   Status getStatus();
   
   void invalidate();
}
  • The Status enum defines the following values, which reflect the various credential states:

    • UNVALIDATED - The credential is yet to be validated.

    • IN_PROGRESS - The credential is in the process of being validated.

    • INVALID - The credential has been validated unsuccessfully

    • VALID - The credential has been validated successfully

    • EXPIRED - The credential has expired

  • getValidatedAccount() - If the credential was successfully validated, this method returns the Account object representing the validated user.

  • getStatus() - Returns the current status of the credential, i.e. one of the above enum values.

  • invalidate() - Invalidate the credential. Implementations of Credential should use this method to clean up internal credential state.

Let's take a look at a concrete example - UsernamePasswordCredentials is a Credentials implementation that supports traditional username/password-based authentication:

public class UsernamePasswordCredentials extends AbstractBaseCredentials {


    private String username;
    private Password password;
    public UsernamePasswordCredentials() { }
    public UsernamePasswordCredentials(String userName, Password password) {
        this.username = userName;
        this.password = password;
    }
    public String getUsername() {
        return username;
    }
    public UsernamePasswordCredentials setUsername(String username) {
        this.username = username;
        return this;
    }
    public Password getPassword() {
        return password;
    }
    public UsernamePasswordCredentials setPassword(Password password) {
        this.password = password;
        return this;
    }
    @Override
    public void invalidate() {
        setStatus(Status.INVALID);
        password.clear();
    }
}

The first thing we may notice about the above code is that the UsernamePasswordCredentials class extends AbstractBaseCredentials. This abstract base class implements the basic functionality required by the Credentials interface. Next, we can see that two fields are defined; username and password. These fields are used to hold the username and password state, and can be set either via the constructor, or by their associated setter methods. Finally, we can also see that the invalidate() method sets the status to INVALID, and also clears the password value.

Let's take a look at an example of the above classes in action. The following code demonstrates how we would authenticate a user with a username of "john" and a password of "abcde":

Credentials creds = new UsernamePasswordCredentials("john",

    new Password("abcde"));
identityManager.validate(creds);
if (Status.VALID.equals(creds.getStatus())) {
  // authentication was successful
}

We can also test if the credentials that were provided have expired (if an expiry date was set). In this case we might redirect the user to a form where they can enter a new password.

Credentials creds = new UsernamePasswordCredentials("john",

    new Password("abcde"));
identityManager.validate(creds);
if (Status.EXPIRED.equals(creds.getStatus())) {
  // password has expired, redirect the user to a password change screen
}

Updating user credentials is even easier than validating them. The IdentityManager interface provides the following two methods for updating credentials:

void updateCredential(Account account, Object credential);

void updateCredential(Account account, Object credential, Date effectiveDate, Date expiryDate);

Both of these methods essentially do the same thing; they update a credential value for a specified Account. The second overloaded method however also accepts effectiveDate and expiryDate parameters, which allow some temporal control over when the credential will be valid. Use cases for this feature include implementing a strict password expiry policy (by providing an expiry date), or creating a new account that might not become active until a date in the future (by providing an effective date). Invoking the first overloaded method will store the credential with an effective date of the current date and time, and no expiry date.

Note

One important point to note is that the credential parameter is of type java.lang.Object. Since credentials can come in all shapes and sizes (and may even be defined by third party libraries), there is no common base interface for credential implementations to extend. To support this type of flexibility in an extensible way, PicketLink provides an SPI that allows custom credential handlers to be configured that override or extend the default credential handling logic. Please see the next section for more information on how this SPI may be used.

Let's take a look at a couple of examples. Here's some code demonstrating how a password can be assigned to user "jsmith":

User user = BasicModel.getUser(identityManager, "jsmith");

identityManager.updateCredential(user, new Password("abcd1234"));

This example creates a digest and assigns it to user "jdoe":

User user = BasicModel.getUser(identityManager, "jdoe");

Digest digest = new Digest();
digest.setRealm("default");
digest.setUsername(user.getLoginName());
digest.setPassword("abcd1234");        
identityManager.updateCredential(user, digest);

For IdentityStore implementations that support multiple credential types, PicketLink provides an optional SPI to allow the default credential handling logic to be easily customized and extended. To get a better picture of the overall workings of the Credential Handler SPI, let's take a look at the sequence of events during the credential validation process when validating a username and password against JPAIdentityStore:

  • 1 - The user (or some other code) first invokes the validateCredentials() method on IdentityManager, passing in the Credentials instance to validate.

  • 1.1 - After looking up the correct IdentityStore (i.e. the one that has been configured to validate credentials) the IdentityManager invokes the store's validateCredentials() method, passing in the IdentityContext and the credentials to validate.

  • 1.1.1 - In JPAIdentityStore's implementation of the validateCredentials() method, the IdentityContext is used to look up the CredentialHandler implementation that has been configured to process validation requests for usernames and passwords, which is then stored in a local variable called handler.

  • 1.1.2 - The validate() method is invoked on the CredentialHandler, passing in the security context, the credentials value and a reference back to the identity store. The reference to the identity store is important as the credential handler may require it to invoke certain methods upon the store to validate the credentials.

The CredentialHandler interface declares three methods, as follows:

public interface CredentialHandler {

    void setup(IdentityStore<?> identityStore);
    void validate(IdentityContext context, Credentials credentials,
                  IdentityStore<?> identityStore);
    void update(IdentityContext context, Account account, Object credential,
                IdentityStore<?> identityStore, Date effectiveDate, Date expiryDate);
}

The setup() method is called once, when the CredentialHandler instance is first created. Credential handler instantiation is controlled by the CredentialHandlerFactory, which creates a single instance of each CredentialHandler implementation to service all credential requests for that handler. Each CredentialHandler implementation must declare the types of credentials that it is capable of supporting, which is done by annotating the implementation class with the @SupportsCredentials annotation like so:

@SupportsCredentials(

  credentialClass = { UsernamePasswordCredentials.class, Password.class },
  credentialStorage = EncodedPasswordStorage.class
)
public class PasswordCredentialHandler implements CredentialHandler {

Since the validate() and update() methods receive different parameter types (validate() takes a Credentials parameter value while update() takes an Object that represents a single credential value), the @SupportsCredentials annotation must contain a complete list of all types supported by that handler.

Similarly, if the IdentityStore implementation makes use of the credential handler SPI then it also must declare which credential handlers support that identity store. This is done using the @CredentialHandlers annotation; for example, the following code shows how JPAIdentityStore is configured to be capable of handling credential requests for usernames and passwords, X509 certificates and digest-based authentication:

@CredentialHandlers({ PasswordCredentialHandler.class,

          X509CertificateCredentialHandler.class, DigestCredentialHandler.class })
public class JPAIdentityStore implements IdentityStore<JPAIdentityStoreConfiguration>, 
                                         CredentialStore {

The CredentialStorage interface is essentially used to represent the state required to validate an account's credentials, and is persisted within the identity store. The base interface is quite simple and only declares two methods - getEffectiveDate() and getExpiryDate():

public interface CredentialStorage {

   @Stored Date getEffectiveDate();
   @Stored Date getExpiryDate();
}

The most significant thing to note above is the usage of the @Stored annotation. This annotation is used to mark the properties of the CredentialStorage implementation that should be persisted. The only requirement for any property values that are marked as @Stored is that they are serializable (i.e. they implement the java.io.Serializable interface). The @Stored annotation may be placed on either the getter method or the field variable itself. An implementation of CredentialStorage will typically declare a number of properties (in addition to the effectiveDate and expiryDate properties) annotated with @Stored. Here's an example of one of a CredentialStorage implementation that is built into PicketLink - EncodedPasswordStorage is used to store a password hash and salt value:

public class EncodedPasswordStorage implements CredentialStorage {


    private Date effectiveDate;
    private Date expiryDate;
    private String encodedHash;
    private String salt;
    @Override @Stored
    public Date getEffectiveDate() {
        return effectiveDate;
    }
    public void setEffectiveDate(Date effectiveDate) {
        this.effectiveDate = effectiveDate;
    }
    @Override @Stored
    public Date getExpiryDate() {
        return expiryDate;
    }
    public void setExpiryDate(Date expiryDate) {
        this.expiryDate = expiryDate;
    }
    @Stored
    public String getEncodedHash() {
        return encodedHash;
    }
    public void setEncodedHash(String encodedHash) {
        this.encodedHash = encodedHash;
    }
    @Stored
    public String getSalt() {
        return this.salt;
    }
    public void setSalt(String salt) {
        this.salt = salt;
    }
}

PicketLink provides built-in support for the following credential types:

Warning

Not all built-in IdentityStore implementations support all credential types. For example, since the LDAPIdentityStore is backed by an LDAP directory server, only password credentials are supported. The following table lists the built-in IdentityStore implementations that support each credential type.


The next sections will describe each of these built-in types individually. Configuration parameters are set at initialization time - see Section 7.1.8.1, “Passing parameters to Credential Handlers” for details.

This credential handlers supports a username/password based authentication.

Credentials can be updated as follows:

User user = BasicModel.getUser(identityManager, "jsmith");

identityManager.updateCredential(user, new Password("abcd1234"));

In order to validate a credential you need to the following code:

UsernamePasswordCredentials credential = new UsernamePasswordCredentials();


Password password = new Password("abcd1234");
credential.setUsername("jsmith");
credential.setPassword(password);
identityManager.validateCredentials(credential);
if (Status.VALID.equals(credential.getStatus()) {
  // successful validation
} else {
  // invalid credential
}

The following table describes all configuration parameters supported by this credential handler:

Table 4.2. Configuration Parameters

ParameterDescription
PasswordCredentialHandler. PASSWORD_ENCODERIt must be a org.picketlink.idm.credential.encoder.PasswordEncoder sub-type. It defines how passwords are encoded. Defaults to SHA-512.
PasswordCredentialHandler. SECURE_RANDOM_PROVIDERIt must be a org.picketlink.common.random.SecureRandomProvider sub-type. It defines how SecureRandom are created in order to be used to generate random numbers to salt passwords. Defaults to SHA1PRNG with a default seed.
PasswordCredentialHandler. RENEW_RANDOM_NUMBER_GENERATOR_INTERVALTo increase the security of generated salted passwords, SecureRandom instances can be renewed from time to time. This option defines the time in milliseconds. Defaults to disabled, what means that a single instance is used during the life-time of the application.
PasswordCredentialHandler. ALGORITHM_RANDOM_NUMBERDefines the algorithm to be used by the default SecureRandomProvider. Defaults to SHA1PRNG.
PasswordCredentialHandler. KEY_LENGTH_RANDOM_NUMBERDefines the key length of seeds when using the default SecureRandomProvider. Defaults to 0, which means it is disabled.
PasswordCredentialHandler. LOGIN_NAME_PROPERTYThis option defines the name of the property used to lookup the Account object using the provided login name. It has a default value of loginName and can be overridden if the credential handler is to be used to authenticate an Account type that uses a different property name.
PasswordCredentialHandler. SUPPORTED_ACCOUNT_TYPESThis option defines any additional Account types that are supported by the credential handler. If no value is specified and/or no identity instances of the specified types are found then the credential handler's fall back behaviour is to attempt to lookup either an Agent or User (from the org.picketlink.idm.model.basic package) identity. The property value is expected to be an array of Class<? extends Account> objects.

This credential handlers supports a username/password based authentication.

Credentials can be updated as follows:

User user = BasicModel.getUser(identityManager, "jsmith");


TOTPCredential credential = new TOTPCredential("abcd1234", "my_totp_secret");
identityManager.updateCredential(user, credential);

Users can have multiple TOTP tokens, one for each device. You can provide configure tokens for a specific user device as follows:

User user = BasicModel.getUser(identityManager, "jsmith");


TOTPCredential credential = new TOTPCredential("abcd1234", "my_totp_secret");
credential.setDevice("My Cool Android Phone");
identityManager.updateCredential(user, credential);

In order to validate a credential you need to the following code:

User user = BasicModel.getUser(identityManager, "jsmith");


TOTPCredentials credential = new TOTPCredentials();
credential.setUsername(user.getLoginName());
credential.setPassword(new Password("abcd1234"));
TimeBasedOTP totp = new TimeBasedOTP();
// let's manually generate a token based on the user secret
String token = totp.generate("my_totp_secret");
credential.setToken(token);
// if you want to validate the token for a specific device
// credential.setDevice("My Cool Android Phone");
identityManager.validateCredentials(credential);
if (Status.VALID.equals(credential.getStatus()) {
  // successful validation
} else {
  // invalid credential
}

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<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);
}