Rationale and Overview
- To facilitate single sign-on across multiple web applications, as well as to core services that aren't necessarily web-based but have a web front end.
- To allow untrusted services offered by organizations other than ITS (as well as, of course, trusted services) to authenticate users without having access to their passwords.
- To simplify procedures that applications need to follow in order to perform authentication.
- To localize actual ("primary") authentication to a single web application, which makes it easier for users to safeguard their passwords and which lets ITS change authentication logic if necessary without having to change numerous applications.
Design and Implementation
The Central Authentication Server (CAS) is designed as a standalone web application. It is currently implemented as several Java servlets and runs through the HTTPS server on secure.its.yale.edu. It is accessed through three URLs described below: the login URL, the validation URL, and the optional logout URL.
To use the central authentication service, an application redirects its users, or simply creates a hyperlink, to the login URL. For example, Yale's CAS login URL for general-purpose Yale users is https://secure.its.yale.edu/cas/servlet/login. Users may also access this URL manually if they wish to pre-authenticate their sessions.
The login URL handles actual, "primary" authentication. That is, it prompts the user for a NetID and a password and validates it against a backing authentication provider. Particular CAS deployers will plug in generic or custom PasswordHandlers to validate the username and password against whatever backing authentication mechanism is appropriate.
To allow for the possibility of automatic re-authentication later, the CAS also attempts to send an in-memory cookie (one that expires automatically when the browser closes) back to the browser. This cookie, which we call a "ticket-granting cookie," identifies the user as one who has already logged in successfully.
It is worth noting that this cookie is an optional part of the CAS authentication mechanism. With it, the user achieves the appearance of "single sign-on" to multiple web applications; that is, he enters his NetID and password only once but gains access to any services that use the CAS. Without the cookie, the user will need to enter his NetID and password every time an application redirects him to the CAS. (Users can direct the CAS to destroy the cookie by going to a logout URL; for example, Yale's CAS server logout URL is: https://secure.its.yale.edu/cas/servlet/logout.)
In addition to handling primary authentication, the CAS also notes the service that the user was redirected or linked from. It can do this because applications that redirect or link a user to the login URL are required also to pass the CAS a service identifier (labelled serviceID in the diagram above). If authentication is successful, the CAS creates a long, random number, which we call a ticket. It then associates this ticket with the user who successfully authenticated and the service to which the user was trying to authenticate. That is, if user peon is passed from service S, the CAS creates ticket T which allows peon to access service S. This ticket is intended as a one-time-use-only credential; it is useful only for peon, only for service S, and only once. It expires as soon as it is used.
Once primary authentication is complete, the CAS redirects the user's browser back to the application from which it came. It knows what URL to redirect the user to because the service ID discussed above also functions as a "callback URL." That is, the identifier that an application uses must represent a URL that is part of, or at least associated with, that application. The CAS redirects the user's browser back to this URL, adding the ticket discussed above as a request parameter.
To make the discussion concrete, consider an example. Suppose that Technology and Planning wants to authenticate users before giving them access to http://www.yale.edu/tp. From the front door for the http://www.yale.edu/tp website, we would redirect users to
Suppose that authenticate.jsp is a JavaServer Pages (JSP) application that is part of the Technology and Planning web site. This JSP page should be designed to expect a ticket string to be passed to it as a request parameter named ticket. That is, the JSP page expects to be called as follows:
Our JSP page then just needs to validate the ticket once it receives it. It does so by passing it as the ticket parameter to the validation URL, which is currently https://secure.its.yale.edu/cas/servlet/validate. Our JSP page needs to marshal a request to this URL and read the data at that URL, probably by using JSSE. (A web application written in Perl, by contrast, might use the Net::SSLeay package.) When constructing this request, the JSP page will also need to pass in the service ID it used previously, when redirecting the user to the login URL; to do this, it uses the request parameter named service.
When the CAS receives a ticket through the validation URL, it checks its internal database to determine whether it saved, in the past, a ticket corresponding to the one it just received. If it did, and if the service associated with that ticket matches the service that was passed in by the application that's requesting validation, it returns the NetID associated with that ticket to the requesting application. Otherwise, it refuses to validate the request.
The protocol that the validation URL uses to return data to applications that request validation is straightforward. The CAS responds with two lines (in a text/plain HTTP response); the first line is either yes or no, corresponding to whether the ticket that the application presented is valid or not. If the ticket is valid, the second line contains the NetID of the ticket holder - that is, the identification of the user who has authenticated successfully. If the ticket is invalid, the second line is empty. Thus, an example response is:
If the ticket was valid, the CAS immediately removes it so that it cannot be used again.
When the cycle is completed, a web application has been able to verify a user's identity without ever having access to that user's password. Furthermore, in the case where the user's browser accepts cookies, it is left with a cookie that can re-identify the user to CAS so that the user does not have to enter his NetID and password in the future. (Currently, the in-memory "ticket-granting cookies" remain active for eight hours.)
Handling non-web services
What if the web application desiring central authentication is merely a front end to a back-end application that has its own notion of authentication? The CAS can still be used as long as the back-end application's authentication mechanism can be modified. (A simple rule of thumb appropriate to applications at Yale is that any application that can be configured or hacked to validate Kerberos IV NetIDs and passwords can also be configured or hacked to use the CAS.)
From the CAS's perspective, there is no significant distinction between a web service that provides its own content and one that depends on a back-end service. Logically speaking, the CAS simply groups the front end and the back end together. (This grouping corresponds to the dotted circle in the diagram above.) However, given that the front-end and the back-end are (by definition) not the same program, the question of how they are to communicate arises.
We suggest the following design for web applications that need to authenticate users to a back-end, non-web service. The back-end service should be modified, broadly speaking, in three ways:
- It should be able to recognize an attempt to authenticate using a CAS ticket instead of a username/password pair.
- When a ticket is recognized, it should be able to validate it by contacting the CAS server directly.
- When a ticket is recognized, it should also check to make sure that the ticket was sent from a host from whom it is willing to receive proxied credentials.
The first two of these requirements are straightforward. The third is necessary to prevent arbitrary web services from accessing a particular user's sensitive data ("sensitive" by the back-end server's standards) by directing a user's browser to present a session ticket and then proxying that ticket to the back-end service. For instance, if an IMAP server is modified to use CAS for authentication, it is likely that its administrator would want it to speak only to one particular web-mail front-end, not to arbitrarily many. Who knows whether a particular web-mail front-end is trusted or whether it's monitoring and keeping a copy of users' mail. (Note that even without the third requirement, web applications cannot impersonate arbitrary users to back-end applications. Malicious web applications would still need a user to present a ticket to them.)
The first two objectives are straightforward to implement. The front-end and back-end applications need to agree on a mechanism for distinguishing tickets from username/password combinations. This can be achieved through any logically consistent mechanism that doesn't depend on overlapping namespaces; for instance, if the front-end application wants to present a ticket instead of a username, it might send an empty string as the username and the ticket as a password. Or it might send a prearranged identifier as the username. Then, once the back-end service receives the ticket, it just needs to call up an HTTPS URL, using perhaps OpenSSL, JSSE, or another convenient library.
The third requirement - authenticating the front-end application - can be achieved by sharing a secret between the front-end and back-end applications or by having the back-end application validate a signature generated by the front-end application's private key. It could also use a topology-based restriction, although this is less desirable unless complemented by a scheme that's actually secure.
To facilitate topology-based restrictions, the CAS will, in the short term, be modified to support a mode whereby it codes each ticket not only for a particular username and service ID but also for a particular IP address. A back-end application attempting to validate a ticket can then check the ticket's associated IP address against the IP address the host attempting to gain access to the back-end service. This mode of the CAS will be redundant and not provide any new, meaningful security, but it might make first-pass security simpler to implement for organizations that have front-end/back-end application pairs on the same secure network.