Authentication module
Authentication module
See also notes from a Yale meeting on this subject.
What is
The "authentication" module contains interfaces and classes which solve the authentication concern of the system. Main interfaces are AuthenticationManager (main service component to authenticate "things". Delegates to pluggable AuthenticationHandler s), Principal (an authenticated "thing") and CredentialsToPrincipalResolver to handle resolving credentials to an actual principal. Again a UML class diagram of this module would be useful.
An idea
We collapse AuthenticationHandler and CredentialsToPrincipalResolver into a single AuthenticationHandler and then collapse the AuthenticationManager into that AuthenticationHandler to yield a single core interface of the Authentication package.
Proposed interfaces
/** * Implementations will examine particular Credentials for their representing * credentials that provide evidence of authenticity (e.g., a username and password) on the basis of which * the AuthenticationHandler creates an AuthenticationResult. AuthenticationResults either represent * AuthenticationSuccesses or AuthenticationFailures. */ public interface AuthenticationHandler { /** * Examine some Credentials for evidence that it authenticates a Principal. * Return an AuthenticationResult communicating the authenticated Principal and * possibly additional information about the nature of its authentication, or * a characterization of the failure to authenticate -- the Credentials were not understood or * supported, the user wasn't found, the password was wrong, the account has been locked, whatever -- * particular AuthenticationHandlers will return particular AuthenticationResult implementations appropriate * to their implementations. */ AuthenticationResult authenticate(Credentials credentials); }
The output of AuthenticationHandler is an AuthenticationResult, which may represent a success (an authenticated Principal and perhaps other information about the authentication), or may represent a failure (no authenticated principal and perhaps other information about the nature of the failure).
/** * The AuthenticationResult interface represents the commonality of AuthenticationSuccesses and * AuthenticationFailures as being different types of AuthenticationResult -- the two possible return values * from an AuthenticationHandler. * All AuthenticationResults must be either AuthenticationSuccesses or AuthenticationFailures. */ public interface AuthenticationResult { // this interface declares no methods. // It is a wrapper interface that allows AuthenticationHandlers to have a single return type which // will represent the result of authentication, whether that be success or failure. }
public interface AuthenticationSuccess { public Principal getPrincipal(); }
public interface AuthenticationFailure { // this is a marker interface. Actual implementations of this interface may communicate // the type of failure }
Thoughts about managers
And right about now you should be saying, "Hey! Wait! Where's my AuthenticationManager? I needed that."
Under this view, AuthenticationManager becomes just a particular implementation of AuthenticationHandler – an AuthenticationHandler that knows how to delegate to other AuthenticationHandlers.
Why are these managers just implementations of AuthenticationHandler and not something more? Because fundamentally they are implementing the AuthenticationHandler interface. They take credentials, and they return AuthenticationResults. This makes them AuthenticationHandlers.
public class ManagingAuthenticationHandler implements AuthenticationHandler { // not listed here but assume setters and getters. private List childHandlers; /** * Implements this interface method by delegation. * * The return value is the following: * 1) If any delegate handler returned an AuthenticationSuccess, returns the AuthenticationSuccess * returned by the first delegate to succeed. * 2) If all delegate handlers returned CredentialsNotSupported, returns CredentialsNotSupported. * 3) If all delegates returned AuthenticationFailures and at least one delegate returned an * AuthenticationFailure that is not a CredentialsNotSupported, returns an AggregatedFailures representing * all accumulated AuthenticationFailures that were not CredentialsNotSupported. */ public AuthenticationResult authenticate(Credentials credentials) { AggregatedFailures aggregatedFailures = new AggregatedeFailures(); for (Iterator iter = this.childHandlers.iterator() { AuthenticationHandler handler = (AuthenticationHandler) iter.next(); AuthenticationResult result = handler.authenticate(credentials); if (result instanceof AuthenticationSuccess) { return result; } else if (result instanceof CredentialsNotSupported) { // do nothing } else { // we have an AuthenticationFailure that is not a CredentialsNotSupported aggregatedFailures.addFailure(result); } } if (this.aggregatedFailures.getFailures().isEmpty()) { return new UnsupportedCredentialsFailure(); } else { return aggregatedFailures; } } }
public class AggregatedFailures implements AuthenticationFailure { public void addFailure(AuthenticationFailure failure); /** * Get a List of AuthenticationFailures. */ public List getFailures(); }
Exceptions versus AuthenticationFailure as a type of AuthenticationResult
Instead of using AuthenticationFailures as a type of AuthenticationResult, we could instead go with encapsulating failure information in an Exception thrown by AuthenticationHandlers. Either way, the point is that AuthenticationHandlers may have information to return in the case of a failure that will have effects all the way back up in the view layer where a user is told "Your account has been locked" rather than given another opportunity to type her username and password.
More thoughts
See also notes from a Yale CAS meeting on this subject.