If you find some of the libs in this repo useful, please put a star. With enough popularity on the repo, I could publish articles on media like Baeldung, reach more users and maybe have some of the code integrated to spring-framework (which would reduce my maintenance workload on this repo).
Do not hesitate to fork this repo and send pull requests, even for things as small as a typo in READMEs or Javadoc. This would promote you as contributor.
Start with tutorials.
You might also clone this repo and hack samples. That will help you figure out what dependencies and configuration are necessary for your specific use-case.
git clone https://github.com/ch4mpy/spring-addons.git
cd spring-addons
mvn test
# Open in your IDE and run JUnit tests under samples directory
Unit tests run without any authorization server, but you'll need one to start spring boot apps. Keycloak is perfect match.
You'll most probably have to edit application.properties
files to set authorization-server URI and authorities claims according to its configuration.
With either spring-addons-webmvc-jwt-resource-server
or spring-addons-webflux-jwt-resource-server
, you can configure webmvc or webflux REST APIs secured with JWTs with 0 java config, 1 dependency and just a few properties:
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<!-- spring-addons-webflux-jwt-resource-server` instead for reactive apps -->
<artifactId>spring-addons-webmvc-jwt-resource-server</artifactId>
<version>5.1.0</version>
</dependency>
com.c4-soft.springaddons.security.issuers[0].location=https://localhost:9443/auth/realms/master
com.c4-soft.springaddons.security.issuers[0].authorities.claims=realm_access.roles,resource_access.spring-addons.roles
com.c4-soft.springaddons.security.cors[0].path=/greet/**
com.c4-soft.springaddons.security.permit-all=/actuator/health/readiness,/actuator/health/liveness,/v3/api-docs/**
In a spring-boot app, this will configure:
- OIDC authorization-server
issuer
URI (can configure just one or as as many as you need for multi-tenant scenarios) - convenient granted-authorities mapping: source claims of course (not just
scope
), but also prefix and case processing - fine grained CORS configuration (per path)
- 401 (unauthorized) instead of 302 (redirect to login) when authentication is missing or invalid on protected end-point
- enabled anonymous
- list of "permit-all" routes accesssible to anonymous
- default security to
isAuthenticated()
(all routes which are not listed in "permit-all") - "stateless" session management
- enabled CSRF (with
CookieCsrfTokenRepository
if session management is state-less)
Bootyful, isn't it? You can find it in action in simple and advanced tutorials, webflux and webmvc samples, and also in complete apps like this one
This provides with probably better defaults than spring-boot-starter-oauth2-resource-server
and none of deprecated Keycloaks libs limitations:
- no adherence to Keycloak: works with any OIDC authorization-server
- works with webflux (to my knowledge, Keycloak spring-boot adapter is limited to servlet apps)
- spring-boot 2.7+ complient (does not use
WebSecurityConfigurerAdapter
for instance)
spring-addons-oauth2-test
provides with annotations to help you configure OpenID related Authentication
instances in test security context. Are provided out of the box:
@OpenId
builds and injectsOAuthentication<OpenidClaimSet>
for apps using this repo alternative starters for OAuth2 resource-servers@WithMockJwtAuth
builds and injectsJwtAuthenticationToken
for apps using spring JWT decoder@WithMockBearerTokenAuthentication
builds and injectsBearerTokenAuthentication
for apps using spring token introspection@WithMockAuthentication
builds and injects a Mockito mock of random type (you'll probably have to configure it further in your tests) Sample for@OpenId
(but others are very similar):
@WebMvcTest(GreetingController.class)
@AutoConfigureSecurityAddons
@Import(WebSecurityConfig.class)
class GreetingControllerTest {
@Autowired
MockMvcSupport mockMvc;
@Test
@OpenId(
authorities = { "NICE_GUY", "AUTHOR" },
claims = @OpenIdClaims(preferredUsername = "Tonton Pirate")
void whenNiceGuyThenCanBeGreeted() throws Exception {
mockMvc
.get("/greet")
.andExpect(status().isOk())
.andExpect(content().string("Hi Tonton Pirate!"));
}
}
spring-addons-keycloak
provides with @WithMockKeycloakAuth
which builds and injects KeycloakAuthenticationToken
.
Be aware of above mentioned deprecation and limitations of Keycloak libs and KeycloakAuthenticationToken
before rushing to it.
JDK 17 is highly recommended: spring version released late 2022 will require it as minimum.
I have branches (far behind master) for JDKs 1.8 and 11. You might explore maven central to find which version are available for this JDKs. I recommend you use JDK 1.8 or 11 releases for @WithMockKeycloakAuth
only.
As I write this, latest version is 5.1.0
but I could forget to update before releasing, so please refer to maven central to pick latest available release of one of the following:
<properties>
<springaddons.version>5.1.0</springaddons.version>
</properties>
<dependencies>
<!-- alternatively, you can pick *-jdk11 or *-jdk1.8 artifacts if can't use JDK 17 -->
<!-- if here for @WithMockKeycloakAuth -->
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-addons-keycloak</artifactId>
<version>${springaddons.version}</version>
<scope>test</scope>
</dependency>
<!-- otherwise, one of the following pairs: -->
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-addons-webflux-jwt-resource-server</artifactId>
<version>${springaddons.version}</version>
</dependency>
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-addons-webflux-test</artifactId>
<version>${springaddons.version}</version>
<scope>test</scope>
</dependency>
<!-- or -->
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-addons-webmvc-jwt-resource-server</artifactId>
<version>${springaddons.version}</version>
</dependency>
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-addons-webmvc-test</artifactId>
<version>${springaddons.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
Provides samples for different security scenari, with configuration and unit tests for
- servlet or reactive apps
- spring's
JwtAuthenticationToken
, Keycloak'sKeycloakAuthenticationToken
, this repoOAuthentication<OpenidClaimSet>
- granted authorities retrieved from the token or from an external source (JPA repo in the sample but could be a web-service)
- usage of test annotations or "fluent API" (MockMvc request post-processors and WebTestClient mutators)
Declined for webmvc: spring-addons-webmvc-jwt-resource-server and webflux: spring-addons-webflux-jwt-resource-server
Thin layer on top of spring-security resource-server libs which brings:
- spring-boot
@AutoConfiguration
for RESTful APIs. Defaults (which can be configured fromconfiguration.propeties
) are:- CORS enabled to all routes (a REST API is not intended to serve UI)
- CSRF enabled (with
CookieCsrfTokenRepository
if session-management is state-less) - anonymous enabled (but authentication required to all routes that is not included in
permit-all
property) - authorities mapped from
realm_access.roles
with no transformation (no prefix, no case alteration) - stateless sessions
- 401 (instead of redirection to login) if user is not authenticated (or authentication is invalid)
OpenidClaimSet
: basically aMap<String, Object>
with accessors to OpenID standard claims- an OAuth Authentication:
OAuthentication<T extends Map<String, Object>
to use with any authorization-server (Keycloak, MS identity-server, Auth0, ...)
Contains the code depending on depreacted Keycloak spring adapters.
Important note: You do not have to use KeycloakAuthenticationToken
to authenticate your request against a Keycloak instance. Spring's JwtAuthenticationToken
, this repo OAuthentication<? extends OpenidClaimSet
, or an Authentication
implementation of your own can do the job and would likely be more portable. See tutorials for alternatives.
But if @WithMockKeycloakAuth
is the only reason for you landing on this repo, then spring-addons-keycloak
is the lib you are looking for.
A set of archetypes for webflux and webmvc apps, initiated either as multi or single module maven projects. All generate projects focused on REST micro-services:
- Servlet or reactive @RestController
- OpenID security
- default conf providing with enabled CORS, enabled CSRF with
CookieCsrfTokenRepository
, stateless sessions, 401 instead of redirect to login, ... - Spring exception-handling
- spring-native for smaller / faster booted services
- bootstraped JPA and unit tests
A set of re-usable @Components
(mostly @Service
and @ConfigurationProperties
) with spring-boot @AutoConfiguration
:
- WebClient.Builder factory with proxy configuration from properties
- reCAPTCHA Uses Webclient starter to provide with Google reCAPTCHA validation Service (secret & proxy configuration from properties)
Using such libs is dead simple: just declare depedency on one of those libs and use @Components just as if were declared in your app package (define properties and auto-wire components as usual in your boot project).
2.0 comes with a noticeable amount of breaking changes. So lets start tracking features.
- Support token introspection for resource-servers.
- Rename
spring-addons-*-jwt-resource-server-test
tospring-addons-*-test
as it apply for both JWT and introspection
Rename modules to:
- have all module names start with
spring-addons
prefix, then intermediate module if any (archetypes
,samples
,starters
,webmvc
orwebflux
) and last what leaf module aims at - better reflect what it do
For instance, spring-security-oauth2-webmvc-addons
only applies to resource-servers secured with JWTs (not to opaque tokens) -> renamed to spring-addons-webmvc-jwt-resource-server
Rename com.c4-soft.springaddons.security.token-issuers
configuration properties to com.c4-soft.springaddons.security.issuers
for the same reason: only accepts JWT token issuers (and not opaque token issuers with introspection end-point for instance)
CSRF enabled by default, using CookieCsrfTokenRepository
if session management is "stateless".
add reCAPTCHA validation spring-boot starter
rename @WithMockOidcAuth
to shorter and more expressive @OpenId
: it populates test security context with an OAuth2 Àuthentication
containing an OpenID claim-set
- rename
OpenidClaimSet
toOpenidClaimSet
: more expressive as this class contains OpenID token claims only - rename
OAuthentication
toOAuthentication
: it has no more adherence to OpenID (just specific to authentication with encoded claims in a bearer string)
Slight properties rework. Now, to configure issuers and authorities mapping:
# should be set to where your authorization-server is
com.c4-soft.springaddons.security.issuers[0].location=https://localhost:9443/auth/realms/master
# should be configured with a list of private-claims this authorization-server puts user roles into
# below is default Keycloak conf for a `spring-addons` client with client roles mapper enabled
com.c4-soft.springaddons.security.issuers[0].authorities.claims=realm_access.roles,resource_access.spring-addons.roles
# use IDE auto-completion or see SpringAddonsSecurityProperties javadoc for complete configuration properties list
where caze
is one of unchanged
, upper
or lower
- gh-50: One entry per authorization-server for authorities mapping (see samples
application.properties
files for new configuration structure). - gh-51: Group archetypes, webmvc and webflux modules.
- gh-49: Samples in dedicated modules. All samples are moved from libs tests to
samples
module, with one sub-module per sample.
Cleanup and prepare for spring-boot 3:
- gh-46: split webmvc & webflux content from
spring-addons-oauth2
- gh-47: provide
SecurityFilterChain
bean instead of extendingWebSecurityConfigurerAdapter
- gh-48: make use of spring-boot
@AutoConfiguration
- Replace multiple JWT issuers JwtDecoder (from 4.1.4) with
AuthenticationManagerResolver
@Beans
- JwtDecoder for configuring multiple JWT issuers (single resource server accepting IDs from two or more authorization-servers)
- finer configuration control with
SpringAddonsSecurityProperties
- move keycloak related code to
spring-addons-keycloak
- Master branch back to single JDK: 17
- Create
jdk1.8
andjdk11
branches
- Add spring-addons-archetypes-webmvc-multimodule to boostrap native-ready Spring REST API with webmvc, JPA, OpenAPI and OpenID security.
- Add a sample with
OpenidClaimSet
specialisation (parse private claims in addition to authorities).
- Improve
OidcReactiveApiSecurityConfig
andOidcServletApiSecurityConfig
usability: ease security beans replacement (including authorities and authentication converter for use cases where OAuthentication is not enough)
- Rename
SecurityProperties
to less conflictingSpringAddonsSecurityProperties
- Turn
AbstractOidc...ApiSecurityConfig
intoOidc...ApiSecurityConfig
with default authorities mapper being keycloak or Auth0 depending oncom.c4-soft.springaddons.security.keycloak.client-id
being set or not - More CORS and authorities mapping configuration in
SecurityProperties
- Fix missing JTI claim mapping from
@OpenIdClaims
(gh-35).
- Add
AbstractOidcReactiveApiSecurityConfig
tospring-addons-oauth2
. It provides with reasonable default WebSecurityConfig for a reactive (weblux) based API secured with OAuthentication.
- Add
AbstractOidcServletApiSecurityConfig
tospring-addons-oauth2
. It provides with reasonable default WebSecurityConfig for a servlet based API secured with OAuthentication.
- lombok with provided scope (gh-31)
- spring-boot 2.6.1
- release with JDK version (compilation and runtime target)
- spring-boot 2.6
- in OAuth2 related test annotations all claims are now grouped 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, reason 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-addons-oauth2
(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-addons-oauth2-test
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 OAuthentication 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 rich, 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>
Add-opens for releasing with JDK 17:
export JDK_JAVA_OPTIONS='--add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED'