CAS 2 and Redirects

Introduction and about this page

This page is supplemental documentation of how redirects work in CAS 2.

CAS 2 behavior is governed by the specification.

Getting there: when CAS server issues redirects

CAS server may issue redirects in response to requests for its /login URL, which is mapped to the Login servlet.

LoginServlet delcaration and mapping in web.xml
    <!-- Login -->
    <servlet>
      <servlet-name>Login</servlet-name>
      <servlet-class>edu.yale.its.tp.cas.servlet.Login</servlet-class>
    </servlet>
   <!-- ... --->
    <servlet-mapping>
      <servlet-name>Login</servlet-name>
      <url-pattern>/login</url-pattern>
    </servlet-mapping>

The LoginServlet dispatches requests considering

  1. whether authentication can be accomplished via CAS single sign on session cookie (valid cookie is present and "renew" behavior doesn't opt us out of single sign on)
  2. whether the "gateway" parameter is present indicating we should redirect to the service without a ticket rather than prompt for primary credentials (username and password)
  3. whether valid primary credentials (username and password) are presented on the request

The Login servlet will use a RequestDispatcher to forward to one of the relative URLs with which it was configured at servlet initialization.

For example:

Example of forwarding the request
app.getRequestDispatcher(serviceSuccess).forward(request, response);
Configuring the Login views in web.xml
    <!-- Login page URL -->
    <context-param>
        <param-name>edu.yale.its.tp.cas.loginForm</param-name>
        <param-value>/login.jsp</param-value>
    </context-param>

    <!-- Page URL for generic login success message -->
    <context-param>
        <param-name>edu.yale.its.tp.cas.genericSuccess</param-name>
        <param-value>/success.jsp</param-value>
    </context-param>

    <!-- Page URL for simple JavaScript-based redirection -->
    <context-param>
        <param-name>edu.yale.its.tp.cas.redirect</param-name>
        <param-value>/redirect.jsp</param-value>
    </context-param>

    <!-- Page URL for login success message when 'service' is specified -->
    <context-param>
        <param-name>edu.yale.its.tp.cas.serviceSuccess</param-name>
        <param-value>/goService.jsp</param-value>
    </context-param>

    <!-- Page URL for login success message + confirmation
         when 'service' is specified -->
    <context-param>
        <param-name>edu.yale.its.tp.cas.confirmService</param-name>
        <param-value>/warnService.jsp</param-value>
    </context-param>

Redirecting

The two views that are interesting for purposes of this Wiki article about redirection in CAS 2 are edu.yale.its.tp.cas.redirect and edu.yale.its.tp.cas.serviceSuccess .

Redirect

The edu.yale.its.tp.cas.redirect view is the way that the Login servlet accomplishes CAS Gateway. It's CAS server giving up on authenticating this user transparently and redirecting back to the service rather than forcing interactive authentication.

By default this is implemented in redirect.jsp:

redirect.jsp
<%@ page session="false" %>
<%
  String serviceId = (String) request.getAttribute("serviceId");
%>
<html>
<head>
<title>Yale Central Authentication Gateway</title>
 <script>
  window.location.href="<%= serviceId %>";
 </script>
</head>

<body bgcolor="#0044AA">
 <noscript>
  <p>
   Click <a href="<%= serviceId %>">here</a>
   to access the service you requested.
  </p>
 </noscript>
</body>

</html>

This redirect is accomplished by means of a JavaScript script, with a fallback of simple HTML with a link the user can click if he has JavaScript turned off.

serviceSuccess

edu.yale.its.tp.cas.serviceSuccess is more interesting. It is CAS server attempting to redirect back to the service with a ticket.

goService.jsp
<%@ page session="false" %>
<%
  String serviceId = (String) request.getAttribute("serviceId");
  String token = (String) request.getAttribute("token");
  String service = null;
  boolean safari = true;  // will set this below
  if (serviceId.indexOf('?') == -1)
    service = serviceId + "?ticket=" + token;
  else
    service = serviceId + "&ticket=" + token;
  service =
    edu.yale.its.tp.cas.util.StringUtil.substituteAll(service, "\n", "");
  service = 
    edu.yale.its.tp.cas.util.StringUtil.substituteAll(service, "\r", "");
  service =
    edu.yale.its.tp.cas.util.StringUtil.substituteAll(service, "\"", "");

  // Set Refresh header on initial login only if user isn't using Safari.
  // Fixes security bug where Safari would repost login credentials to the
  // web application rather than to CAS.
  if (((String)request.getAttribute("first")).equals("false")
    || request.getHeader("User-Agent") == null
    || request.getHeader("User-Agent").indexOf("Safari") == -1) {
    safari = false;
  }
%>
<html>
<head>
<title>Yale Central Authentication Service</title>
<% if (!safari) { %>
<script>
  window.location.href="<%= service %>";
</script>
<% } %>
</head>

<body bgcolor="#0044AA" link="#ffffff" alink="#ffffff" vlink="#ffffff">
<% if (!safari) { %>
<noscript>
<% } %>
  <p>Login successful.</p>
  <p>
   Click <a href="<%= service %>">here</a>
   to access the service you requested.
  </p>
<% if (!safari) { %>
</noscript>
<% } %>
</body>

</html>

Provided the user isn't using Safari, this redirect is accomplished by means of a JavaScript script, with a fallback of simple HTML with a link the user can click if he has JavaScript turned off. If the user is using Safari, then it will always render the manual link.

(A potential improvement would be to only suppress the JavaScript redirect for Safari in the case where the password or other primary credential is present on the request.)