Skip to content

ch4mpy/spring-addons

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Ease OAuth2 / OpenID in Spring RESTful backends

8.0.2 is 🚀. It is designed to work with Spring Boot 3.4.1 (Security 6.4.2 and Cloud 2024.0.0). See the release notes for details.

The new spring-addons-starter-rest can be a game changer for inter-service calls when OAuth2 or an HTTP proxy is involved. Give it a try!

Minimal OAuth2 Background for Spring Backends

This repo hosts tutorials for configuring Spring RESTful backends with OAuth2 / OIDC. Carefully read the OAuth2 essentials section before jumping into implementation.

Three tutorials from this repo have been moved to Baeldung:

When using just spring-boot-starter-oauth2-client or spring-boot-starter-resource-server we almost always end up writing the Security(Web)FilterChain ourselves, which requires a solid security background, some knowledge of Spring Security internals, and can be verbose.

spring-addons-starter-oidc builds on top of "offical" starters to significantly reduce the need for security configuration code. It even brings it down to 0 in most cases.

We have complete control over what spring-addons-starter-oidc auto-configures. With application properties, of course, but also bean definitions: almost all auto-configured components are @ConditionalOnMissingBean, meaning that spring-addons backs off each time a component is explicitly defined in an application. But no need to define a complete security filter-chain, defining just the component to override should be enough.

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 responses returned to the frontend during the authorization-code and RP-Initiated Logout flows:
    • specify the URI in Location header to activate a route after login / logout (defaults can be defined in application properties and overridden by the frontend using headers or query parameters)
    • avoid some CORS issues with the authorization server: set the HTTP status in the 2xx range to observe the response and handle the redirection in Javascript code instead of letting the browser follow with an Ajax request. There is no reason for these redirections to be cross-origin requests, plain navigation is what should actually happen.
  • exposing CSRF token as a cookie accessible to a single-page application
  • logging out from an authorization server not strictly implementing RP-Initiated Logout (case of Auth0 and Amazon Cognito for instance)
  • activating and configuring Back-Channel Logout
  • adding extra parameters to authorization & token requests (like the audience required by Auth0)
  • CORS configuration
  • allowing anonymous preflight requests using the path-matchers in CORS configuration

At an age where OpenAPI specs can be generated from REST APIs source code, and the client code to consume these APIs generated from the specs, the main challenge for inter-service communication is the configuration of REST clients.

Spring promotes the usage of RestClient or WebClient, but configuring those for Basic or Bearer authentication, an HTTP proxy, and connection & read timeouts is pretty complicated, verbose, and error-prone.

spring-addons-starter-rest makes this configuration a snap.

Sample usage

com:
  c4-soft:
    springaddons:
      rest:
        client:
          # Exposes a RestClient bean named machinClient (or WebClient in a WebFlux app)
          machin-client:
            base-url: ${machin-api}
            authorization:
              oauth2:
                # Authorize outgoing requests with the Bearer token in the security context (possible only in a resource server app)
                forward-bearer: true
          # Exposes a RestClient.Builder bean named biduleClientBuilder (mind the "expose-builder: true")
          bidule-client:
            base-url: ${bidule-api}
            # Expose the builder instead of an already built client (to fine tune its conf)
            expose-builder: true
            authorization:
              oauth2:
                # Authorize outgoing requests with the Bearer token obtained using an OAuth2 client registration
                oauth2-registration-id: bidule-registration

This exposes pre-configured RestClient or WebClient beans (or their builders) that we can auto-wire in any kind of @Component - like @Controller & @Service - or use in @Configuration - for instance to generate implementations of @HttpExchange interfaces and expose them as beans.

Proxy configuration is applied by default to REST clients as soon as the HTTP_PROXY and NO_PROXY environment variables are set. This can be overridden and disabled with application properties.

Unit & Integration Testing With Security

Testing access control requires configuring the test security context with a fine-tuned Authentication instance.

For that, spring-security-test provides MockMvc request post-processors and WebTestClient mutators, but it can work only in the context of a request, which limits its usage to controllers. To test any type of @Component (@Controller, of course, but also @Service and @Repository) there are only two options:

  • build tests security context by ourself and populate it with stubbed / mocked authentications 😢
  • use annotations to do it for us (this is where spring-addons-oauth2-test jumps in) 😃

Also, a notable difference between @MockJwt and those in spring-security-test is that spring-security-test ignores the authentication converter defined in the security conf 😭. To understand the consequences, let's consider the flow to build the security context in a resource server with a JWT decoder:

  1. the JWT Bearer string is decoded, validated, and turned into a org.springframework.security.oauth2.jwt.Jwt by a JwtDecoder
  2. this Jwt (not JWT) is turned into something extending AbstractAuthenticationToken by an authentication converter. This step includes converting claims to authorities and the choice of a specific Authentication implementation.
  3. the Authentication instance is put in the security context

With @WithJwt, only the 1st step is mocked. A stub Jwt (not JWT) is built using a JSON payload in test resources and provided to the authentication converter. With spring-security-test post-processors and mutators, factories skip to step 3 and build a stub Authentication themselves, setting properties with what is provided in the test code. So, authorities conversion logic is used only with @WithJwt. Similarly, a custom Authentication implementation will be used in tests only if the authentication converter is called by the factory, and as so, with @WithJwt, but not with .jwt() post-processor.

Useful resources:

Useful links