2005.03.07 Yale Meeting

Meeting meta information

a Monday. Met 9:30 am to 10:30 am.

Present: Andy, Andrew, Susan.

  • Agenda item: implementing ACAS using CAS3: tabled until Wednesday as Drew not present and Drew among us best has is brain around ACAS.
  • Agenda item: Andrew reported on speculative design and implementation of non-interactive user login.

Next meeting

Next meeting will be Wed 03.09. Andrew will report on actual code implementing the topics discussed today. We will also discuss ACAS.

Explanation of whiteboard

Attached is screenshot of whiteboard.

LoginController and LoginFormController

(See black square at top left of whiteboard.)

The object called LoginController in the CAS3 HEAD is an implementation of LoginController that is about being a form controller. It works well for things that look like username password forms. Let's let it be good at what it's good at and not shoehorn onto it support for credentials other than those expressed by forms (i.e., as request parameters that can be easily bound to an expected Object).

So, before reading the rest of this page, mv LoginController LoginFormController in your conception of the CAS3 head. Now, this thing that we're going to talk about here, it is the NonInteractiveLoginController. I'll be calling it LoginController for short here.

The End Game

(See far left of whiteboard.)

Before we embark, let's review where we're going. Where we're going is that as a result of interacting with the LoginController, we're going to issue a TicketGrantingTicket that maps back to some information. Specifically, the information we're going to have is one or more AuthenticationEntry instances along with a Principal.

AuthenticationEntries are about what and how authentication happened. They have a type, a value, and a method. In the case of authenticating using username and password where I typed it this request, for me this would be a type of "NetID", a value of "awp9", and a method of "username/password", and "happened this request". If I then come back through via Single Sign On, this would be "via SSO cookie" rather than "happened this request".

We might also issue X.509 certificates to authenticate NetIDs. Thus I might have an X.509 cert issued by Yale which authenticates the "NetID" "awp9". If I present this, I'd have an authentication entry of "NetID", "awp9", "X.509 certificate". We might be remembering this via SSO or we might have actually examined the certificate on this request.

One might also implement that dopey "send a PIN to your email address" approach of authenticating a person's email address. After having completed this, we'd also have an authentication entry of "microcline@gmail.com", "email", "PIN to email". We might be remembering this via SSO or we might have actually had the user type in her PIN this request.

In addition to how the user was authenticated, we also have a Principal. Whereas the AuthenticationEntries were individual assertions about the way in what and how authentication happened, a Principal is about who it is that we've authenticated. The Principal contains a primary identifier (round here at Yale it might be a NetID) as well as zero or more name,value pairs representing user attributes, things like alumni status, email aliases, etc.

We bundle the Principal and the AuthenticationEntries into an Authentication. The end game is that given a TicketGrantingTicket and a desired Service, we can retrieve stored Authentication information and use it to produce a ServiceTicket which will reveal a view on that Authentication to the service that eventually validates it.

Exception Handlers

(See bottom left of whiteboard.)

Now we need to remember something about Spring Web MVC. Our Controller implementation can throw Exceptions. Those Exceptions can be mapped to Handlers which can in turn specify Views (ModelAndViews, really) which the Spring Web MVC will render. So we can throw particular exceptions up out of our Controller and the deployer will be able to map and appropriate View.

With that in mind,

NonInteractiveLoginController

(See middle of whiteboard.)

Here's what our NonInteractiveLoginController is going to do:

Get a LoginConfig

It takes a Request. It's going to feed the Request into an (injected) RequestToLoginConfig instance. That RequestToLoginConfig is going to compute a LoginConfig and return it. Or it's going to throw an exception (potential exception throws noted in red on whiteboard).

By default CAS2 allows any value for the "service" parameter to login. A common extension is to restrict which services can use CAS for authentication. The RequestToLoginConfig is an extension point for accomplishing this: instead of computing a LoginConfig, a particular implementation of RequestToLoginConfig can throw a particular exception (e.g., InvalidServiceException), which in turn the deployer maps in the configured Exception Handlers to an appropriate view ("The service 'http://bobsspamshack/' is not authorized to use University Authentication.").

RequestToLoginConfig is an interface. The default implementation is to do the CAS2 thing of looking at the request parameters. Additionally customized implementations might consider configuration XML, injected dependencies, Databases, Web Services, or whatever else to consider such things as service regitry information, additional request parameters, databases, user preferences keyed via persistent cookie, etc.

