Best way to implement Post Authentication actions

103 views
Skip to first unread message

Bobby Esfandiari

unread,
Nov 22, 2019, 4:51:31 PM11/22/19
to CAS Community
Hello,

I'm trying to find a way of updating a DateTime field in my user table, to indicate when that user was last logged in.
My best guess right now is creating a new handler that implements the org.apereo.cas.authentication.PrePostAuthenticationHandler interface and adding my functionality in the PrePostAuthenticationHandler() method.

Just wondering if this is the correct way though or if there is a better way of going about it. Any examples/references would be much appreciated.

Thank you.

Andy Ng

unread,
Nov 24, 2019, 9:42:38 AM11/24/19
to CAS Community
Hi Bobby,

I have searched around the CAS 6 documentation, seems like there are nothing similar to your use case build in (Althoguh it is still quite possible that such feature exist but I didn't find it). I guess custom implementing might be an feasible idea.

If I am customizing to add last use time, instead of implements from PrePostAuthenticationHandler, I think I would just extends from the existing AuthenitcaiontHandler and add my postAuthentication() function instead.

- I would go and make an Authenticaiton Handler, extending QueryDatabaseAuthenticationHandler itself
- Then, add the timestamp feature in the postAuthentication() function

If you do it that way, there will be no need to create your JDBC from scretch, and also have your last use timestamp implemented.

See if the above help...

Cheers!
- Andy




Bobby Esfandiari

unread,
Nov 25, 2019, 6:47:12 PM11/25/19
to CAS Community
Hi Andy,

Thanks for taking the time to look at this.
I did implement a new custom handler by extending QueryDatabaseAuthenticationHandler. According to the logs, the new handler is being picked up by CAS and successfully used. However, the postAuthenticate() method does not seem to be getting called at all. 
Do I need to make an explicit call to this method? I assumed that CAS would do that somewhere, as a part of its pipeline but I'm also not seeing it anywhere in the source code.

Andy Ng

unread,
Nov 25, 2019, 8:27:41 PM11/25/19
to CAS Community
Hi Bobby,

> the postAuthenticate() method does not seem to be getting called at all
How did you verified that postAuthenticate does not get called? Did you use some logs or you just try executing some post JDBC query and they didn't get called?

Have you used the keyword @Override to make sure your function did override the parent function?

Also, mind if you share your custom authentication handler with us (only the part that are related is ok)?

I haven't used the postAuthenticate() function myself before, but looking at the source code it seems very likely that postAuthenticate should be called when authenticate happens...

See that there are one of the Official AuthenticationHandler (i.e. TokenAuthenticaiontHandler) uses postAuthenticate here, didn't seems:
https://github.com/apereo/cas/blob/v6.1.2/support/cas-server-support-token-authentication/src/main/java/org/apereo/cas/token/authentication/TokenAuthenticationHandler.java#L71

Also, postAuthentate logic is here:

See if the above helps!

Cheers!
- Andy

Bobby Esfandiari

unread,
Nov 26, 2019, 12:11:52 PM11/26/19
to CAS Community
Andy,

I checked both the logs and the database and neither showed evidence of the postAuthenticate() method actually being called. Here is my custom handler:

public class CustomAuthenticationHandler extends QueryDatabaseAuthenticationHandler {

Logger LOGGER = LoggerFactory.getLogger(CustomAuthenticationHandler.class);

private final String sql;
private final String fieldPassword;
private final String fieldExpired;
private final String fieldDisabled;
private final Map<String, Object> principalAttributeMap;

public CustomAuthenticationHandler(final String name,
final ServicesManager servicesManager,
final PrincipalFactory principalFactory,
final Integer order,
final DataSource dataSource,
final String sql,
final String fieldPassword,
final String fieldExpired,
final String fieldDisabled,
final Map<String, Object> attributes) {
super(name, servicesManager, principalFactory, order, dataSource, sql, fieldPassword, fieldExpired, fieldDisabled, attributes);
this.sql = sql;
this.fieldPassword = fieldPassword;
this.fieldExpired = fieldExpired;
this.fieldDisabled = fieldDisabled;
this.principalAttributeMap = attributes;

if (StringUtils.isBlank(this.fieldPassword)) {
LOGGER.warn("When the password field is left undefined, CAS will skip comparing database and user passwords for equality "
+ ", (specially if the query results do not contain the password field),"
+ "and will instead only rely on a successful query execution with returned results in order to verify credentials");
}
}

@Override
public AuthenticationHandlerExecutionResult postAuthenticate(Credential credential, AuthenticationHandlerExecutionResult result) {

LOGGER.debug("==================================================INSIDE POSTAUTHENTICATE==================================================");
Integer updateResult = updateLastLogin((UsernamePasswordCredential) credential);

if(updateResult != 1)
LOGGER.debug("==================================================BAD UPDATE==================================================");
else
LOGGER.debug("==================================================GOOD UPDATE==================================================");

return super.postAuthenticate(credential, result);
}

private Integer updateLastLogin(final UsernamePasswordCredential credential) {
LOGGER.info("INSIDE updateLastLogin");
return getJdbcTemplate().update("update user_table set last_login = NOW() WHERE username = '" + credential.getUsername() + "';");
}
}

As previously mentioned, the handler itself seems to be getting picked up successfully since I'm seeing these messages in the logs:
cas_1                | 2019-11-26 17:09:29,675 TRACE [org.apereo.cas.authentication.DefaultAuthenticationBuilder] - <Recording authentication handler result success under key [CustomAuthenticationHandler]>
cas_1                | 2019-11-26 17:09:29,675 DEBUG [org.apereo.cas.authentication.PolicyBasedAuthenticationManager] - <Authentication handler [CustomAuthenticationHandler] successfully authenticated [UsernamePasswordCredential(username=myusername, source=null, customFields={})]>

Thanks again!

Bobby Esfandiari

unread,
Nov 26, 2019, 4:11:06 PM11/26/19
to CAS Community
UPDATE

After digging into this further, with debugging and increased log levels, it appears that the method is actually getting hit and issuing the update command to the DB. However, the update is immediately rolled back so the database status is unchanged.
This is the new log message that comes from trying to update the database:
cas_1                | 2019-11-26 20:13:07,143 DEBUG [com.zaxxer.hikari.pool.ProxyConnection] - <HikariPool-2 - Executed rollback on connection org.postgresql.jdbc.PgConnection@cb7f7de due to dirty commit state on close().>

If I manually add a commit to the SQL then it does in fact persist the update in the database. I'm looking into what's causing the dirty state and rollback.


On Monday, November 25, 2019 at 5:27:41 PM UTC-8, Andy Ng wrote:

Andy Ng

unread,
Nov 26, 2019, 8:28:26 PM11/26/19
to CAS Community
Hi Bobby,

See if you can try autocommit=true, as suggeested by this here: https://groups.google.com/a/apereo.org/forum/#!topic/cas-user/Kf-dB0b_OuQ

If that would helps or not...\


Cheers!
- Andy

Bobby Esfandiari

unread,
Nov 27, 2019, 5:55:27 PM11/27/19
to CAS Community
Hi Andy,

Are you referring to the autocommit property of the connection or the database?
I'm using a postgresql database that has autocommit=true by default. 
However, while debugging CAS I noticed that getJdbcTemplate().getDataSource().getConnection().getAutoCommit() returns false
Manually setting it to true does not seem to be possible, at least not on the object that is wired up by default.

Once again, really appreciate the assist.

Thanks, 
Bobby

Ray Bon

unread,
Nov 27, 2019, 6:14:13 PM11/27/19
to cas-...@apereo.org
Bobby,


one of them is
# ${configurationKey}.autocommit=false

where configurationKey would be something like cas.authn.jdbc.blah[0] (I have not configured database connectivity).

autocommit set in CAS controls the ORM that connects to the database and is independent of database config.

Ray
-- 
Ray Bon
Programmer Analyst
Development Services, University Systems

I respectfully acknowledge that my place of work is located within the ancestral, traditional and unceded territory of the Songhees, Esquimalt and WSÁNEĆ Nations.

Bobby Esfandiari

unread,
Nov 27, 2019, 6:28:04 PM11/27/19
to CAS Community
I just came across that config property as your message came in :) 
Thanks for the explanation Ray, it makes sense now that the ORM is picking up that property when initializing everything that I saw during debugging.

That did do the trick, thanks everyone!

Andy Ng

unread,
Nov 27, 2019, 6:35:00 PM11/27/19
to CAS Community
np problem, glad it helps :) -Andy
Reply all
Reply to author
Forward
0 new messages