Skip to content

About

OAuth2 / OIDC for Spring backends of SPAs and mobile apps: the Spring Security beans the official starters leave to you (authorities, CORS, 2xx/401 for fetch, CSRF cookie, validated redirects, logout, one refresh flow at a time), as properties.

Topics

Resources

Contributing

Stars

673 stars

Watchers

11 watching

Forks

Latest commit

 

History

1,973 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

spring-addons

Spring Security's OAuth2 defaults were written for server-rendered applications. These Spring Boot starters give a Spring backend for single-page and mobile applications the OAuth2 / OpenID Connect behaviours it actually needs, as properties, and access-control tests that run the real authorities mapping.

Maven Central CI Spring Boot License Documentation

Works with Keycloak, Auth0, Amazon Cognito, Microsoft Entra ID and any other OpenID Provider, with several of them at a time if needed. Servlet (WebMVC) and reactive (WebFlux) applications are both supported. These libs are a complement to the official spring-boot-starter-oauth2-resource-server and spring-boot-starter-oauth2-client, not a replacement: they add the beans those starters leave to the application.

Why not just the official starters

Fewer lines of configuration is the visible part, not the reason. A backend consumed by JavaScript or by a mobile app hits Spring Security defaults which were designed for a browser doing navigations, and none of them fails at startup:

What is observed Why With spring-addons-starter-oidc
hasRole('ADMIN') never matches, even for an admin Authorities come from the scope claim only. Keycloak, Auth0 and Cognito put roles elsewhere authorities[].path, a JSON path in the claims
Every call fails with a CORS error on the preflight CORS configured on the MVC side runs after the security filters, which reject an OPTIONS without credentials cors[] on the security chain
A fetch starting the login flow dies with an opaque CORS error 302 to the authorization server, which the browser follows cross-origin inside the fetch 2xx with the Location header, the JavaScript navigates itself
An API call without a session gets a login page instead of a 401 The entry point redirects, as it should for a navigation authentication-entry-point: UNAUTHORIZED
Every POST from the frontend is 403, so CSRF gets disabled The CSRF token lives in the session, JavaScript cannot read it csrf: cookie-accessible-from-js
The frontend chooses where to land after login, and the BFF follows blindly That destination is application code, and unvalidated it is an open redirect Validated against post-login-allowed-uri-patterns
Users are logged out at random under load One refresh_token flow per parallel request, and rotated tokens invalidate the others. Declined upstream One token request per session at a time
Tests pass with authorities production never grants spring-security-test builds the Authentication itself and skips the application's converter @WithJwt runs the real converter, on any @Component

The complete list, with what each item is (a security flaw, a broken behaviour, or a test that lies) and the honest boundary of the claim, is on what goes wrong without it. The starter keeps Spring's defaults unless a property says otherwise, and the bff sample sets all of them.

Quickstart

A REST API accepting access tokens issued by Keycloak, with roles mapped to Spring Security authorities, CORS, and public routes. There is no security Java configuration to write.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
    <groupId>com.c4-soft.springaddons</groupId>
    <artifactId>spring-addons-starter-oidc</artifactId>
    <version>${springaddons.version}</version>
</dependency>
<dependency>
    <groupId>com.c4-soft.springaddons</groupId>
    <artifactId>spring-addons-starter-oidc-test</artifactId>
    <version>${springaddons.version}</version>
    <scope>test</scope>
</dependency>

Set the springaddons.version property to the version displayed by the Maven Central badge above.

