Using CAS from external link or custom external form
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.
Other proposed solutions
The Solution
The solution is to bypass the login form in case we post a "auto" HTTP parameter with value "true".
In that case, an auto-posting form will be rendered and posted to CAS as if a user had been authenticating himself.
As even without that modification, anybody can create a site whit a login / password form which would then post it from that server to the CAS server with 2 request (first one to retrieve the values of lt & execution, second one to post it & redirect the final user to the CAS server with the session retrieved from that second request), there is no security regression here.
Modification to CAS war
You just have to edit the casLoginView.jsp file and add those modification at the head and the tail of the file :
casLoginView.jsp
...
<%@ page contentType="text/html; charset=UTF-8" %>
<%
String auto = request.getParameter("auto");
if (auto != null && auto.equals("true")) {
%>
<html>
<head>
<script language="javascript">
function doAutoLogin() {
document.forms[0].submit();
}
</script>
</head>
<body onload="doAutoLogin();">
<form id="credentials" method="POST" action="<%= request.getContextPath() %>/login?service=<%= request.getParameter("service") %>">
<input type="hidden" name="lt" value="${loginTicket}" />
<input type="hidden" name="execution" value="${flowExecutionKey}" />
<input type="hidden" name="_eventId" value="submit" />
<input type="hidden" name="username" value="<%= request.getParameter("username") %>" />
<input type="hidden" name="password" value="<%= request.getParameter("password") %>" />
<% if ("true".equals(request.getParameter("rememberMe"))) {%>
<input type="hidden" name="rememberMe" value="true" />
<% } %>
<input type="submit" value="Submit" style="visibility: hidden;" />
</form>
</body>
</html>
<%
} else {
%>
<jsp:directive.include file="includes/top.jsp" />
...
<jsp:directive.include file="includes/bottom.jsp" />
<%
}
%>Usages
With that modification done, you can create external links such as this one :
You can also create some custom external form such as this one :
<html>
<head />
<body>
<form method="GET" action="https://cas.example.com/cas/">
<p>Username : <input type="text" name="username" /></p>
<p>Password : <input type="password" name="password" /></p>
<p>Remember me : <input type="checkbox" name="rememberMe" value="true" /></p>
<p><input type="submit" value="Login !" /></p>
<input type="hidden" name="auto" value="true" />
<input type="hidden" name="service" value="http://app.example.com/myapp/" />
</form>
</body>
</html>
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.
So, you can leave your casLoginView.jsp as bundled and implements all login inside that Action