Set of tools I find useful to work with Spring-framework. For now it is focused on spring-security with OAuth2, but could grow.
As I write this, latest springaddons.version
is 3.0.1
but I could forget to update before releasing, so please refer to https://repo1.maven.org/maven2/com/c4-soft/springaddons/spring-addons/ to pick latest available release of one of the following:
<dependencies>
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-security-oauth2-addons</artifactId>
<version>${springaddons.version}</version>
</dependency>
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-security-oauth2-test-addons</artifactId>
<version>${springaddons.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-security-oauth2-test-webflux-addons</artifactId>
<version>${springaddons.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-security-oauth2-test-webmvc-addons</artifactId>
<version>${springaddons.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
Currently limited to a portable OpenID Authentication implementation: OidcIdAuthenticationToken
, which is intended to be used with any OpenID authorization server: Keycoak, Auth0, MS Identity Server, ...
Code common to webmvc and webflux test libs. This includes annotations allowing to tests not only @Controller
but also any other kind of @Component
(such as @Service
):
@WithMockKeycloakAuth
the most probable reason for you landing on this page@WithMockOidcId
achieves about the same thing as@WithMockKeycloakAuth
, building a mockedOidcIdAuthenticationToken
(instead ofKeycloakAuthenticationToken
)@WithMockAuthentication
to help (a bit, not as handy as the two others) inject a mockedAuthentication
of any type in test context
You don't need any other dependency to use @WithMockKeycloakAuth
in your tests
Builds on top of spring-security-oauth2-test-addons
, adding:
- "fluent" API for
WebTestClient
- some tooling around
WebTestClient
: configurable default media-type and charset, requests shortcuts
Builds on top of spring-security-oauth2-test-addons
, adding:
- "fluent" API for
MockMvc
- some tooling around
MockMvc
: configurable default media-type and charset, requests shortcuts
I put quite a few spring-boot app samples in spring-security-oauth2-test-webmvc-addons
and spring-security-oauth2-test-webflux-addons
.
The reason why samples are in test sources (under src/test
folders) is to keep jar small. It can, of course, be run / debug from within your favorite IDE.
I recommand you clone my repo and debug the samples with a REST client like Postman, so that you can hack the config and tests.
Adapting the samples to your Keycloak instance should be just a matter of editing application.properties
.
Caveat do not narrow your exploration to keycloak
sample just beacause you are using a Keycloak authorization-server:
I run all samples against a Keycloak instance.
Last, *RetrievingAuthoritiesFromDatabase
samples retrieve authorities from a DB instead of extracting it from JWT claims. The key in the DB is the user "subject".
In that case, Keycloak authorisation-server is responsible for ensuring user ID only, authorities are the responsibility of the resource-server.
As a consequence, (to run only, not in unit-tests) those samples expect a database to be accessible and populated, which I can't do for you
as I can't know the "subject" claims for your test users registered in your Keycloak instance.
1.8 or higher. It has been 11 for long, but I'm payed for a project constrained to 1.8, so...
If using Keycloak with version >= 9.0.2
and < 11.0.0
, you need to add following bean to your conf because of a regression:
@Configuration
public class SpringBootKeycloakConfigResolver implements KeycloakConfigResolver {
private KeycloakDeployment keycloakDeployment;
private AdapterConfig adapterConfig;
@Autowired
public SpringBootKeycloakConfigResolver(AdapterConfig adapterConfig) {
this.adapterConfig = adapterConfig;
}
@Override
public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
if (keycloakDeployment != null) {
return keycloakDeployment;
}
keycloakDeployment = KeycloakDeploymentBuilder.build(adapterConfig);
return keycloakDeployment;
}
}
From 11.0.0 on, just @Import(KeycloakSpringBootConfigResolver.class)
with @KeycloakConfiguration
on your KeycloakWebSecurityConfigurerAdapter
implementation.
2.0 comes with a noticeable amount of breaking changes. So lets start tracking features.
- in OAuth2 related test annotations all claims are now groupped under a single
claims = @OpenIdClaims(...)
@WithMockJwtAuth
in addition to@WithMockKeycloakAuth
and@WithMockOidcAuth
- some code cleanup quite a bunch of code removed and some renaming, including breaking changes, reson for new major version
- import spring-boot 2.5.5 BOM (instead of inheriting 2.5.4 POM)
- Downgrade Java compatibility to 1.8
- spring-boot 2.5.4
- replace
KeycloakOidcIdAuthenticationConverter
withSynchronizedJwt2OidcIdAuthenticationConverter
and complement it withReactiveJwt2OidcIdAuthenticationConverter
- remove references to Keycloak from
spring-security-oauth2-addons
(implementations where mostly useless)
- bump Keycloak BOM to 14.0.0
- bump spring-boot to 2.5
- introduce
@JsonObjectClaim
and@JsonArrayClaim
to configure complex private claims. Sample:@WithMockKeycloakAuth(otherClaims = @ClaimSet(jsonObjectClaims = @JsonObjectClaim(name = "foo", value = "{\"bar\":\"bad\", \"nested\":{\"deep\":\"her\"}, \"arr\":[1,2,3]}")))
or@WithMockOidcId(privateClaims = @JsonObjectClaim(name = "foo", value = "{\"bar\":\"bad\", \"nested\":{\"deep\":\"her\"}, \"arr\":[1,2,3]}"))
- issue #14 added jti and nbf (from JWT spec) to @IdTokenClaims (an ID token is a JWT)
- issue #14 added session_state to @IdTokenClaims as per https://openid.net/specs/openid-connect-session-1_0.html#CreatingUpdatingSessions
- issue #14 rename
privateClaims
tootherClaims
in@WithMockKeycloakAuth
- issue #15
GrantedAuthoritiesMapper
is now optional in test config. Defaulted toNullAuthoritiesMapper
- rename
ServletKeycloakAuthUnitTestingSupport::keycloakAuthenticationToken()
toauthentication()
to improve API fluidity (api.with(keycloak.authentication()).get(...)
)
- implementation closer to open ID specs: split claims into
@IdTokenClaims
and@OidcStandardClaims
- re-use OIDC ID annotations into
@WithMockKeycloakAuth
OidcId::getName()
returnssubject
claim instead ofpreferred_username
- replace
name
withsubject
in@WithMockOidcId
- replace
name
from@WithMockKeycloakAuth
withpreferedUsername
in@WithAccessToken
- support for private claims in
@WithMockOidcId
and@WithMockKeycloakAuth
(claims with values of typeint
,long
,String
andString[]
only) - add missing subject claim in Keycloak access and ID tokens
- compose
@WithAccessToken
with@WithKeycloakIDToken
instead of repeting properties (AccessToken
extendsIDToken
) - add advanced
@WithMockKeycloakAuth
sample usage inspring-security-oauth2-test-addons
README
- fix Keycloak typo (was wrongly spelled Keycloack at many places)
- add samples with authrities retieved from a DB instead of the JWT for both OidcIdAuthenticationToken and JwtAuthenticationToken
- add sample involving
keycloak-spring-boot-starter
andkeycloak-spring-security-adapter
These release is still focused on unit-testing Spring OAuth2 applications
@WithMockAuthentication
annotation along withmockAuthentication()
servlet (webmvc) and reactive (webflux) flow APIs. You choose theAuthentication
type, the framework feeds the security context with a Mockito mock. This is dead simple but should cover 99% of test cases. I wonder why I didn't think of it sooner...- Focus solely on adding to Spring
Authentication
implementations and tests tooling (no more alternatives, with an exception forOidcId
which overlaps Spring'sOidcIdToken
) - Split
webmvc
(servlets) andwebflux
(reactive) code in distinct libs to ease dependency management - Re-shuffle packages and jars (less code, less jars, more expressive package names)
- WIP: Extensives samples and tests. Samples are boot apps under
src/test
to keep jars small - Use Keycloak as authorisation-server for all resource-server samples, each of which configuring a specific
Authentication
impl
Note that I chose Keycloak because it's a feature reach, easy to setup authorisation-server.
It should not be much of an effort to migrate sample resource-servers to another one, with an exception of those using KeycloakAuthenticationToken
as authentication impl, of course.
Cheat-sheets for me when setting up a new development environment
gpg --list-keys
# if key absent, then generate one with
gpg --gen-key
# publish public key to one of supported servers
gpg --keyserver hkp://pgp.mit.edu --send-keys (replace with "pub" key)
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<servers>
<server>
<!-- OSSRH Jira account -->
<id>ossrh</id>
<username>ch4mpy</username>
<password>${env.OSSRH_PWD}</password><!-- password retrieved from environment variable -->
</server>
</servers>
<profiles>
<profile>
<id>ossrh</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<gpg.executable>gpg</gpg.executable>
<gpg.passphrase>${env.GPG_PWD}</gpg.passphrase><!-- password retrieved from environment variable -->
</properties>
</profile>
</profiles>
</settings>