oreilly.comSafari Books Online.Conferences.


CAS+: Single Sign-On with Jifty (Part 1)
Pages: 1, 2, 3

Jifty Components

Before I go too far into the description of the code and process, let me give you a crash course in the pieces that make up Jifty. Here are the most important ones:

  • Dispatcher. The dispatcher (CASPlus::Dispatcher in CAS+) determines how incoming requests are handled. By default, Jifty attempts to find a view with the same name as the URL, but custom dispatcher rules are written to modify this behavior (or overrule it altogether).
  • Models. All information in Jifty is stored in models. This article is focused on the login process, so I do not directly discuss the models used to store the various tickets and sessions, but I've provided links to them. Any package starting with the name "CASPlus::Model::" is a data model that is stored in the database.
  • Actions. Actions are used by Jifty to create, update, delete, search for, or otherwise act upon records in a model. Normally, actions are constructed by views to automatically build forms required to activate the actions on submit. In the case of CAS+, I've had to manually activate them in order to follow the protocol exactly.
  • Views. Views are used to display any response to a request. The views of CAS+ are all written in templates using Mason. The views of CAS+ can be found under the "share/web/templates" directory.

This is a pretty typical arrangement for MVC web frameworks.


The key to understanding most Jifty applications is in the dispatcher. Basically, the Jifty dispatcher determines what views to display for any given request made by a client. Jifty has a declarative syntax for building the rules, which makes it particularly easy to determine what's going on. Because of this, I've been able to define one rule per action step in the CAS protocol, since each step can be uniquely identified by the dispatcher.

The Jifty dispatcher for CAS+ is named CASPlus::Dispatcher. (The dispatcher is always named App::Dispatcher for all Jifty applications where "App" is the name of the main application class.)

Login check

The first step the CAS server is aware of is a request for login. This can happen either as a direct request from the user (i.e., he directly requests the /login URL from the server) or by a redirect from a web service when it wants to verify the user's identity. In Jifty, this looks like the following:

