Hello CAS Community,
We are using CAS 6.6.14 for authentication against database and LDAP.
So far our use of CAS 66x is ok, but recently we've encountered a scenario a little bit different, that is causing us problems.
The scenario is:
1) after a successeful authentication, if our custom code concludes the password is expired,
we customized the spring web flow "login flow" to move to a custom page
with message "your password is expired, you should proceed to Recover Password"
2) once we click on a "Recover Password" button,
which should provoke navigation to another custom page,
the following error occurs:
2024-04-16 12:49:12,889 [http-nio-8443-exec-4] WARN : org.apereo.cas.authentication.MyCustomAuthenticationHandler
java.io.NotSerializableException: org.apereo.cas.authentication.MyCustomAuthenticationHandler
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1187)
at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1572)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1529)
at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1438)
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1181)
at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:350)
at java.base/java.util.TreeMap.writeObject(TreeMap.java:2758)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at java.base/java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1070)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1516)
at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1438)
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1181)
at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1572)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1529)
at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1438)
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1181)
at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1572)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1529)
at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1438)
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1181)
at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:350)
at java.base/java.util.LinkedHashMap.internalWriteEntries(LinkedHashMap.java:334)
at java.base/java.util.HashMap.writeObject(HashMap.java:1497)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at java.base/java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1070)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1516)
at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1438)
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1181)
at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1572)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1529)
at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1438)
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1181)
at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1572)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1529)
at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1438)
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1181)
at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:350)
at java.base/java.util.HashMap.internalWriteEntries(HashMap.java:1944)
at java.base/java.util.HashMap.writeObject(HashMap.java:1497)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at java.base/java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1070)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1516)
at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1438)
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1181)
at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1572)
at java.base/java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:443)
at org.springframework.webflow.core.collection.LocalAttributeMap.writeObject(LocalAttributeMap.java:333)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at java.base/java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1070)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1516)
at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1438)
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1181)
at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1572)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1529)
at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1438)
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1181)
at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:350)
at org.apereo.cas.web.flow.executor.EncryptedTranscoder.writeObjectToOutputStream(EncryptedTranscoder.java:89)
at org.apereo.cas.web.flow.executor.EncryptedTranscoder.encode(EncryptedTranscoder.java:60)
at org.apereo.cas.web.flow.executor.ClientFlowExecutionRepository.getKey(ClientFlowExecutionRepository.java:97)
at org.springframework.webflow.engine.impl.FlowExecutionImpl.assignKey(FlowExecutionImpl.java:419)
(...)
It seems the problemas cause is, the following CAS authentication class,
org.apereo.cas.authentication.DefaultAuthentication
(which contains a string reference to the successful authentication handler),
is beeing store in the flow variable "flowExecutionKey", which in Thymeleaf pages is in,
"<input type="hidden" name="execution" th:value="${flowExecutionKey}"/>",
and then, after submiting our custom page,
this flowExecutionKey is deserialized, including the refered class DefaultAuthentication.
We found what we think is a temporary fix for this problem,
which is making our "org.apereo.cas.authentication.MyCustomAuthenticationHandler" class serializable,
but this involves things like having to create new dummy parent class, not serializable, with an explicit default constructor,
(because we can't create a default constructor on our class current parent class,
which is org.apereo.cas.adaptors.jdbc.QueryAndEncodeDatabaseAuthenticationHandler)
and having to make some fields transient, to excluded them from serialization.
It's true that some CAS authentication related classes are serializable, like:
-org.apereo.cas.authentication.DefaultAuthentication
and
-org.apereo.cas.authentication.AuthenticationHandlerExecutionResult
But on the other hand, none of the
"org.apereo.cas.authentication.AuthenticationHandler" child classes are serializable,
and also, the AuthenticationHandlerExecutionResult class only contains a "String handlerName"
(name of the authentication handler that successfully authenticated a credential),
not an AuthenticationHandler interface implementation class attribute.
We couldn't find any information about this problem in the following sources:
- CAS 6 official site,
https://apereo.github.io/cas/6.6.x/- CAS community,
https://groups.google.com/a/apereo.org/g/cas-user?pli=1- Misagh Moayyed "Fawnoos blog",
https://fawnoos.com/blog/Did anybody had this problem?
Any advice or hint on what's the best solution for this problem?
Kind regards,
Luis Costa