Skip to content

Support SSF Receivers with Push Delivery#43950

Closed
thomasdarimont wants to merge 30 commits intokeycloak:mainfrom
thomasdarimont:issue/gh-43614-ssf-support-v1
Closed

Support SSF Receivers with Push Delivery#43950
thomasdarimont wants to merge 30 commits intokeycloak:mainfrom
thomasdarimont:issue/gh-43614-ssf-support-v1

Conversation

@thomasdarimont
Copy link
Copy Markdown
Contributor

@thomasdarimont thomasdarimont commented Nov 4, 2025

This PR adds initial support for Shared Signals Framework with Security Event Token (SET) Push Delivery using HTTP.

Users can manage SSF Receiver components within a realm that can be connected with a SSF Transmitter.
The SSF Transmitter can send SETs (Security Event Tokens) via HTTP Push to an endpoint exposed by the Keycloak realm. A new SsfEventListener SPI can be used to react on received SSF events from a SET.

This PR assumes SSF Streams to be registered outside of Keycloak. A SSF Receiver in Keycloak is associated with a stream by configuring the StreamId, Audience and SSF Transmitter Access token in the SSF Receiver configuration.

There can be multiple logical SSF Receivers defined within a realm that can handle SET events via a SsfEventListener.

  • Add new experimental SSF feature flag
  • Add SET parsing infrastructure
  • Add support for Subject Identifier parsing
  • Add receiver management via Identity Providers
  • Add transmitter keys management
  • Add PushEndpoint for SET PUSH Delivery
  • Add support for standard SSF Stream, CAEP and RISC events.
  • Add plugable SsfEventListener infrastructure to handle SSF events
  • Add support for handling CAEP SessionRevoked event to terminate user sessions.

Fixes #43614
image

image # Demo https://github.com/user-attachments/assets/fffeb99d-0feb-4e4f-a962-4a962aeb684d

Signed-off-by: Thomas Darimont [email protected]

@thomasdarimont
Copy link
Copy Markdown
Contributor Author

thomasdarimont commented Nov 4, 2025

TODOs:

  • Add tests for SET parsing / validation
  • Add user documentation

Backlog:

  • Add "link" with Identity providers to register SSF receiver if IdP can act as SSF Transmitter
  • Allow user/admin to specify interested SSF events

@thomasdarimont thomasdarimont changed the title Initial support for Shared Signals Framework Initial support for Shared Signals Framework with Push Delivery Nov 4, 2025
@thomasdarimont
Copy link
Copy Markdown
Contributor Author

thomasdarimont commented Nov 9, 2025

Usage example with caep.dev

SSF Stream setup with caep.dev

Register with https://caep.dev to obtain an SSF transmitter access token:

  1. https://caep.dev/register
  2. Provide name / email address
  3. Provide (dummy) Org data, and specify SSF Stream audience, in the example below we use https://ssf-receiver.keycloak.org
  4. Verify email
  5. Download access token

Create a SSF Stream on caep.dev

  1. https://caep.dev/transmitter/events
  2. Enter provided access token
  3. Create stream (see create a stream request)

Create a stream request with caep.dev

Use the provided access token as transmitterToken and specify your own push endpoint URL pushEndpointUrl and push auth header via pushAuthHeader.
With this PR, the pushEndpointUrl for a SSF Receiver with the alias caepdev is: $REALM_ISSUER_URL/ssf/push/caepdev.

In the following I use the Keycloak realm ssf-poc.

POST https://ssf.caep.dev/ssf/streams
Authorization: Bearer {{transmitterToken}}
Content-Type: application/json

{
  "delivery": {
    "method": "urn:ietf:rfc:8935",
    "endpoint_url": "{{pushEndpointUrl}}",
    "authorization_header": "{{pushAuthHeader}}"
  },
  "events_requested": [
    "https://schemas.openid.net/secevent/caep/event-type/session-revoked",
    "https://schemas.openid.net/secevent/caep/event-type/credential-change",
    "https://schemas.openid.net/secevent/caep/event-type/device-compliance-change",
    "https://schemas.openid.net/secevent/caep/event-type/token-claims-change",
    "https://schemas.openid.net/secevent/caep/event-type/assurance-level-change"
  ],
  "description": "This field is optional. Remove this field if not needed."
}

This will yield a 201 Created response like this:

{
  "stream_id": "5cf99a08-7dd4-4c51-9a88-ada2a9c89feb",
  "iss": "https://ssf.caep.dev/",
  "aud": "https://ssf-receiver.keycloak.org",
  "delivery": {
    "authorization_header": "...pushAuthHeader..",
    "method": "urn:ietf:rfc:8935",
    "endpoint_url": "..pushEndpointUrl.."
  },
  "events_supported": [
    "https://schemas.openid.net/secevent/caep/event-type/session-revoked",
    "https://schemas.openid.net/secevent/caep/event-type/credential-change",
    "https://schemas.openid.net/secevent/caep/event-type/device-compliance-change",
    "https://schemas.openid.net/secevent/caep/event-type/assurance-level-change",
    "https://schemas.openid.net/secevent/caep/event-type/token-claims-change",
    "https://schemas.openid.net/secevent/ssf/event-type/verification"
  ],
  "events_requested": [
    "https://schemas.openid.net/secevent/caep/event-type/session-revoked",
    "https://schemas.openid.net/secevent/caep/event-type/credential-change",
    "https://schemas.openid.net/secevent/caep/event-type/device-compliance-change",
    "https://schemas.openid.net/secevent/caep/event-type/token-claims-change",
    "https://schemas.openid.net/secevent/caep/event-type/assurance-level-change"
  ],
  "events_delivered": [
    "https://schemas.openid.net/secevent/caep/event-type/session-revoked",
    "https://schemas.openid.net/secevent/caep/event-type/credential-change",
    "https://schemas.openid.net/secevent/caep/event-type/device-compliance-change",
    "https://schemas.openid.net/secevent/caep/event-type/token-claims-change",
    "https://schemas.openid.net/secevent/caep/event-type/assurance-level-change"
  ],
  "description": "This field is optional. Remove this field if not needed."
}

Now our stream is created and we can start sending events to it. Bit first we need to create a
SSF Receiver associated with the SSF stream we just created.

Create a SSF Receiver in Keycloak

  1. In the ssf-poc Keycloak realm goto Identity Providers
  2. Click on the new SSF Receiver item (or select it from the Providers drop down list).
  3. Use the alias caepdev and SSF Receiver for caep.dev as display name (enter dummy data for clientid/secret - will be removed later)
  4. Click add
  5. Fill out the following
    Description: Test Reciever for caep.dev SSF Transmitter
    Issuer: https://ssf.caep.dev/
    Transmitter Token: ... the token from caep.dev
    Audience: https://ssf-receiver.keycloak.org
    StreamID: 5cf99a08-7dd4-4c51-9a88-ada2a9c89feb (from the create stream response)
    Push Authorization Header: .. (from the create stream response)

Demo

  1. Create test user with dummy email address, e.g. [email protected] and a password
  2. Sign in to the account console with that user
  3. Go to https://caep.dev/transmitter/events and enter your transmitter access token
  4. Select event type Session Revoked, Subject Type Email with the user email, e.g. [email protected].
  5. Click on Send CAEP Event and wait for a Event Pushed message.
  6. Reload the account console, the user should now be signed out.

In the server log you should see log messages like this:

2025-11-09 14:30:05,400 DEBUG [org.keycloak.protocol.ssf.receiver.transmitter.DefaultSsfTransmitterClient] (executor-thread-1) Sending transmitter metadata request. realm=ssf-poc url=https://ssf.caep.dev/.well-known/ssf-configuration
2025-11-09 14:30:05,842 DEBUG [org.keycloak.protocol.ssf.receiver.transmitter.DefaultSsfTransmitterClient] (executor-thread-1) Received transmitter metadata response. realm=ssf-poc status=200
2025-11-09 14:30:05,848 DEBUG [org.keycloak.protocol.ssf.receiver.transmitter.DefaultSsfTransmitterClient] (executor-thread-1) Stored transmitter metadata in cache. realm=ssf-poc url=https://ssf.caep.dev/.well-known/ssf-configuration
2025-11-09 14:30:06,017 DEBUG [org.keycloak.protocol.ssf.endpoint.SsfPushDeliveryResource] (executor-thread-1) Ingesting valid security event token. realm=ssf-poc receiverAlias=caepdev jti=YTBhYmRjMjktZTEyZC00NmE3LWI4Y2YtOTIxYzJkOTJlMmZj
2025-11-09 14:30:06,020 DEBUG [org.keycloak.protocol.ssf.event.processor.DefaultSsfSecurityEventProcessor] (executor-thread-1) Processing SSF events for security event token. realm=ssf-poc jti=YTBhYmRjMjktZTEyZC00NmE3LWI4Y2YtOTIxYzJkOTJlMmZj streamId=5cf99a08-7dd4-4c51-9a88-ada2a9c89feb eventCount=1
2025-11-09 14:30:06,028 DEBUG [org.keycloak.protocol.ssf.event.listener.DefaultSsfEventListener] (executor-thread-1) Security event received. eventId=YTBhYmRjMjktZTEyZC00NmE3LWI4Y2YtOTIxYzJkOTJlMmZj eventType=https://schemas.openid.net/secevent/caep/event-type/session-revoked subjectId=EmailSubjectId{email='[email protected]'} eventClass=org.keycloak.protocol.ssf.event.types.caep.SessionRevoked
2025-11-09 14:30:06,050 DEBUG [org.keycloak.protocol.ssf.event.listener.DefaultSsfEventListener] (executor-thread-1) Removed 1 sessions for user. realm=ssf-poc userId=302a333e-aa2d-46c1-af5d-de22576cd036 for SessionRevoked event. reasonAdmin=null reasonUser=null