on GET 'login' => run {
    my $service = get 'service';
    my $renew   = get 'renew';
    my $gateway = get 'gateway';
    # Check for existing login
    my $action = Jifty->web->new_action(
        class     => 'LoginCheck',
        arguments => {
            service => $service,
            renew   => $renew,
            gateway => $gateway,

 # Deal with $action->result...

The first line declares that any GET request coming to "/login" should be handled by running the LoginCheck action (CASPlus::Action::LoginCheck), which checks to see whether the user already has a valid login with the system.[2] I normally don't specify that it has to be a GET in my Jifty dispatchers, but in this case it was convenient to separate the GET from the POST since POST is used for login submission.

The next three lines fetch query string parameters. The "service" parameter may hold a URL. This is the URL to redirect to upon successful (or in some cases, failed) login. The "renew" parameter may be set in order to force the user to log in again. This can be useful if a service wants to reconfirm that the user is still the correct person for additional security. If this is set, the LoginCheck action will always fail because a new login is required by the service, even if the user already has valid login credentials. The "gateway" parameter is used by a service just to perform a quick check for login.

The actual action carried out by LoginCheck is complex, but mundane. I'll summarize by explaining the base cases and then the exceptions.

  • Direct Login. If the user goes directly to the login page and requests login. LoginCheck will return success if the user already has a login cookie (called the Ticket Granting Cookie, or TGC in the CAS protocol). If success is returned, a status screen stating that she is logged in will be returned to the user. If failure is returned, the login form will be displayed for the user to fill in.
  • Service Login. If the user has been sent to the "/login" by a service (i.e., the "service" parameter is set to a URL), then a slightly different set of actions are performed. Normally, if the user has a login cookie, the user is immediately redirected to the URL set in "service" with an additional "ticket" parameter set to a new CAS service ticket. That is, a ticket for validating the user's credentials is passed back to the web service without the user's direct knowledge (unless he happens to notice the URL in the location bar in his browser flick to the CAS server and then back with an extra parameter attached). If the user does not have a login ticket, he is shown the login screen with the "service" parameter set to the URL for later redirect.

Those are the two typical cases. If you were to implement your own SSO system, these might be the two basic cases for your service and you wouldn't necessarily need to worry about the exceptions. However, CAS implements some special features that allow for additional flexibility.

  • Login Warning. On login, the user is presented with a checkbox allowing her to request a login warning. This allows users that want to monitor their privacy in more detail to be notified whenever a service requests their credentials from the CAS server. Therefore, during the service login above, the user will be shown an additional status screen notifying her that a web service is requesting her identity rather than redirecting straight back to the web service. She can then choose to click on the link to continue, or to not do so, thus preventing the other service from authenticating her.
  • Login Renewal. On the other hand, a service might want additional security. It can request this by including the "renew" parameter in the request. In this case, the LoginCheck will always fail. This will result in the login form being displayed. The user continues having a valid TGC, but unless he logs in again, he will not be returned to the service with a service ticket. This allows the original service to re-verify the user before taking an operation requiring an elevated level of security.
  • Login Gateway. The final special case is that a service can merely check to see whether the user is logged in without actually requiring a login. In this case, the "gateway" parameter is set by the service. In this case, LoginCheck always returns success. If the user has requested a warning, she is still warned about the identity information being sent. If the user is logged in, the service will get a service ticket in return. If not, CAS will redirect back without a service ticket letting the service know that the user is not logged.

In summary, a service requests a login by redirecting the user to "/login" with at least the "service" parameter set. If the user has a login cookie (TGC), the browser is redirected back to the URL set in the "service" parameter with an additional "ticket" parameter attached containing a service ticket. If the user does not have a TGC, the user will be shown a login screen and asked to log in.

Login form

The first time the user reaches the "/login" URL, he will be shown a login form (share/web/templates/login). Once filled, the form is submitted and processed. This is handled by this dispatcher rule:

on POST 'login' => run {
    my $username = get 'username';
    my $password = get 'password';
    my $lt       = get 'lt';
    my $service  = get 'service';
    my $warn     = get 'warn';
    # The login screen uses a standard action, find it
    my $login = Jifty->web->new_action(
        class     => 'Login',
        moniker   => 'login',
        arguments => {
            username => $username,
            password => $password,
            lt       => $lt,
            service  => $service,
            warn     => $warn,
    # handle $action->result success or failure...

This time, the dispatcher handles POST requests to "/login" by running the Login action.

The Login action (CASplus::Action::Login) makes its decisions on the basis of five different parameters submitted with the form. The "username" and "password" parameters are exactly what you would expect, the username and password submitted by the user. CAS does not require password authentication, but it is the form most web developers and end users are familiar with. CAS+ currently only supports password authentication, but other forms of authentication may be added in the future.

The "lt" parameter is an interesting one that is worth some elaboration. It represents the Login Ticket (LT), a unique identifier placed as a hidden parameter within every CAS login form. These are represented in CAS+ by the LoginAttempt model. A login, whether successful or not, may only use an LT for one submission. The purpose of this parameter is to prevent someone from performing what is called a "replay attack." Say you use a public terminal to log in to CAS, do your work, and then log out of CAS, but your browser session remains open. A malicious user could use the Back button of the browser to go back to the login form and repost it (since POSTs are often cached with the history on most browsers) and gain access to your resources. However, because a Login Ticket may never be used more than once, the attack is thwarted most of the time.[3] This may not be quite the problem it once was with older browsers, but it is still an interesting security precaution to be aware of.

The "service" parameter is the URL of the web service requesting the user's identity. This URL is put into the form so that the user (who does not yet have a session within CAS) may be redirected back to the web service upon successful login. The "warn" parameter is set when the "Warn me when logging into additional services" checkbox is checked. This is the setting that allows the user to be notified any time another web service attempts to gain access to the user's identity.

The Login action processes the most important of these parameters in the way you would expect. If the "lt" has already been used or is not one the CAS server has issued, the login will fail. If the "username" and "password" don't match a user record, the Login action will fail. On failure, the dispatcher returns the user to the form with an error message.

If the "username" and "password" match a record in the users table, a cookie (the TGC) is set for later reference to the user's session. A TGC record is created in the database (in the SSOSession model), which is used to remember the user's session information for later reference. After successfully logging in, the Login action will perform additional actions. If the user checked the "warn" checkbox, a note is made in the session record to warn the user on future LoginCheck actions.

Finally, if "service" is specified, a Service Ticket (ST) is generated (and stored in the ServiceSession model). A service ticket is an identifier attached to the user's login session that associated the "service" URL with the session. During the next phase of CAS authentication, the web service uses this identifier to validate the user's login claim and learn the user's identity.

On success, the dispatcher will redirect the user back to the "service" URL with the Service Ticket attached if the "service" parameter was set. The Service Ticket is added to the "service" URL in the "ticket" parameter. If no "service" URL has been given, the dispatcher just shows a page stating that login was successful.

Pages: 1, 2, 3

Next Pagearrow

Sponsored by: