Motivation

We wanted to be more flexible in the use of the login UI, so e.g. wanted to embed it in several places as a small panel. Moreover, we wanted to understand CAS as a pure service, not having to maintain layout information twice. Also, we wanted to support both, login postings from another site and direct login at the CAS server as a fallback.

How to get rid of the Frame after login? in the default view a successful login would lead to redirect only in that frame. If you use target="parent" in the from tag, how do you display errors then?

You would have limited layout possibilities.

So for our requirements, none of the proposed solutions were adequate.

Our Solution

We decided to use Javascript triggered redirects, which allows us to use this mechanism also on static html pages (e.g. CMS based contents). While loading we request a login ticket, then the login form displays and then it submits to the CAS login URL. If Errors are present at CAS we redirect to the referring page, adding a parameter "error_message" to that request.

CAS side

Adapting only the views and introducing if-else statements there meant having logic that belongs to the controllers in the view. Therefore we decided to inject our add-on into the spring web flow. Here are the changes we have made to login-webflow.xml (changes highlighted in blue):

</action-state>
<action-state id="sendTicketGrantingTicket">
<action bean="sendTicketGrantingTicketAction" />
<transition on="success" to="serviceCheck" />
</action-state>
<action-state id="serviceCheck">
<action bean="hasServiceCheckAction" />
<transition on="authenticatedButNoService"
to="viewGenericLoginSuccess" />
<transition on="hasService" to="generateServiceTicket" />
</action-state>
<action-state id="generateServiceTicket">
<action bean="generateServiceTicketAction" />
<transition on="success" to="warn" />
<transition on="error" to="viewLoginForm" />
<transition on="gateway" to="redirect" />
</action-state>
<!--
The "warn" action makes the determination of whether to redirect directly to the requested
service or display the "confirmation" page to go back to the server.
-->
<action-state id="warn">
<action bean="warnAction" />
<transition on="redirect" to="redirect" />
<transition on="warn" to="showWarningView" />
</action-state>
<!--
the "viewGenericLogin" is the end state for when a user attempts to login without coming directly from a service.
They have only initialized their single-sign on session.
-->
<end-state id="viewGenericLoginSuccess"
view="casLoginGenericSuccessView" />
<!--
The "showWarningView" end state is the end state for when the user has requested privacy settings (to be "warned") to be turned on. It delegates to a
view defines in default_views.properties that display the "Please click here to go to the service." message.
-->
<end-state id="showWarningView" view="casLoginConfirmView" />
<!--
The "redirect" end state allows CAS to properly end the workflow while still redirecting
the user back to the service required.
-->
<end-state id="redirect"
view="externalRedirect:${externalContext.requestParameterMap['service']}${requestScope.ticket == null ? '' : (externalContext.requestParameterMap['service'].indexOf('?') != -1 ? '&amp;' : '?') + 'ticket=' + requestScope.ticket}" />
<global-transitions>
<transition to="viewServiceErrorView"
on-exception="org.jasig.cas.services.UnauthorizedServiceException" />
</global-transitions>
</flow>

Problems encountered and suggested solutions

The solution works well, but it has a very dirty edge: introducing a new event in the submit method that allows us to distinguish between a login posting from the CAS Site and another site. Here we had to redeclare the submit method in org.jasig.cas.web.flow.AuthenticationViaFormAction (changes marked in blue):

13 Comments

The IFRAME method actually does work. As stated in emails on the CAS developer's list, it requires replacing the default view with a JavaScript redirect view.

This allows the following:
(a) errors will continue to be displayed correctly (in the iframe)
(b) redirects the browser correctly
(c) usernames and passwords are never collected on the client application

Hi there,
I made the changes described here, but I'm stuck with these problems:
- Where the file "viewRedirectToRequestor.jsp" must be placed ?
- There isn't the definition of bean with class "ProvideLoginTicketToRemoteRequestorAction" in "applicationContext.xml".
- After adding it, I get the exception:

A much simpler and elegant version is to not deal with getting a login ticket at all and to simply auto submit the CAS form via Javascript if a param such as 'auto' is submitted to the login form along with the service, username, and password parameters.

No fuss, no muss. CAS will then redirect back to your service url with a ticket that is ready to be validated. This comes in really handy if you are doing something like creating users in your system and then auto logging them in after the creation process.

I know purists will hate the fact that we put a scriptlet in there (i'm not too wild about it either), but it is alot easier than all the redirects involved for grabbing a login ticket just to submit credentials.

Just a note, you will also want to change the name of the submit button from 'submit' or else Javascript will have a fit. Hope this helps.

Here's a tardy response to Alberto Mozzone's post. Sorry for not having answered any earlier. But better late than never It still seems to be of interest:

I have forgotten to mention in this article that the view bmRedirectToRequestorView needs to be defined in the default_views.properties file, which is located at "/WEB-INF/classes". The following entries need to be added there:

This was a nice article. But i need to do the same with that latest version of CAS i.e. 3.4 . Can you please provide me the steps for the same. Atleast the stucture for login-webflow.xml as it is now using spring-webflow-2.0.xsd .

I have the same question. Does anyone have any tips on doing this in CAS 3.4.x and is there now a different way of accomplishing it with the new version? My end goal is to alter the Liferay login portlet to submit credentials to CAS over https. Out of the box IFrame portlets would hard code the service URL, breaking deep link capabilities. Thank you - Ed

If you want evict client redirection, what is proposed solution here, I think that is possible in do an external login html form implementing a org.jasig.cas.web.flow.AbstractNonInteractiveCredentialsAction like SPNEGO, X509 Certificates or remote trusted client are doing. With that philosophy, its easy have the login layout outside and the authentication logic inside CAS.

Our action inspect the request for a username and password field and construct a UsernamePasswordCredentials with that request info.

Concrete, in my system i have chained the X509 and the trusted (the first to do support for client certs and the last, to do support NTLM or Apache mod_aut_cas) non interactive check and last my AuthenticationNonInteractiveViaFormAction. I said "last" because the last authenticatior in chain always redirect to login form and in my action I save the validation errors like bindAndValidate phase of viewLoginForm are doing, and then the errors are magically rendered in the default cas login form