The project provides a test artifact with the classifier generators containing utility classes for testing JWT validation.
See Test Artifact Dependencies in the main module documentation.
-
de.cuioss.sheriff.token.validation.test- Core test utilities -
de.cuioss.sheriff.token.validation.test.dispatcher- MockWebServer dispatchers: discovery/JWKS (WellKnownDispatcher,JwksResolveDispatcher,EnhancedJwksResolveDispatcher,MultiIssuerJwksDispatcher) and the OP-endpoint dispatchers (TokenDispatcher,UserInfoDispatcher,RevocationDispatcher,IntrospectionDispatcher,EndSessionDispatcher,ParDispatcher), plus the sharedAdversarialResponsespayloads -
de.cuioss.sheriff.token.validation.test.generator- Token and claim generators -
de.cuioss.sheriff.token.validation.test.junit- JUnit 5 extensions and annotations (@TestTokenSource)
Comprehensive TokenContent implementation for testing with dynamic token generation.
public static final String TEST_AUDIENCE = "test-audience";
public static final String TEST_CLIENT_ID = "test-client-app";
public static final String TEST_ISSUER = "Token-Test-testIssuer";These constants model the semantic distinctions required by RFC 7519, RFC 9068, and OpenID Connect Core.
TestTokenHolder tokenHolder = new TestTokenHolder(TokenType.ACCESS_TOKEN,
ClaimControlParameter.defaultForTokenType(TokenType.ACCESS_TOKEN));
String token = tokenHolder.getRawToken();
tokenHolder.withClaim("custom-claim", ClaimValue.forPlainString("custom-value"));
tokenHolder.withoutClaim("sub");
IssuerConfig issuerConfig = tokenHolder.getIssuerConfig();-
Dynamic Token Generation: Creates JWT tokens on demand with configurable claims
-
Claim Manipulation: Add, remove, or modify claims
-
Token Type Support: ACCESS_TOKEN, ID_TOKEN, REFRESH_TOKEN
-
IssuerConfig Generation: Creates matching IssuerConfig for the token
-
Key Material Integration: Uses InMemoryKeyMaterialHandler for signing and verification
Controls which claims are included or excluded when generating tokens:
ClaimControlParameter params = ClaimControlParameter.builder()
.missingIssuer(true)
.missingSubject(true)
.build();
TestTokenHolder tokenHolder = new TestTokenHolder(TokenType.ACCESS_TOKEN, params);
ClaimControlParameter defaultParams = ClaimControlParameter.defaultForTokenType(TokenType.ID_TOKEN);Factory methods for creating TypedGenerator instances producing TestTokenHolder objects, built on the cui-test-generator framework:
TypedGenerator<TestTokenHolder> accessTokenGenerator = TestTokenGenerators.accessTokens();
TestTokenHolder accessToken = accessTokenGenerator.next();
TypedGenerator<TestTokenHolder> idTokenGenerator = TestTokenGenerators.idTokens();
TypedGenerator<TestTokenHolder> refreshTokenGenerator = TestTokenGenerators.refreshTokens();The @TestTokenSource annotation is the preferred way to inject test tokens into parameterized tests:
@ParameterizedTest
@TestTokenSource(value = TokenType.ACCESS_TOKEN, count = 5)
@DisplayName("Test with access token")
void shouldTestWithAccessToken(TestTokenHolder tokenHolder) {
String token = tokenHolder.getRawToken();
AccessTokenContent result = TokenValidator.builder()
.issuerConfig(tokenHolder.getIssuerConfig())
.build()
.createAccessToken(AccessTokenRequest.of(token));
assertNotNull(result);
assertEquals(TestTokenHolder.TEST_ISSUER, result.getIssuer());
}@Test
void shouldValidateToken() {
TestTokenHolder tokenHolder = TestTokenGenerators.accessTokens().next();
String token = tokenHolder.getRawToken();
AccessTokenContent result = TokenValidator.builder()
.issuerConfig(tokenHolder.getIssuerConfig())
.build()
.createAccessToken(AccessTokenRequest.of(token));
assertNotNull(result);
assertEquals(tokenHolder.getClaims().get("sub").getOriginalString(), result.getSubject().orElseThrow());
}@Test
void shouldRejectTokenWithMissingClaims() {
ClaimControlParameter params = ClaimControlParameter.builder()
.missingIssuer(true)
.build();
TestTokenHolder tokenHolder = new TestTokenHolder(TokenType.ACCESS_TOKEN, params);
IssuerConfig issuerConfig = IssuerConfig.builder()
.issuerIdentifier(TestTokenHolder.TEST_ISSUER)
.expectedAudience(TestTokenHolder.TEST_AUDIENCE)
.expectedClientId(TestTokenHolder.TEST_CLIENT_ID)
.jwksContent(InMemoryJWKSFactory.createDefaultJwks())
.build();
TokenValidator validator = TokenValidator.builder()
.issuerConfig(issuerConfig)
.build();
TokenValidationException exception = assertThrows(TokenValidationException.class,
() -> validator.createAccessToken(AccessTokenRequest.of(tokenHolder.getRawToken())));
assertEquals(SecurityEventCounter.EventType.MISSING_CLAIM, exception.getEventType());
}The WellKnownDispatcher utility simulates an OpenID Provider’s /.well-known/openid-configuration and JWKS endpoints for testing without network calls. It implements ModuleDispatcherElement from cui-test-mockwebserver-junit5.
@EnableMockWebServer
class MyServiceUsingWellKnownTest {
@Getter
private final WellKnownDispatcher moduleDispatcher = new WellKnownDispatcher();
@BeforeEach
void setUp() {
moduleDispatcher.returnDefault();
moduleDispatcher.setCallCounter(0);
}
@Test
void testSuccessfulOidcDiscovery(URIBuilder uriBuilder) throws Exception {
moduleDispatcher.returnDefault();
String baseUrl = uriBuilder.buildAsString();
String wellKnownUrl = uriBuilder.addPathSegment(".well-known")
.addPathSegment("openid-configuration").buildAsString();
WellKnownConfig wellKnownConfig = WellKnownConfig.builder()
.wellKnownUrl(wellKnownUrl)
.retryConfig(RetryConfig.builder().maxAttempts(1).build())
.build();
HttpWellKnownResolver resolver = wellKnownConfig.createResolver();
assertEquals(baseUrl, resolver.getIssuer().orElseThrow());
moduleDispatcher.assertCallsAnswered(1);
}
@Test
void testOidcDiscoveryError(URIBuilder uriBuilder) throws Exception {
moduleDispatcher.returnError();
String wellKnownUrl = uriBuilder.addPathSegment(".well-known")
.addPathSegment("openid-configuration").buildAsString();
WellKnownConfig wellKnownConfig = WellKnownConfig.builder()
.wellKnownUrl(wellKnownUrl)
.retryConfig(RetryConfig.builder().maxAttempts(1).build())
.build();
HttpWellKnownResolver resolver = wellKnownConfig.createResolver();
// getIssuer() returns empty Optional when discovery fails
assertTrue(resolver.getIssuer().isEmpty());
assertEquals(LoaderStatus.ERROR, resolver.getLoaderStatus());
}
}-
Configuration:
returnDefault(),returnError(),returnInvalidJson(),returnMissingIssuer(),returnMissingJwksUri(),returnInvalidIssuer(),returnOnlyRequiredFields() -
Verification:
assertCallsAnswered(int),getCallCounter(),setCallCounter(int)
The dispatcher dynamically generates URLs based on incoming requests, ensuring issuer and jwks_uri values match the server’s base URL.
The framework provides automatic server management, dynamic port assignment, URIBuilder parameter injection, and HTTPS support. See the cui-test-mockwebserver-junit5 repository for details.