com:
  c4-soft:
    springaddons:
      oidc:
        # Trusted OpenID Providers. The "iss" claim of an access token selects the entry,
        # and with it the username and authorities mapping. Add as many as needed.
        ops:
          - iss: http://localhost:7080/auth/realms/spring-addons
            username-claim: preferred_username
            authorities:
              # JSON paths in the token payload: Keycloak realm roles and client roles here
              - path: $.realm_access.roles
              - path: $.resource_access.*.roles
        resourceserver:
          # Anything else requires a valid access token and is answered with a 401, not a
          # redirection to a login page
          permit-all:
            - /greetings/public
        cors:
          - path: /**
            allowed-origin-patterns: https://localhost:4200

Access control then lives where it belongs, next to the code it protects:

@GetMapping("/greetings/me")
@PreAuthorize("hasAuthority('NICE')")
GreetingDto getGreeting(Authentication auth) { ... }

And the tests run that very same authorities mapping, without decoding a token and without an authorization server:

@Test
@WithJwt("brice.json") // a JSON claim-set in test resources
void givenUserIsBrice_whenGetMe_thenOk() throws Exception {
    api.get("/greetings/me").andExpect(status().isOk());
}

@WithJwt builds the security context by running that claim-set through the application's own authentication converter, so the username, the authorities and the Authentication implementation are the ones the application would build at runtime. This is what spring-security-test request post-processors and mutators cannot do: they skip the converter and build a stub Authentication themselves.

From there, the five runnable samples are the fastest way in. Start with resource-server, it is the smallest.

Compatibility

Each line of spring-addons targets a line of Spring Boot and is built against it. Fixes land on master (Boot 4) first and are ported to the 8.5 and 8.4 branches (Boot 3.5 and 3.4) when they are not specific to Boot 4.

spring-addons Spring Boot Java Notes
9.x 4.1.x (4.0.x up to 9.1.5) 17+ Current line. 9.4.0 is built against Boot 4.1.1 and Spring Security 7.1.1, the samples use Spring Cloud 2025.1.3.
8.5.x 3.5.x 17+ Maintenance line on the 8.5 branch: the fixes and improvements of 9.4.x which are not specific to Boot 4, ported to Boot 3.5. 8.5.0 is built against Boot 3.5.16, September 2026.
8.4.x 3.4.x 17+ Same content as 8.5.x on the 8.4 branch. 8.4.0 is built against Boot 3.4.13, September 2026.
7.x 3.3.x 17+ Last release 7.8.12, November 2024.

The exact version of the current line is the one in the Maven Central badge above. Changes are documented per version in the release notes, and breaking changes in the migration guides (7.0.0, 8.0.0, 8.4.0 / 8.5.0 when coming from 8.1.x, 9.2.0). The number of guides overstates the churn: 8.4.0, 8.5.0 and 9.2.0 concern spring-addons-starter-rest only, and the last breaking change in spring-addons-starter-oidc is the OAuthentication class in 8.0.0, which touched applications using that class and none of the properties.

Modules

Module What it is for
spring-addons-starter-oidc Resource server and oauth2Login client security auto-configuration, driven by properties
spring-addons-starter-rest RestClient / WebClient / @HttpExchange beans auto-configured from properties: authorization, proxy, SSL, timeouts
spring-addons-oauth2-test Annotations populating the test security context with OAuth2 authentications, on any kind of @Component
spring-addons-starter-oidc-test Test companion for applications using spring-addons-starter-oidc
spring-addons-starter-openapi Makes the enum values in a springdoc-openapi spec match what the application really accepts and emits
spring-addons-starter-recaptcha Server-side validation of Google reCAPTCHA v2 and v3

Each module is usable on its own. The complete reference is on the documentation site, one page per concern.

What would be hard to write yourself

Cutting configuration code is the visible part. The reasons to actually depend on these starters are the cases where the framework leaves us alone:

  • Several heterogeneous OpenID Providers at once, static or resolved dynamically, each with its own username claim and its own authorities mapping. One more entry in ops, no code.
  • One refresh_token flow at a time per session. Most authorization servers rotate refresh tokens. When a user-agent fires parallel requests with an expired access token in session, Spring Security runs one flow per request, only one succeeds, and the others are answered with a 401. This was declined upstream as something the framework cannot solve generally. spring-addons-starter-oidc decorates the authorized client provider so that the authorization server sees a single token request.
  • A complete BFF. Authorization code with PKCE, RP-Initiated Logout including for the providers which do not strictly implement it (Auth0, Cognito), Back-Channel Logout, a CSRF cookie readable by JavaScript, and 2xx statuses instead of 3xx so that a single-page application can follow the redirections itself.
  • REST clients that survive real networks. HTTP proxies from HTTP_PROXY and NO_PROXY including proxy credentials on HTTPS tunnels, SSL bundles, self-signed certificates, timeouts, and switching the underlying HTTP client library, all from properties.
  • Tests that exercise the real mapping, as shown in the quickstart above, on @Service and @Repository too, not only on controllers.

We keep complete control over what is auto-configured. Almost every auto-configured component is @ConditionalOnMissingBean, so spring-addons backs off as soon as the application defines its own bean, and overriding a default means defining that one bean, not a whole Security(Web)FilterChain. The auto-configured filter chains have the lowest precedence, so an application can add its own chains with stricter security matchers. The risks, in both directions page is worth two minutes before adopting, and what you would write without spring-addons names the hand-written Spring Security equivalent of every auto-configured bean.

When not to use it: a server-rendered application with oauth2Login and no JavaScript caller is well served by Spring Security's defaults, and a team which wants to own every security bean should write them and read the two pages above as a checklist.

spring-addons-starter-oidc

Auto-configuration for resource servers:

  • accepting tokens issued by several trusted authorization servers
  • mapping authorities from a variety of claims, including nested ones, with custom prefix and case
  • CORS configuration
  • allowing anonymous preflight requests using the path-matchers in CORS configuration

Auto-configuration for clients with oauth2Login:

  • customizing the responses returned to the frontend during the authorization code and RP-Initiated Logout flows. The URI in the Location header can be defined in properties and overridden by the frontend with headers or query parameters, and the HTTP status can be moved to the 2xx range so that the frontend observes the response and handles the redirection in JavaScript instead of letting the browser follow with a cross-origin Ajax request.
  • exposing the CSRF token as a cookie accessible to a single-page application
  • logging out from authorization servers which do not strictly implement RP-Initiated Logout, Auth0 and Amazon Cognito for instance
  • activating and configuring Back-Channel Logout
  • adding extra parameters to authorization and token requests, like the audience required by Auth0 or Keycloak's kc_idp_hint
  • CORS configuration, and anonymous preflight requests

The reference pages for resource servers and clients document every property, with an FAQ covering Auth0 audiences, Keycloak dynamic realms, Microsoft Entra ID caveats, proxies in front of the token endpoint, and cookies.

spring-addons-starter-rest

At an age where OpenAPI specs are generated from REST API source code, and client code generated from those specs, what is left to do for inter-service calls is configuring the clients: Basic or Bearer authorization, HTTP proxies, timeouts, SSL bundles, and the choice of the underlying HTTP client library. All of it is verbose and error-prone in Java, and all of it is a few properties here:

com:
  c4-soft:
    springaddons:
      rest:
        client:
          # Exposes a RestClient bean named machinClient (or a WebClient in a reactive app)
          machin-client:
            base-url: ${machin-api}
            authorization:
              oauth2:
                # Authorize outgoing requests with the Bearer token in the security context
                forward-bearer: true
          bidule-client:
            base-url: ${bidule-api}
            # Expose the builder instead of an already built client, to fine tune it
            expose-builder: true
            authorization:
              oauth2:
                # Authorize outgoing requests with a token obtained from a client registration
                oauth2-registration-id: bidule-registration

These beans can be auto-wired in any @Component or used to back @HttpExchange proxies. An HTTP Service group can be backed by an auto-configured client, so that the generated proxies share its base URL, headers, authorization, and request factory:

@Configuration
@ImportHttpServices(group = "keycloak-group", types = {UsersApi.class, ClientRoleMappingsApi.class})
class HttpServicesConfiguration {
}
com:
  c4-soft:
    springaddons:
      rest:
        client:
          keycloak-client:
            base-url: https://${cn}:3643/auth
            ssl-bundle: self-signed
            headers:
              Accept:
                - application/json
                - application/problem+json
            authorization:
              oauth2:
                oauth2-registration-id: rest-api
        group:
          keycloak-group:
            client: keycloak-client

Proxy configuration is applied by default as soon as the HTTP_PROXY and NO_PROXY environment variables are set, and can be overridden or disabled with properties. This starter is usable on its own, including in non-web applications. See the reference for the bean naming rules and the advanced options.

Testing access control

Testing access control requires a security context populated with a fine-tuned Authentication. spring-security-test provides MockMvc request post-processors and WebTestClient mutators, but they work only in the context of a request, which limits them to controllers. To test any kind of @Component, including @Service and @Repository, the options are to build the security context by hand, or to let annotations do it.

The notable difference with spring-security-test is the one mentioned in the quickstart: the authentication converter defined in the security configuration is ignored by the post-processors and mutators. Building the security context in a resource server with a JWT decoder has three steps:

  1. the Bearer string is decoded, validated, and turned into a org.springframework.security.oauth2.jwt.Jwt by a JwtDecoder
  2. that Jwt is turned into an AbstractAuthenticationToken by an authentication converter, which is where claims become authorities and where a custom Authentication implementation is chosen
  3. the Authentication is put in the security context

@WithJwt mocks step 1 only: it builds a stub Jwt from a JSON payload in test resources and hands it to the authentication converter. The .jwt() post-processor jumps to step 3 and builds the Authentication itself, so the conversion logic is never exercised. Note that this requires the authentication converter to be exposed as a @Bean rather than inlined as a lambda in the filter chain definition, which spring-addons-starter-oidc does out of the box.

Reference: the annotations, the test slices, and this Baeldung article.

Documentation and tutorials

A few weeks of trial and error can save fifteen minutes of reading a page. The documentation site is the complete reference, the samples are runnable, and three tutorials from this repo now live on Baeldung:

Also useful: the runnable samples, the release notes, llms.txt when a coding assistant is involved, contributing, and the Maven Central reminders for maintainers.

About

OAuth2 / OIDC for Spring backends of SPAs and mobile apps: the Spring Security beans the official starters leave to you (authorities, CORS, 2xx/401 for fetch, CSRF cookie, validated redirects, logout, one refresh flow at a time), as properties.

Topics

Resources

Contributing

Stars

673 stars

Watchers

11 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages