Implementing a required checkbox on CAS login

By Andrew Petro
December 2, 2010

Your institution may have an Acceptable Use Policy or other policy to which users must agree in order to use your online systems. You might want to require users to indicate acceptance of this policy before authenticating via Jasig CAS single sign on. In this blog post, I explore how this can be implemented, showcasing the power and extensibility of Jasig CAS's use of Spring Web Flow in the process.

This post is in response to a recent thread on the cas-dev list on this topic where other, perhaps better, solutions were also suggested by others. Certainly this isn't the only solution. At the least, I think it's an interesting, blog-worthy solution.

Starting point

I started from CAS server version 3.4.3.1, the latest Jasig CAS server release, following these best practices for adopting CAS via "Maven overlay".   That is, I didn't download the CAS 3.4.3.1 release from the Jasig website via that link I just gave you -- I used Maven to define my own local customized CAS project that overlays on the CAS 3.4.3.1 release. I've attached my source code, linked below. First, I'd like to talk through my changes.

Generic vanilla unmodified CAS login

How to implement the change

Adding the checkbox to the login page JSP

First, I add a checkbox to the JSP implementing the UI for the login form. The login JSP is /src/main/webapp/WEB-INF/view/jsp/default/ui/casLoginView.jsp, so I added that JSP (and only that JSP) to my overlay, thereby replacing the default casLoginView.jsp that ships with CAS. I added this block:

<div class="row check">

     <input id="acceptpolicy" name="acceptpolicy" value="true"

tabindex="4" accesskey="I" type="checkbox" />
    

<label for="acceptpolicy">I accept the institutional Acceptable Use Policy.</label>

     <c:if test="${failedToAcceptPolicy}">
         <div class="errors">
        You must accept the policy to authenticate.
    </div>
     </c:if>
                       
  </div>

right where you'd expect in casLoginView.jsp. It creates a new checkbox indicating acceptance of institutional policy and responds to an adhoc invented-just-for-this variable representing the case where the form was submitted without that policy-acceptance checkbox checked.

CAS login modified to show a checkbox asking if the user accepts the acceptable use policy

Adding server-side validation of the checkbox

Cool, there's a checkbox. So far, this is just a checkbox that doesn't do anything. I can still log in without accepting the policy, whereas the requirement is that authentication only succeed if the box is checked. So, next, I'll need to implement server-side checking of that checkbox. (You could of course implement client-side checking via JavaScript, or supplement the server-side checking with client-side checking. I'll leave that as an exercise for the reader, or as a subject for another blog post.) CAS uses Spring Web Flow. So, while one could implement server-side handling of this checkbox alongside handling of the "warn" checkbox in AuthenticationViaFormAction, or extend the UsernamePasswordCredentials to create UsernamePasswordAndAcceptableUsePolicyAcceptanceCredentials, it might be best to just handle this right in the Spring Web Flow configuration. in login-webflow.xml, I modified the "viewLoginForm" state and added a new "requireCheckbox" decision state.


   <view-state id="viewLoginForm" view="casLoginView" model="credentials">
        <var name="credentials" 
            class="org.jasig.cas.authentication.principal.UsernamePasswordCredentials" />
        <binder>
            <binding property="username" />
            <binding property="password" />
        </binder>
        <on-entry>
            <set name="viewScope.commandName" value="'credentials'" />
        </on-entry>
		<transition on="submit" bind="true" validate="true" to="requireCheckbox">
            <set name="flowScope.credentials" value="credentials" />
            <set name="flowScope.failedToAcceptPolicy" 
                value="requestParameters.acceptpolicy neq 'true'" />
            <evaluate expression="authenticationViaFormAction.doBind(flowRequestContext, 
                flowScope.credentials)" />
        </transition>
    </view-state>
	

    <decision-state id="requireCheckbox">
		<if test="requestParameters.acceptpolicy eq 'true'" 
                    then="realSubmit" else="viewLoginForm" />
    </decision-state>

If the user doesn't accept the policy, then the web flow both sets a flag so the JSP can display an error message, and it transitions back to the form display so that the user can see that message and decide what to do about it.

Results

There's an acceptable use policy checkbox, and you have to check it to successfully log in. If you don't check it, you can't log in. That's enforced server-side rather than client side, but no custom Java is required to implement the server-side checking.

CAS login showing error message stating that policy must be accepted to log in

Cleanup

CAS implements good localization, so it's a shame to hard-code any user-facing text in the JSP when these strings can be externalized for localization. Rather than coding the label and error message right in the JSP, you might want to externalize that like the other messages in Spring. I've lazily left that as an exercise for the reader.

Source code

I've attached a .tar.gz of a Maven overlay project demonstrating the changes. You can build it to a /target/cas.war via "mvn package" executed in its root. This is exactly along the lines of the Maven Overlay best practice document linked above. This project contains the pom.xml file invoking the overlay, the modified JSP and web flow definition, and a default log4j.xml file with the logging cranked up since I figure if you download and try out this code, you'll want to see what it's doing in excruciating detail.

Backporting to CAS 3.3.5

If you're running CAS 3.3.5, you may wish to think about upgrading to CAS 3.4. In the meantime, this same solution will work in CAS 3.3.5 with a little backporting. CAS 3.3.5 uses Spring Web Flow 1 rather than 2, so the web flow syntax is a little different. The addition to the JSP is the same, however. I modified the "bindAndValidate" state and added a new "requireCheckbox" state:



<action-state id="bindAndValidate">
   <action bean="authenticationViaFormAction" />
   <transition on="success" to="requireCheckbox">
       <set attribute="failedToAcceptPolicy" scope="flow" 
           value="${requestParameters.acceptpolicy neq 'true'}"/>
   </transition>
   <transition on="error" to="viewLoginForm" />
</action-state>

<decision-state id="requireCheckbox">
    <if test="${requestParameters.acceptpolicy eq 'true'}" 
        then="submit" else="viewLoginForm"/>
</decision-state>   

I've also attached a Maven Overlay-using project demonstrating this in CAS 3.3.5. Note that you should use CAS 3.3.5.1 (that is, 3.3.5 with security patches) if you're running CAS 3.3 in production.

AttachmentSize
must_accept_policy.png57.04 KB
cas-server-3_4_3_1-with-aup-checkbox.tar.gz7.04 KB
cas-server-3_3_5-with-aup-checkbox.tar.gz4.26 KB
with_aup_checkbox.png1.68 MB
vanilla_cas_login.png1.47 MB

Your Blogmaster:

apetro's picture

Andrew Petro

After graduating with a degree in Computer Science from Yale University in 2004, Andrew stayed on to serve his alma mater as a casual systems programmer with the Technology & Planning group. His interests include automated software testing, application frameworks, and electronic security. Projects in which Andrew has been involved include the Central Authentication Service, YaleInfo Portal (Yale's uPortal implementation), the Jasig uPortal project, and the Jasig CAS project. Andrew has previously served on the Jasig uPortal and CAS steering committees, has been the release engineer for uPortal, and has been published in the Communications of the Association for Computing Machinery on the topic of electronic voting. In spring 2006 Andrew joined Unicon full time, serving various roles, including now as the Cooperative Support for CAS technical lead.