Whether the default way or in some other way, our configured RequestToLoginConfig gets us a LoginConfig instance from this Request.

What is a LoginConfig? (See upper right of whiteboard.) At the meeting there was a feeling this isn't the best name for it, but I haven't heard a better one yet as I write these notes, so I'm going with this one for now. A LoginConfig is the set of configuration information that is not the authentication credentials. It is the runtime configuration for the NonInteractiveLoginController. It is the thing that knows for what service, if any, we're trying to issue a ServiceTicket. It's the thing that knows whether we're in gateway (do not interrupt the user) mode. It's the thing that knows whether a prospective Authentication is going to be sufficient (we can go back to the service with a ticket now) or whether we need to do some more (likely interactive) authentication. It's the thing that has some advice about what more is needed in the case where more is needed.

Again, by default if we're implementing the CAS2 version of things, our RequestToLoginConfig merely examines the request and computes a simple LoginConfig reflecting whether renew=true, gateway=true, what service=. For CAS3 we invent a request protocol that expresses more completely what it is the service is looking for (or accomplish it via a service registry.) I don't propose any particular new way of computing our LoginConfig here. I just assert that somehow, given an HttpServletRequest, our RequestToLoginConfig has come up with one.

Great. We've got a LoginConfig. Store it in a local variable.

Get some Credentials

Now our NonInteractiveLoginController takes the Request, feeds it to a RequestToCredentials, and out comes some Credentials. I'd rather see Credentials be a Set of Objects that represent Credentials to somebody than an array of Credentials marker interface instances, but whatever. It needs to be an array or set because we might (will) have multiple credentials.

We will sometimes have multiple credentials because a Ticket Granting Ticket is a Credential.

This step might fail with there being no credentials at all in evidence. With no credentials, we've nothing to ask CAS about. We need to jump ahead to the "Login Config didn't think the Authentication was good enough" step.

Suppose there were Credentials, though.

Feed those Credentials into the Central Authentication Service service bean instance

Now we take our Set of objects representing Credentials, or our Credentials[], and we feed it alongside the Service from our LoginConfig into CAS. Out of CAS comes a Ticket Granting Ticket (so we can write it back to the browser), a Service Ticket (so we can put it on the redirect if it turns out to be worthy), and the Authentication that would inform the ticket validation view on the service ticket were it to be validated.

See if the results are sufficient

We ask our LoginConfig, "is this good enough?" We give it the Authentication and it replies with a boolean.

When Authentication wasn't sufficient

If no, not good enough, then we ask it if we're in gateway mode.

If no, we're not in gateway mode, we forward to a configured view which likely is a view that is the first step in an interactive login process (a username password form, e.g.) We need to insert some Fairy Dust here to allow the LoginConfig to advise about what more is required. Perhaps it exposes either a List of Strings keying to AuthenticationHandlers we must be sure to actuate. Perhaps it exposes some AuthenticationEntries demonstrative of what it expects to require. Perhaps it itself knows how to compose and return the ModelAndView, effectively selecting what form we send the user to.

If yes, we're in gateway mode, then we redirect to the Service without any ticket.

When Authentication was sufficient

If yes, the LoginConfig thinks the Authentication was good enough, we need to know if LoginConfig wants us to be in "warn" mode. Note that we're a NonInteractiveLoginController. We've had no interaction with the user yet. Yet a user might have opted into, or failed to opt out of, being asked to give her informed consent for each authentication. If no, we're not in warn mode, we can redirect to the serivce with the service ticket. If yes, we're in warn mode, we need to pass the service ticket and service to a WarnView that will obtain the user's informed consent before allowing her to follow a link or otherwise be redirected to a the service.

Asking the LoginConfig about sufficiency of Authentication, gateway mode, and warn mode, provides an important extension point. Advanced implementations of the RequestToLoginConfig "factory" of LoginConfigs and of LoginConfigs themselves might implement such rules as "Never allow Single Sign On from the IP addresses of known kiosks". In general the LoginConfig could consider where the request seems to be coming from (looks like a kiosk), user preferences based on persistent browser cookie (SSO opt-in cookie was present or opt-out-of-SSO cookie was not present), user preferences based on authenticated identity (we know awp9 has opted out of privacy), service preferences (we know the service for which we're trying to issue a ST will accept nothing less than a client cert along with username password along with NTLM authentication).