Possible Bug: Ticket Cleaner in CAS Skips Expired Tickets When Using Firestore Registry

55 views
Skip to first unread message

steven gladwell

unread,
Jul 30, 2025, 3:49:45 AMJul 30
to CAS Community

Hey all,

I've been troubleshooting an issue where expired tickets aren't getting cleaned up when using the GoogleCloudFirestoreTicketRegistry. After digging through the code, I think I found the reason why — and it seems like it might be a bug or at least an oversight.

In DefaultTicketRegistryCleaner, the cleaner does this:

java
CopyEdit
try (val expiredTickets = ticketRegistry.stream() .filter(Objects::nonNull) .filter(Ticket::isExpired)) { expiredTickets.forEach(ticket -> { LOGGER.debug("Cleaning up expired ticket [{}]", ticket.getId()); ticketRegistry.deleteSingleTicket(ticket); }); }

So it’s expecting to stream all tickets, then filter the expired ones.

But in the Firestore implementation, the getTickets() method already filters out expired tickets before returning them:

java
CopyEdit
.filter(ticket -> !ticket.isExpired())

That means ticketRegistry.stream() never includes expired tickets in the first place — so the cleaner never sees anything to delete. No logs, no cleanup, nothing.

Is that filtering supposed to be there? It seems like the registry should return everything and let the cleaner decide what to do with them. Otherwise, expired tickets just sit in Firestore forever.

Anyone else run into this? Am I missing something?

Petr Bodnár

unread,
Oct 26, 2025, 2:10:05 PMOct 26
to CAS Community, steven gladwell
Hi there,

I've checked the related code and its history - and it does seem like a bug since v7.0.0-RC5. All those ".filter(ticket -> !ticket.isExpired())" were basically added by https://github.com/apereo/cas/commit/cfe7adf78 to all the different *Registry classes and their methods and then partially removed by https://github.com/apereo/cas/commit/53271ce15. Thereafter, GoogleCloudFirestoreTicketRegistry was created, maybe as a clone of MongoDbTicketRegistry, which has got this extra line in getTickets() still present. A simple logic says these new lines should belong just to the getSessions*() methods, but as always, I might have overlooked something...

Best regards
Petr
Reply all
Reply to author
Forward
0 new messages