Skip to content

Add a client-activity workflow event provider - #53082

Open
antoniocasagrande-airia wants to merge 2 commits into
keycloak:mainfrom
antoniocasagrande-airia:workflow-client-activity-event
Open

antoniocasagrande-airia wants to merge 2 commits into
keycloak:mainfrom
antoniocasagrande-airia:workflow-client-activity-event

Conversation

@antoniocasagrande-airia

Copy link
Copy Markdown

Part of #51595.

Workflows can already react to a client authenticating, but that event is EventType.CLIENT_LOGIN, which only the client_credentials grant emits. A public authorization-code client — what dynamic client registration produces — never raises it. So a CLIENTS workflow meaning "disable this client if it has been idle for N days" silently degrades to "N days after it was created" for exactly the population most likely to accumulate.

This adds client-activity over LOGIN, CODE_TO_TOKEN and REFRESH_TOKEN, so any use of the client resets the timer.

The *_ERROR variants are deliberately excluded. They carry the client id too, so counting them would let failed authentication against a dormant client keep it alive indefinitely — a property worth stating explicitly, since it is the kind of thing that looks like an oversight later.

The first commit is a separate defect

JpaWorkflowStateProvider schedules steps with Instant.now() while the due-check queries against Time.currentTimeMillis():

entity.setScheduledStepTimestamp(Instant.now().plus(duration).toEpochMilli());   // real clock
...
cb.lessThan(stateRoot.get("scheduledStepTimestamp"), Time.currentTimeMillis());  // offsettable clock

Under any time offset the due-check moves and the schedule does not, so steps fire at the wrong time. This is independent of the event provider and affects existing workflows; it is first here because the activity test cannot express "the timer resets on use" without both sides agreeing on a clock. Happy to split it into its own PR if you would rather review it separately.

It also changes BrokeredUserLifecycleWorkflowTest's offsets from 5/10/10 to 5/15/25. That test was passing for the wrong reason: with scheduling on the real clock the offsets were not actually cumulative. They are now, and the comment there says so.

Testing

New integration test covering activation on use and the reset behaviour, plus a case pinning that failed logins do not activate.

Verified locally: model/jpa compiles, tests/base test-compiles, and mvn spotless:check -pl model/jpa is clean. I have not run the integration test against a server locally — it is written to the same shape as the sibling activation tests.

Context: this was originally part of #51629, which I have closed since the client-attribute condition it also carried is superseded by #52189. #52189 does not include an activity event, so this half is still open and is here on its own.

… the same clock

JpaWorkflowStateProvider.scheduleStep stamped scheduledStepTimestamp with
Instant.now() while getDueScheduledSteps compares it against
Time.currentTimeMillis(). Production behavior is unchanged (Time delegates
to the system clock), but under a test time offset steps were scheduled
against the wrong clock, making restart scenarios untestable with
TimeOffSet. Steps scheduled during an offset run are now scheduled
relative to the simulated clock, so the cumulative offsets in
BrokeredUserLifecycleWorkflowTest are adjusted accordingly.

Signed-off-by: Antonio Casagrande <antoniocasagrande@airia.com>
Workflows can already react to a client authenticating, but that event is
EventType.CLIENT_LOGIN, which only the client_credentials grant emits. A public
authorization-code client - what dynamic client registration produces - never
raises it, so a workflow that means "disable this client if it has been idle for
N days" silently degrades to "N days after it was created" for exactly the
population most likely to accumulate.

Add client-activity over LOGIN, CODE_TO_TOKEN and REFRESH_TOKEN, so any use of
the client resets the timer. The *_ERROR variants are deliberately excluded:
they carry the client id too, so counting them would let failed authentication
against a dormant client keep it alive indefinitely.

Signed-off-by: Antonio Casagrande <antoniocasagrande@airia.com>
@antoniocasagrande-airia
antoniocasagrande-airia requested a review from a team as a code owner September 22, 2026 17:04
Copilot AI balanced review requested due to automatic review settings September 22, 2026 17:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The public event is undocumented, and two supported activity event paths lack integration coverage.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 Medium severity · 1 Low severity

Open (2)
What changed in this PR

Adds client activity tracking for workflows and aligns workflow scheduling with Keycloak’s offset-aware clock.

Changes:

  • Adds client-activity for successful login, code exchange, and token refresh events.
  • Adds activation/reset and failed-login integration coverage.
  • Fixes cumulative scheduling under time offsets.
File Description
BrokeredUserLifecycleWorkflowTest.java Updates cumulative time offsets.
ClientActivityWorkflowTest.java Tests client activity workflow behavior.
WorkflowEventProviderFactory Registers the new provider.
JpaWorkflowStateProvider.java Uses the offset-aware clock.
ClientActivityWorkflowEventProvider.java Defines qualifying activity events.
ClientActivityWorkflowEventFactory.java Exposes the client-activity event.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +44 to +45
@Test
public void testActivateWorkflowOnClientActivity() {

public class ClientActivityWorkflowEventFactory implements WorkflowEventProviderFactory<WorkflowEventProvider> {

public static final String ID = "client-activity";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants