View on GitHub

Single Sign-On for the Web

Work in progress

You are viewing the development documentation for the CAS server. The functionality presented here is not released yet. This is a work in progress and will be continually updated as development makes progress. To view the documentation for a specific CAS server version, please choose an appropriate version.

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

<bean name="loginFlowExecutor" class="org.springframework.webflow.executor.FlowExecutorImpl" 
    c:definitionLocator-ref="loginFlowRegistry"
    c:executionFactory-ref="loginFlowExecutionFactory"
    c:executionRepository-ref="loginFlowExecutionRepository" />

...

<webflow:flow-registry id="loginFlowRegistry" 
    flow-builder-services="builder" base-path="/WEB-INF/webflow">
    <webflow:flow-location-pattern value="/login/*-webflow.xml"/>
</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-executor>

...

<webflow:flow-registry id="logoutFlowRegistry" 
     flow-builder-services="builder" base-path="/WEB-INF/webflow">
    <webflow:flow-location-pattern value="/logout/*-webflow.xml"/>
</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

CAS provides a facility for storing flow execution state on the client in Spring Webflow. Flow state is stored as an encoded byte stream in the flow execution identifier provided to the client when rendering a view. The following features are presented via this strategy:

By default, the conversational state of Spring Webflow is managed inside the application session, which can time out due to inactivity and must be cleared upon the termination of flow. Rather than storing this state inside the session, CAS automatically attempts to store and keep track of this state on the client in an encrypted form to remove the need for session cleanup, termination and replication.

<bean id="loginFlowExecutionRepository" 
    class="org.jasig.spring.webflow.plugin.ClientFlowExecutionRepository"
    c:flowExecutionFactory-ref="loginFlowExecutionFactory"
    c:flowDefinitionLocator-ref="loginFlowRegistry"
    c:transcoder-ref="loginFlowStateTranscoder" />

Default encryption strategy controlled via the loginFlowStateTranscoder component is using the 128-bit AES in CBC ciphering mode with compression turned on. These settings can be controlled via the following settings defined in the cas.properties file:

# cas.webflow.cipher.alg=AES
# cas.webflow.cipher.mode=CBC
# cas.webflow.cipher.padding=PKCS7
# cas.webflow.keystore=classpath:/etc/keystore.jceks
# cas.webflow.keystore.type=JCEKS
# cas.webflow.keystore.password=changeit
# cas.webflow.keyalias=aes128
# cas.webflow.keypassword=changeit
Usage Warning!

While the above settings are all optional, it is recommended that you provide your own configuration and settings for encrypting and transcoding of the web session state.

Required Service for Authentication Flow

By default, CAS will present a generic success page if the initial authentication request does not identify the target application. In some cases, the ability to login to CAS without logging in to a particular service may be considered a misfeature because in practice, too few users and institutions are prepared to understand, brand, and support what is at best a fringe use case of logging in to CAS for the sake of establishing an SSO session without logging in to any CAS-reliant service.

As such, CAS optionally allows adopters to not bother to prompt for credentials when no target application is presented and instead presents a message when users visit CAS directly without specifying a service.

This behavior is controlled via cas.properties:

# Indicates whether an SSO session can be created if no service is present.
# create.sso.missing.service=false

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

Acceptable Usage Policy Flow

CAS presents the ability to allow the user to accept the usage policy before moving on to the application. The task of remembering the user’s choice is kept in memory by default and will be lost upon container restarts and/or in clustered deployments. Production-level deployments of this feature would require modifications to the flow such that the retrieval and/or acceptance of the policy would be handled via an external storage mechanism such as LDAP or JDBC.

Configuration

Enable Webflow

<transition on="success" to="acceptableUsagePolicyCheck" />
<!-- Enable AUP flow	
<action-state id="acceptableUsagePolicyCheck">
    <evaluate expression="acceptableUsagePolicyFormAction.verify(flowRequestContext, flowScope.credential, messageContext)" />
    <transition on="success" to="sendTicketGrantingTicket" />
    <transition to="acceptableUsagePolicyView" />
</action-state>
...

-->

Configure Storage

The task of remembering and accepting the policy is handled by AcceptableUsagePolicyFormAction. Adopters may extend this class to retrieve and persistent the user’s choice via an external backend mechanism such as LDAP or JDBC.

<bean id="acceptableUsagePolicyFormAction" 
      class="org.jasig.cas.web.flow.AcceptableUsagePolicyFormAction"/>