View on GitHub

Single Sign-On for the Web

Webflow Customization

CAS uses Spring Web Flow to do “script” processing of login and logout protocols. Spring Web Flow builds on Spring MVC and allows implementing the “flows” of a web application. A flow encapsulates a sequence of steps that guide a user through the execution of some business task. It spans multiple HTTP requests, has state, deals with transactional data, is reusable, and may be dynamic and long-running in nature. Each flow may contain among many other settings the following major elements:

Spring Web Flow presents CAS with a pluggable architecture where custom actions, views and decisions may be injected into the flow to account for additional use cases and processes. Note that to customize the weblow, one must possess a reasonable level of understanding of the webflow’s internals and injection policies. The intention of this document is not to describe Spring Web Flow, but merely to demonstrate how the framework is used by CAS to carry out various aspects of the protocol and business logic execution.

Login Flow

The flow in CAS is given a unique id that is registered inside a flowRegistry component. Support is enabled via the following configuration snippets in cas-servlet.xml:

Components

<webflow:flow-executor id="loginFlowExecutor" flow-registry="loginFlowRegistry">
	<webflow:flow-execution-attributes>
	  <webflow:always-redirect-on-pause value="false" />
	  <webflow:redirect-in-same-state value="false" />
	</webflow:flow-execution-attributes>
	<webflow:flow-execution-listeners>
	  <webflow:listener ref="terminateWebSessionListener" />
	</webflow:flow-execution-listeners>
</webflow:flow-executor>

...

<webflow:flow-registry id="loginFlowRegistry" flow-builder-services="builder">
    <webflow:flow-location path="/WEB-INF/login-webflow.xml" id="login" />
</webflow:flow-registry>

login-flow.xml Overview

The login flow is at a high level composed of the following phases:

A high-level diagram detailing major states in the flow is presented here:

Acceptance of user credentials and invoking the authentication handler components is carried out by:

<bean id="authenticationViaFormAction" class="org.jasig.cas.web.flow.AuthenticationViaFormAction"
        p:centralAuthenticationService-ref="centralAuthenticationService"
        p:warnCookieGenerator-ref="warnCookieGenerator"/>

Handling authentication failures, mapping the result of which event to a new state is carried out by:

<bean id="authenticationExceptionHandler" class="org.jasig.cas.web.flow.AuthenticationExceptionHandler" />

...

<action-state id="realSubmit">
	<evaluate expression="authenticationViaFormAction.submit(flowRequestContext, flowScope.credential, messageContext)" />

  	<transition on="success" to="sendTicketGrantingTicket" />
    <transition on="authenticationFailure" to="handleAuthenticationFailure" />
    <transition on="error" to="generateLoginTicket" />
</action-state>

....
<action-state id="handleAuthenticationFailure">
    <evaluate expression="authenticationExceptionHandler.handle(currentEvent.attributes.error, messageContext)" />
    <transition on="AccountDisabledException" to="casAccountDisabledView"/>
    <transition on="AccountLockedException" to="casAccountLockedView"/>
    <transition on="CredentialExpiredException" to="casExpiredPassView"/>
    <transition on="InvalidLoginLocationException" to="casBadWorkstationView"/>
    <transition on="InvalidLoginTimeException" to="casBadHoursView"/>
    <transition on="FailedLoginException" to="generateLoginTicket"/>
    <transition on="AccountNotFoundException" to="generateLoginTicket"/>
    <transition on="UNKNOWN" to="generateLoginTicket"/>
</action-state>

Certain error conditions are also classified as global transitions, particularly in cases of unauthorized services attempting to use CAS:

<global-transitions>
    <transition to="viewLoginForm" on-exception="org.jasig.cas.services.UnauthorizedSsoServiceException"/>
    <transition to="viewServiceErrorView" on-exception="org.springframework.webflow.execution.repository.NoSuchFlowExecutionException" />
    <transition to="viewServiceErrorView" on-exception="org.jasig.cas.services.UnauthorizedServiceException" />
</global-transitions>

Logout Flow

The flow in CAS is given a unique id that is registered inside a flowRegistry component. Support is enabled via the following configuration snippets in cas-servlet.xml:

Components

<webflow:flow-executor id="logoutFlowExecutor" flow-registry="logoutFlowRegistry">
    <webflow:flow-execution-attributes>
      <webflow:always-redirect-on-pause value="false" />
      <webflow:redirect-in-same-state value="false" />
    </webflow:flow-execution-attributes>
    <webflow:flow-execution-listeners>
      <webflow:listener ref="terminateWebSessionListener" />
    </webflow:flow-execution-listeners>
</webflow:flow-executor>

...

<webflow:flow-registry id="logoutFlowRegistry" flow-builder-services="builder">
	<webflow:flow-location path="/WEB-INF/logout-webflow.xml" id="logout" />
</webflow:flow-registry>

logout-flow.xml Overview

The logout flow is at a high level composed of the following phases:

The Logout protocol is initiated by the following component:

<bean id="logoutAction" class="org.jasig.cas.web.flow.LogoutAction"
        p:servicesManager-ref="servicesManager"
        p:followServiceRedirects="${cas.logout.followServiceRedirects:false}"/>

Front-channel method of logout is specifically handled by the following component:

<bean id="frontChannelLogoutAction" class="org.jasig.cas.web.flow.FrontChannelLogoutAction"
        c:logoutManager-ref="logoutManager"/>

Termination of Web Flow Sessions

Each flow is registered with a TerminateWebSessionListener whose job is expire the web session as soon as the webflow is ended. The goal is to decrease memory consumption by deleting as soon as possible the web sessions created mainly for login and logout processes. By default, the webflow session is configured to be destroyed in 2 seconds, once the session has ended.

Extending the Webflow

The CAS webflow provides discrete points to inject new functionality. Thus, the only thing to modify is the flow definition where new beans and views can be added easily with the Maven overlay build method.

Adding Actions

Adding Spring Web Flow actions typically involves the following steps:

Once the action bean is configured, you may define it inside the login-webflow.xml:

<action-state id="actionStateId">
	<action bean="customActionBeanId" />
	<transition on="success" to="doThis" />
	<transition on="error" to="doThat" />
</action-state>

Adding Views

Adding Spring Web Flow views involves the following steps:

A sample is presented here:

### View for password update
passwordUpdateView.(class)=org.springframework.web.servlet.view.JstlView
passwordUpdateView.url=/WEB-INF/view/jsp/default/ui/passwordUpdateView.jsp