Shared Signals Framework receivers for Java
easyssf lets your application react when your identity provider revokes a session, changes a credential or signals a risk. It verifies the security events of the OpenID Shared Signals Framework, rejects the access tokens and ends the sessions concerned, and hands everything else to your code.
How it works
A transmitter, usually your identity provider, delivers Security Event Tokens (SETs) to your application, either by pushing them to an endpoint or by letting the application poll for them. easyssf verifies each token, skips duplicates and routes the events to the integrations that act on them.
Push or pollRFC 8935 · RFC 8936
A push endpoint on a route of your choice, secured by its own stateless filter chain, or a poller that fetches and acknowledges SETs from the transmitter.
Every SET verifiedRFC 8417
Signature against the transmitter's JWK Set, discovered from its .well-known/ssf-configuration,
plus typ, iss, aud, jti, iat and events.
The transmitter and every endpoint it publishes must use HTTPS.
Resource server integration
A CAEP session-revoked event makes Spring Security reject the access tokens of that session,
or all tokens of the user issued before the event.
OIDC client integration
session-revoked and credential-change events invalidate the matching local
sessions, by session id, subject or email.
Stream management
Let the application create or update its stream at the transmitter on startup, verify it, and use the
whole stream management API through SsfStreamClient. Several transmitters, several Keycloak
realms say, are each configured by name; the issuer of a SET selects the one that verifies it.
Production details covered
State in your database when there is one, Micrometer metrics, a health indicator for Actuator, retries with backoff, and the application starts even while the transmitter is down.
Getting started
Three steps for a Spring Boot application with the servlet stack.
Add the starter
<dependency>
<groupId>org.easyssf</groupId>
<artifactId>easyssf-receiver-spring-boot-starter</artifactId>
<version>0.1.0-SNAPSHOT</version>
</dependency>
No release yet: the snapshots of main are on the
Maven Central snapshot repository,
which your build has to enable.
Name the transmitter
easyssf:
receiver:
transmitter-issuer: https://idp.example/realms/demo
expected-audience: https://my-app.example
push:
expected-auth-header: Bearer ${SSF_PUSH_SECRET}
Create the stream
Create a stream with push delivery at the transmitter that points to
https://my-app.example/ssf/push and sends the configured header, or set
easyssf.receiver.stream.management: receiver and let the application create it.
With spring-boot-starter-security-oauth2-resource-server or
spring-boot-starter-security-oauth2-client on the classpath, revoked sessions are handled
from then on without further code.
React to any event
@Component
class StepUpHandler implements SsfEventHandler {
@Override
public void handle(SsfEventContext eventContext) {
if (eventContext.hasEvent("CaepAssuranceLevelChange")) {
SsfSubject subject = eventContext.subjectFor("CaepAssuranceLevelChange");
Map<String, Object> event = eventContext.eventFor("CaepAssuranceLevelChange");
// subject.subject(), subject.sessionId(), subject.email() ...
}
}
}
Handlers run before the SET is acknowledged. If one throws, the transmitter is expected to deliver the SET again.
Subjects as the spec defines them
SsfSubject is the sub_id of the SET: a subject identifier in any RFC 9493 format
(iss_sub, email, opaque, account, phone_number,
did, uri, aliases) or a complex subject with its user,
session, device, tenant and other members. Shortcuts cover the common
cases, nothing is lost.
Your own event type aliases
The SSF, CAEP and RISC event types have built-in aliases such as CaepSessionRevoked. Register
aliases for vendor specific event types, in configuration or in code, and use them wherever an event type is
named. The URIs stay canonical; an alias can never redefine another.
Test your receiver
easyssf-test brings a transmitter that runs inside your test JVM: it signs SETs, serves metadata
and keys, hands out tokens and emulates the stream and poll endpoints. Push a revocation, assert the effect.
./mvnw install and see the README for the
complete configuration reference.Modules
Everything is published under the group id org.easyssf.
| Module | What it is | Depends on |
|---|---|---|
easyssf-core | The data structures of SSF shared by receivers and, later, transmitters: SETs, subjects, event types, stream configuration, transmitter metadata. | nothing |
easyssf-receiver | The receiver, independent of any framework: SET verification, de-duplication, event handlers, push handling, polling, stream management, token revocation and session termination logic. | easyssf-core, Nimbus JOSE + JWT, SLF4J |
easyssf-receiver-jdbc | The database-backed stores of the receiver, processed SETs and revocations, independent of any framework: the SQL, the schema and a small execution interface implemented over a DataSource or a framework's template. | easyssf-receiver |
easyssf-receiver-spring-boot-starter | The receiver for Spring Boot 4.1 with Spring Security 7.1 on the servlet stack: configuration properties, auto-configuration, push endpoint, resource server and OIDC client integration. | easyssf-receiver, Spring Boot |
easyssf-test | Test support: a transmitter on a loopback port that signs and delivers SETs, serves metadata and keys and emulates the stream and poll endpoints, for the tests of your receiver. | easyssf-core, Nimbus JOSE + JWT |
easyssf-receiver-spring-boot-examples | An example resource server and OIDC client with a Keycloak setup. | |
easyssf-test-conformance | Runs the OpenID conformance suite's SSF receiver test plans against a receiver under test, in any framework: the suite via Testcontainers, the scenarios the receiver plays, and the plan tests to extend. | easyssf-receiver, Testcontainers, JUnit |
easyssf-receiver-spring-boot-conformance-tests | The Spring Boot receiver under test and the four plan tests for it. |
Without Spring Boot
easyssf-receiver has no framework dependencies. Assemble the parts you need
and call them from the endpoint or scheduler of your framework. The Spring Boot starter is one such integration.
SsfHttpClient httpClient = new JdkSsfHttpClient();
String issuer = "https://idp.example/realms/demo";
SsfTransmitterMetadataResolver metadata = new SsfTransmitterMetadataResolver(issuer, null, httpClient);
NimbusSsfSetVerifier verifier = new NimbusSsfSetVerifier(issuer,
() -> metadata.resolve().jwksUri().toString(), httpClient);
verifier.setExpectedAudience("https://my-app.example");
SsfEventHandler handler = (eventContext) -> {
if (eventContext.hasEvent("CaepSessionRevoked")) {
SsfSubject subject = eventContext.subjectFor("CaepSessionRevoked");
// end the session subject.sessionId() of the user subject.subject()
}
};
SsfSetProcessor processor = new SsfSetProcessor(verifier, new InMemorySsfJtiDedupStore(10_000), List.of(handler));
// PUSH: call this from the endpoint the transmitter posts SETs to
SsfPushHandler pushHandler = new SsfPushHandler(processor, "Bearer " + pushSecret);
SsfPushResponse response = pushHandler.handle(authorizationHeader, requestBody);
// POLL: fetch SETs from the transmitter instead
SsfPoller poller = new SsfPoller(httpClient, tokenProvider, () -> pollEndpoint, processor);
poller.start();
Interoperability
Keycloak
Tested with the SSF transmitter of Keycloak 26.8 (--features=ssf). The examples ship a
pre-configured realm, and the README documents Keycloak's audience, scopes and single-stream rule.
OpenID conformance suite
The receiver test plans of the OpenID conformance suite, default and CAEP interop profile with push and poll delivery, run against the receiver in an automated test module.
Standards
Built on the OpenID Shared Signals specifications and the IETF security event RFCs, see Specifications for the complete list and what easyssf takes from each.
Not included yet: WebFlux applications and long polling.
Specifications
What easyssf implements, and where each piece is defined.
| Specification | Defines | In easyssf |
|---|---|---|
| OpenID Shared Signals Framework 1.0 | Transmitters, receivers, streams, transmitter metadata discovery, stream verification | Metadata discovery, stream management and verification, the SSF event types |
| OpenID CAEP 1.0 | Continuous Access Evaluation Profile: session-revoked, credential-change, assurance-level-change and the other session and credential events |
Event type aliases, the resource server and OIDC client integrations |
| OpenID CAEP Interoperability Profile 1.0 | The minimum a CAEP transmitter and receiver must support to work together | The CAEP interop plans of the conformance tests |
| OpenID RISC Profile 1.0 | Risk Incident Sharing and Coordination: account disabled, purged, credential compromise and the other account events | Event type aliases, for your own handlers |
| RFC 8417 | Security Event Token (SET): the JWT profile every event is delivered in | Verification of signature, typ, iss, aud, jti, iat and events |
| RFC 8935 | Push-based SET delivery over HTTP | The push endpoint and its responses |
| RFC 8936 | Poll-based SET delivery over HTTP | The poller, acknowledgements and setErrs |
| RFC 9493 | Subject identifiers for SETs: account, email, iss_sub, opaque, phone_number, did, uri, aliases |
SsfSubjectIdentifier, every format, and SsfSubject for complex subjects; the matching of events to sessions and users |
| RFC 7515 / RFC 7517 | JSON Web Signature and JSON Web Key | Signature checks against the transmitter's JWK Set, with Nimbus JOSE + JWT |