4.3. Credential Handlers
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 onIdentityManager
, passing in theCredentials
instance to validate. -
1.1 - After looking up the correct
IdentityStore
(i.e. the one that has been configured to validate credentials) theIdentityManager
invokes the store'svalidateCredentials()
method, passing in theIdentityContext
and the credentials to validate. -
1.1.1 - In
JPAIdentityStore
's implementation of thevalidateCredentials()
method, theIdentityContext
is used to look up theCredentialHandler
implementation that has been configured to process validation requests for usernames and passwords, which is then stored in a local variable calledhandler
. -
1.1.2 - The
validate()
method is invoked on theCredentialHandler
, 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 {
4.3.1. The CredentialStore
interface
For
IdentityStore
implementations that support multiple credential types (such as JPAIdentityStore
and FileBasedIdentityStore
), the implementation may choose to also implement the CredentialStore
interface to simplify the interaction between the CredentialHandler
and the IdentityStore
. The CredentialStore
interface declares methods for storing and retrieving credential values within an identity store, as follows:
public interface CredentialStore { /** * Stores the specified credential state. * * @param context The contextual invocation context. * @param account The account which credentials should be removed. * @param storage The credential storage instance to be stored. */ void storeCredential(IdentityContext context, Account account, CredentialStorage storage); /** * Returns the currently active credential state of the specified {@link T}, for the specified {@link org.picketlink.idm.model.Account}. * * @param context The contextual invocation context. * @param account The account which credentials should be removed. * @param storageClass The credential storage type specifying which credential types should be removed. * * @return */ <T extends CredentialStorage> T retrieveCurrentCredential(IdentityContext context, Account account, Class<T> storageClass); /** * Returns a list of all credential state of the specified {@link T}, for the specified {@link org.picketlink.idm.model.Account}. * * @param context The contextual invocation context. * @param account The account which credentials should be removed. * @param storageClass The credential storage type specifying which credential types should be removed. * * @return */ <T extends CredentialStorage> List<T> retrieveCredentials(IdentityContext context, Account account, Class<T> storageClass); /** * <p>Removes all credentials stored by a certain {@link org.picketlink.idm.credential.storage.CredentialStorage} associated * with the given {@link org.picketlink.idm.model.Account}.</p> * * @param context The contextual invocation context. * @param account The account which credentials should be removed. * @param storageClass The credential storage type specifying which credential types should be removed. */ void removeCredential(IdentityContext context, Account account, Class<? extends CredentialStorage> storageClass); }