@thomasdarimont thomasdarimont force-pushed the issue/gh-43614-ssf-support-v1 branch 2 times, most recently from 098ac04 to 998edb6 Compare November 13, 2025 13:21
@thomasdarimont thomasdarimont force-pushed the issue/gh-43614-ssf-support-v1 branch 2 times, most recently from fa479a4 to 27de092 Compare November 24, 2025 11:51
@thomasdarimont thomasdarimont changed the title Initial support for Shared Signals Framework with Push Delivery Support SSF Receivers with Push Delivery Nov 27, 2025
@thomasdarimont thomasdarimont force-pushed the issue/gh-43614-ssf-support-v1 branch 5 times, most recently from 4c289f8 to fd5db12 Compare December 3, 2025 09:59
@thomasdarimont thomasdarimont force-pushed the issue/gh-43614-ssf-support-v1 branch from fd5db12 to 9be27de Compare December 18, 2025 13:42
@thomasdarimont thomasdarimont force-pushed the issue/gh-43614-ssf-support-v1 branch from 9be27de to b6069fe Compare January 25, 2026 21:04
@thomasdarimont thomasdarimont force-pushed the issue/gh-43614-ssf-support-v1 branch from 70628a7 to cfdd345 Compare February 12, 2026 20:52
@thomasdarimont
Copy link
Copy Markdown
Contributor Author

thomasdarimont commented Feb 12, 2026

Added support for triggering verifications via SSF Receiver Provider toolbar in admin UI.

Current Screenshots

New SSF Receiver
image

Configured SSF Receiver
image

Verification Tool:
image

Generated User End Session Events
image

@ahus1 ahus1 force-pushed the issue/gh-43614-ssf-support-v1 branch from eb3b188 to af62fab Compare February 14, 2026 14:31
@ahus1
Copy link
Copy Markdown
Member

ahus1 commented Feb 14, 2026

Rebased to work around problems fixed in main that made CI fail

@thomasdarimont thomasdarimont force-pushed the issue/gh-43614-ssf-support-v1 branch 2 times, most recently from 160df3a to c6c52cd Compare February 18, 2026 22:58
Copy link
Copy Markdown
Contributor

@mabartos mabartos left a comment

Choose a reason for hiding this comment

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

@thomasdarimont Looks very nice! Just briefly looked at it and spotted one missing place to have the provider enabled/disabled based on the feature.

@keycloak-github-bot
Copy link
Copy Markdown

Unreported flaky test detected

If the flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR.

org.keycloak.testsuite.forms.VerifyProfileTest#testRequiredOnlyIfUser

Keycloak CI - Forms IT (chrome)

java.lang.NullPointerException: Cannot invoke "String.equals(Object)" because the return value of "org.keycloak.testsuite.pages.PageUtils.getPageTitle(org.openqa.selenium.WebDriver)" is null
	at org.keycloak.testsuite.pages.VerifyProfilePage.isCurrent(VerifyProfilePage.java:166)
	at org.keycloak.testsuite.pages.AbstractPage.assertCurrent(AbstractPage.java:40)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
...

Report flaky test

Copy link
Copy Markdown

@keycloak-github-bot keycloak-github-bot Bot left a comment

Choose a reason for hiding this comment

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

Unreported flaky test detected, please review

@VinodAnandan
Copy link
Copy Markdown
Contributor

@thomasdarimont Thank you very much for this PR, I think it's a highly valuable feature for Keycloak, and your work is greatly appreciated. From my perspective, it may be beneficial to focus on delivering a well-defined MVP so that we can share it with the community sooner and gather early feedback. This could help us iterate more effectively based on real-world input. Do you currently have a timeline in mind for this PR? Are you considering including this experimental feature in the Keycloak 26.6 release? Please also let us know if there is anything the SIG can do to support you

@thomasdarimont thomasdarimont force-pushed the issue/gh-43614-ssf-support-v1 branch from 9f92d86 to 3f41d41 Compare March 9, 2026 13:14
@VinodAnandan
Copy link
Copy Markdown
Contributor

@thomasdarimont Apologies for the follow-up. I genuinely believe this is a valuable feature and would be a great addition to the Keycloak 26.6 release.

That said, I completely understand that you may have many other priorities and commitments, and I don’t want to add any pressure. I just wanted to mention that, in case it was unintentional, switching the PR from draft to a regular PR might help bring a bit more visibility and attract attention from other maintainers or interested parties/contributors.

@thomasdarimont thomasdarimont force-pushed the issue/gh-43614-ssf-support-v1 branch from 3f41d41 to 33a5bfa Compare March 13, 2026 13:09
- Represent SSF Receivers as Identity Providers in Admin UI
- Gradually move from SsfReceiverModel to SsfReceiverProviderConfig
- Move to external stream management model (streams are created outside of Keycloak)
- Move verification functionality to SsfReceiverProvider
- Make SsfReceiverManager obsolete

Signed-off-by: Thomas Darimont <[email protected]>
Signed-off-by: Thomas Darimont <[email protected]>
Signed-off-by: Thomas Darimont <[email protected]>
Signed-off-by: Thomas Darimont <[email protected]>
- Removed SCIM Events  (which are still in draft status https://www.ietf.org/archive/id/draft-ietf-scim-events-16.html)
- Revised JSON deserialization of SSF events (result is now SsfEvent instead of Map<String,Object>)
- Introduced StreamEvent base class for SSF Stream events
- Remove unnecessary methods

Signed-off-by: Thomas Darimont <[email protected]>
- Rename SsfSpi to SsfReceiverSpi
- Rename SsfReceiverProvider to SsfRegistrationProvider
- Rename SsfProvider to SsfReceiverProvider

This allows us to add SSF Transmitter support independently of the SSF Receiver support

Signed-off-by: Thomas Darimont <[email protected]>
- Move issuer/audience validation into DefaultSsfEventProcessor
- Simplify SsfPushDeliveryResource
- Validate SecurityEventToken type

Signed-off-by: Thomas Darimont <[email protected]>
Signed-off-by: Thomas Darimont <[email protected]>
Signed-off-by: Thomas Darimont <[email protected]>
Signed-off-by: Thomas Darimont <[email protected]>
- Use constant time check for authHeader
- Throw SecurityEventTokenParsingException in case of invalid signatures
- Add missing null checks

Signed-off-by: Thomas Darimont <[email protected]>
- Revise misstyped classname

Signed-off-by: Thomas Darimont <[email protected]>
- Add missing null check for TransmitterTokenType

Signed-off-by: Thomas Darimont <[email protected]>
- Fix inconsistent error messages
- Fix inconsistent status codes
- Fix missing transmitter token type handling

Signed-off-by: Thomas Darimont <[email protected]>
- Fix inconsistent error messages
- Fix inconsistent status codes
- Fix missing transmitter token type handling

Signed-off-by: Thomas Darimont <[email protected]>
Signed-off-by: Thomas Darimont <[email protected]>
SSF Receivers can now select between a "static token" or "client credentials" authentication method to access an SSF Transmitter.

Signed-off-by: Thomas Darimont <[email protected]>
Move realm access to SsfEventContext.

Signed-off-by: Thomas Darimont <[email protected]>
Add support for user lookups via iss/sub based on Identity provider links.

Signed-off-by: Thomas Darimont <[email protected]>
- Check if ssf profile is enabled.

Signed-off-by: Thomas Darimont <[email protected]>
@thomasdarimont thomasdarimont force-pushed the issue/gh-43614-ssf-support-v1 branch from 701a6a8 to 81ac8b4 Compare April 29, 2026 09:54
@thomasdarimont
Copy link
Copy Markdown
Contributor Author

thomasdarimont commented Apr 29, 2026

This PR is superseded by #48254

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Support for SSF Receivers with Push Delivery

4 participants