Reputation: 783
Spring Boot version: 3.2.2
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
What exactly is the difference between these two dependencies? I don't fully understand which one I should use in which situation. In my project, I perform Token transactions with Keycloak. Even though I used AuthenticationManagerResolver when my previous Spring Boot version was 2.7.4, I needed to define the following parameters. When I upgrade to version 3.2.2, I can verify tokens using Keycloak, even though I do not use these parameters:
spring.security.oauth2.client.registration.keycloak.provider=keycloak
spring.security.oauth2.client.registration.keycloak.client-id=payment-client
spring.security.oauth2.client.registration.keycloak.client-secret=apK0d8j5mmn32Do9Es
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.keycloak.scope=openid,read,write
spring.security.oauth2.client.registration.keycloak.redirect-uri=http://keycloak.payment/login/oauth2/code/keycloak
spring.security.oauth2.client.provider.keycloak.authorization-uri=http://keycloak.payment/realms/payment-realm/protocol/openid-connect/auth
spring.security.oauth2.client.provider.keycloak.token-uri=http://keycloak.payment/realms/payment-realm/protocol/openid-connect/token
spring.security.oauth2.client.provider.keycloak.user-info-uri=http://keycloak.payment/realms/payment-realm/protocol/openid-connect/userinfo
spring.security.oauth2.client.provider.keycloak.jwk-set-uri=http://keycloak.payment/realms/payment-realm/protocol/openid-connect/certs
spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username
spring.security.oauth2.client.provider.keycloak.user-info-authentication-method=header
My currently Security class
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final AuthenticationEntryPoint tokenAuthenticationEntryPoint;
private JwtAuthenticationManagerResolver authenticationManagerResolver;
@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.cors(AbstractHttpConfigurer::disable);
http.oauth2ResourceServer(oauth2ResourceServer -> oauth2ResourceServer.authenticationManagerResolver(authenticationManagerResolver).authenticationEntryPoint(tokenAuthenticationEntryPoint));
http.authorizeHttpRequests((authorize) -> authorize.requestMatchers(
"/swagger-ui/**").permitAll().anyRequest().authenticated())
.sessionManagement(manager -> manager.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}
}
My currently AuthenticationManagerResolve class
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationManagerResolver;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.oauth2.jwt.JwtDecoders;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
import org.springframework.stereotype.Component;
import java.util.Collections;
@Component
@RequiredArgsConstructor
public class JwtAuthenticationManagerResolver implements AuthenticationManagerResolver<HttpServletRequest> {
@Override
public AuthenticationManager resolve(HttpServletRequest request) {
JwtAuthenticationProvider authenticationProvider = new JwtAuthenticationProvider(JwtDecoders.fromIssuerLocation("http://keycloak.payment/payment-realm"));
return new ProviderManager(Collections.singletonList(authenticationProvider));
}
}
Even though I never specified the parameters I mentioned above, security is now verifying tokens properly. In what situation should I use these parameters? Currently I'm just using the "spring-boot-starter-oauth2-resource-server" dependency.
When is spring-boot-starter-oauth2-client dependency and above parameters needed.
Note: I use Keycloak only for token transactions. I use my own project for role and authority processing.
Upvotes: 0
Views: 1987
Reputation: 12835
As any Boot starter, both provide with some auto-configuration (use a few application properties to save a lot of Java Configuration code). And as per their names, spring-boot-starter-oauth2-client
helps with OAuth2 client auto-configuration and spring-boot-starter-oauth2-resource-server
OAuth2 resource server ones.
OAuth2 is a 4 actors thing:
Bearer
access token. It validates this tokens (either using a public JWT signing key or by introspecting the access token on the authorization server) and takes access control decisions. It does care who issued the token (can it trust the issuer?), to what (which client?), on behalf of who (the resource owner) and what for (scope and audience). But it really doesn't care how this token was acquired (which OAuth2 flow).openid
scope is granted)offline_access
scope is granted)spring-boot-starter-oauth2-resource-server
(and the spring.security.oauth2.resourceserver.*
properties)
In apps exposing a REST API to OAuth2 clients, meaning to something capable of acquiring an access token and sending it as Bearer
authorization header.
Such clients can be:
RestClient
, WebClient
, RestTemplate
, @FeignClient
, ... (the last two being in "maintenance" mode should probably be avoided)oauth2Login
and the TokenRelay
filterNote that a browser can't be an OAuth2 client without specific tooling (like a dedicated lib running in a Javascript application or a BFF), and won't be able to get protected resources from a resource server.
spring-boot-starter-oauth2-client
(and the spring.security.oauth2.client.*
properties)
When needing to call an OAuth2 resource server using one of the programmatic REST clients listed above or something like the Spring Cloud Gateway TokenRelay
filter.
As a consequence, it is frequent to have client & resource server configuration in apps part of a micro-service system: when a resource server delegates some of the processing to an other one, it is a client for this other service.
A much less frequent case is a Spring app with publicly exposing a REST API to random OAuth2 clients and also a server-side rendered GUI (Thymeleaf or whatever) with oauth2Login
and MVC controller calling the API with a programmatic REST client.
You can use it just to use oauth2Login
, even if you don't use tokens afterwards to call a resource server, but then you should maybe question your architecture: why acquiring tokens if you don't use it?
Requests from browsers to Spring backends configured with oauth2Login
are authorized with session cookies (not with Bearer access tokens), which requires protection against CSRF attacks. It is usually expected that such a backend answers with 302 redirect to login
when a user tries to access a protected URL but does not have an authorized session.
Requests from OAuth2 clients to oauth2ResourceServer
are authorized with Bearer tokens. Sessions are usually disabled, which makes it insensible to CSRF attacks. Last, a resource servers can't reasonably answer something else than 401 Unauthorized
when a token is invalid or issued by an untrusted authorization server, or when the token misses in requests to an endpoint that isn't permitAll
. Resource server don't care how tokens are acquired and don't even know the flow the resource owner can use (authorization-code for users, client-credentials for batches, ...) or, in multi-tenant scenarios, the authorization server it should redirect to.
Because the security configuration requirements are not quite compatible (session VS stateless, CSRF protection enabled VS disabled, 302
VS 401
) oauth2Login
and oauth2ResourceServer
should not be mixed in the same Security(Web)FilterChain
. If you need the two in the same app, expose two different filter-chain beans. This is something I do frequently on OAuth2 BFFs: a filter-chain with oauth2Login
for routes configured with the TokenRelay
filter and another filter-chain with oauth2ResourceServer
for other routes and, publicly accessible assets and REST resources (Spring Boot Actuator).
I maintain an additional starter which pushes OAuth2 auto-configuration way further than official ones and makes quite a few frequent features configurable with just properties. To name a few:
Upvotes: 2