How to define a custom grant type in a Spring Security Oauth2 client? - spring-boot

I have a working api-gateway application built on spring-boot 2.2, which is an Oauth2 client supporting authorization-code grant flow. It is built using spring-boot #EnableOAuth2Sso, which will create a spring session and oauth2 context once the user is successfully logs in. Every request to the resource server will be intercepted by this api-gateway, and validates the user session also the oauth2 token from the session context. All the resource servers are Oauth2 protected.
I need to support SAML2 login also through this gateway. I have setup WSO2 as Identity provider, which provides the SAML response as a POST request to an endpoint in the api-gateway once a user is successfully logged in through an IDP initiated login flow. Right now the WSO2 IDP is able to provide me an Oauth2 token when I submit a token request with SAML assertion and SAML grant type. What I need is when a SAML POST request comes from the WSO2 IDP to the api-gateway, it should create an Oauth2 token from WSO2 using SAML assertion and SAML grant type, also should create a Spring session and Oauth2 context with this received Oauth2 token.
I have two possible options at this moment,
1) Define a custom spring security Oauth2 grant-type in the api-gateway Oauth2 client, and handle the SAML response to generate Oauth2 token using spring-security, also the Spring session and Oauth2 context.
2) Manually write code to generate Oauth2 token using SAML response, also manually create a new Spring session and Oauth2 context, which will be an ugly approach.
Given below the current security configuration class.
#Configuration
#EnableOAuth2Sso
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
private static final String ROOT_PATH = "/";
private static final String SUFFIX = "**";
private static final String ANY_OTHER = "/webjars";
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher(ROOT_PATH + SUFFIX)
.authorizeRequests()
.antMatchers(LOGIN_PATH.value() + SUFFIX, ERROR_PATH.value() + SUFFIX, ANY_OTHER + SUFFIX, "/saml**").permitAll()
.antMatchers(HttpMethod.POST, "/saml**").permitAll()
.anyRequest()
.authenticated()
.and().csrf().ignoringAntMatchers("/saml**")
.and().logout().logoutSuccessUrl(LOGOUT_SUCCESS_PATH.value()).permitAll();
}
#Bean
public OAuth2RestOperations restTemplate(OAuth2ClientContext clientContext, OAuth2ProtectedResourceDetails resourceDetails) {
return new OAuth2RestTemplate(resourceDetails, clientContext);
}
#SuppressWarnings({ "rawtypes", "unchecked" })
#Bean
public FilterRegistrationBean oauth2ClientFilterRedirectRegistration(OAuth2ClientContextFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(filter);
registration.setOrder(-100);
return registration;
}
}
The Yml configuration is given below
security:
basic:
enabled: false
oauth2:
client:
clientId: xxxx
clientSecret: xxxxx
accessTokenUri: http://localhost:9763/oauth2/token
userAuthorizationUri: http://localhost:9763/oauth2/authorize
scope: openid,email,name
resource:
userInfoUri: http://localhost:9763/oauth2/userinfo
jwk:
key-set-uri: http://localhost:9763/oauth2/jwks
Can someone suggest how we can define a custom spring security Oauth2 grant-type in a Oauth2 client? Is there any documentation available to configure the same? Does the spring-security support this requirement?
Also is there any other solutions to handle this scenario? Any suggestions please.

Related

Any Reference with: Spring Boot 3.0.x, spring security 6.0.x with keycloak integration Token based Authentication and Authorization [duplicate]

I updated to Spring Boot 3 in a project that uses the Keycloak Spring Adapter. Unfortunately, it doesn't start because the KeycloakWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter which was first deprecated in Spring Security and then removed. Is there currently another way to implement security with Keycloak? Or to put it in other words: How can I use Spring Boot 3 in combination with the Keycloak adapter?
I searched the Internet, but couldn't find any other version of the adapter.
You can't use Keycloak adapters with spring-boot 3 for the reason you found, plus a few others related to transitive dependencies. As most Keycloak adapters were deprecated in early 2022, it is very likely that no update will be published to fix that.
Directly use spring-security OAuth2 instead. Don't panic, it's an easy task with spring-boot.
spring-addons starters for resource server (app exposes a REST API)
I maintain 4 thin wrappers around "official" boot resource-server starter because, in my opinion, auto-configuration can be pushed one step further to:
make OAuth2 configuration more portable: with a configurable authorities converter, switching from an OIDC provider to another is just a matter of editing properties (Keycloak, Auth0, Cognito, Azure AD, etc.)
ease app deployment on different environments: CORS configuration is controlled from properties file
reduce drastically the amount of Java code (things get even more complicated if you are in multi-tenancy scenario)
reduce chances of misconfiguration (easy to de-synchronise CSRF protection and sessions configuration for instance)
It is very thin (each is composed of three files only) and greatly simplifies resource-servers configuration:
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<!-- replace "webmvc" with "weblux" if your app is reactive -->
<!-- replace "jwt" with "introspecting" to use token introspection instead of JWT decoding -->
<artifactId>spring-addons-webmvc-jwt-resource-server</artifactId>
<!-- this version is to be used with spring-boot 3.0.1, use 5.4.x for spring-boot 2.6.x or before -->
<version>6.0.13</version>
</dependency>
#Configuration
#EnableMethodSecurity
public static class WebSecurityConfig { }
com.c4-soft.springaddons.security.issuers[0].location=https://localhost:8443/realms/realm1
com.c4-soft.springaddons.security.issuers[0].authorities.claims=realm_access.roles,ressource_access.some-client.roles,ressource_access.other-client.roles
com.c4-soft.springaddons.security.cors[0].path=/some-api
com.c4-soft.springaddons.security.permit=all=/actuator/health/readiness,/actuator/health/liveness,/v3/api-docs/**
Nothing more is needed to configure a multi-tenant resource-server with fine tuned CORS policy and authorities mapping. Bootiful, isn't it?
By "multi-tenant", I mean that, as you can guess from this issuers property being an array, you can trust as many OIDC authorization-server instances as you need (multiple Keycloak realms & instances, or even mix with other OIDC providers like Auth0, Cognito, etc.), each with it's own authorities mapping configuration.
Client configuration (UI with oauth2Login())
If your Spring application exposes secured UI elements you want to be accessible with a browser (with OAuth2 login), you'll have to provide a FilterChain with "client" configuration.
If this app exposes both a REST API and a UI to manipulate it (with oauth2Login()), then you'll have to setup two security filter-chains: one with client config and the other with resource-server config.
Add this to pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
Here we demo a SecurityFilterChain applying only to a list of routes defined with a securityMatcher.
This assumes that an additional resource-server SecurityFilterChain is defined, with lower order and no securityMatcher so that all routes are intercepted after all filter chains are evaluated in order. This other filter chain could be defined either implicitly (by spring-addons as described above) or explicitly (with Spring Boot official starter as described below).
Remove the securityMatcher section if your app is solely a client:
// Give higher precedence to security filter-chains with "securityMatcher"
#Order(Ordered.HIGHEST_PRECEDENCE)
#Bean
SecurityFilterChain uiFilterChain(
HttpSecurity http,
ServerProperties serverProperties,
GrantedAuthoritiesMapper authoritiesMapper) throws Exception {
http.securityMatcher(new OrRequestMatcher(
// add path to your UI elements instead
new AntPathRequestMatcher("/ui/**"),
// those two are required to access Spring generated login page
// and OAuth2 client callback endpoints
new AntPathRequestMatcher("/login/**"),
new AntPathRequestMatcher("/oauth2/**")));
http.oauth2Login().userInfoEndpoint().userAuthoritiesMapper(authoritiesMapper);
http.authorizeHttpRequests()
.requestMatchers("/ui/index.html").permitAll()
.requestMatchers("/login/**").permitAll()
.requestMatchers("/oauth2/**").permitAll()
.anyRequest().authenticated();
// If SSL enabled, disable http (https only)
if (serverProperties.getSsl() != null && serverProperties.getSsl().isEnabled()) {
http.requiresChannel().anyRequest().requiresSecure();
}
// Many defaults are kept compared to API filter-chain:
// - sessions (and CSRF protection) are enabled
// - unauthorized requests to secured resources will be redirected to login (302 to login is Spring's default response when authorisation is missing or invalid)
return http.build();
}
#Bean
GrantedAuthoritiesMapper userAuthoritiesMapper(Converter<Map<String, Object>, Collection<? extends GrantedAuthority>> authoritiesConverter) {
return (authorities) -> {
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
authorities.forEach(authority -> {
if (authority instanceof OidcUserAuthority oidcAuth) {
mappedAuthorities.addAll(authoritiesConverter.convert(oidcAuth.getIdToken().getClaims()));
} else if (authority instanceof OAuth2UserAuthority oauth2Auth) {
mappedAuthorities.addAll(authoritiesConverter.convert(oauth2Auth.getAttributes()));
}
});
return mappedAuthorities;
};
}
The code above assumes that a Converter<Map<String, Object>, Collection<? extends GrantedAuthority>> bean is exposed. One is auto-configured by spring-addons starters for resource-server and the "official" starter section below defines one. Take the later as sample if your app is solely a client.
Last, client properties:
spring.security.oauth2.client.provider.keycloak.issuer-uri=https://localhost:8443/realms/master
spring.security.oauth2.client.registration.spring-addons-public.provider=keycloak
spring.security.oauth2.client.registration.spring-addons-public.client-name=spring-addons-public
spring.security.oauth2.client.registration.spring-addons-public.client-id=spring-addons-public
spring.security.oauth2.client.registration.spring-addons-public.scope=openid,offline_access,profile
spring.security.oauth2.client.registration.spring-addons-public.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.spring-addons-public.redirect-uri=http://bravo-ch4mp:8080/login/oauth2/code/spring-addons-public
"Official" Spring Boot resource-server starter
As spring-addons-{webmvc|webflux}-{jwt|introspecting}-resource-server are thin wrappers around spring-boot-starter-oauth2-resource-server, you can of course do the same with just the later.
Here is what it takes to configure a resource-server with a unique Keycloak realm as authorization-server:
#Configuration
#EnableWebSecurity
#EnableMethodSecurity
public class WebSecurityConfig {
public interface Jwt2AuthoritiesConverter extends Converter<Jwt, Collection<? extends GrantedAuthority>> {
}
#SuppressWarnings("unchecked")
#Bean
public Jwt2AuthoritiesConverter authoritiesConverter() {
// This is a converter for roles as embedded in the JWT by a Keycloak server
// Roles are taken from both realm_access.roles & resource_access.{client}.roles
return jwt -> {
final var realmAccess = (Map<String, Object>) jwt.getClaims().getOrDefault("realm_access", Map.of());
final var realmRoles = (Collection<String>) realmAccess.getOrDefault("roles", List.of());
final var resourceAccess = (Map<String, Object>) jwt.getClaims().getOrDefault("resource_access", Map.of());
// We assume here you have "spring-addons-confidential" and "spring-addons-public" clients configured with "client roles" mapper in Keycloak
final var confidentialClientAccess = (Map<String, Object>) resourceAccess.getOrDefault("spring-addons-confidential", Map.of());
final var confidentialClientRoles = (Collection<String>) confidentialClientAccess.getOrDefault("roles", List.of());
final var publicClientAccess = (Map<String, Object>) resourceAccess.getOrDefault("spring-addons-public", Map.of());
final var publicClientRoles = (Collection<String>) publicClientAccess.getOrDefault("roles", List.of());
// Merge the 3 sources of roles and map it to spring-security authorities
return Stream.concat(
realmRoles.stream(),
Stream.concat(confidentialClientRoles.stream(), publicClientRoles.stream()))
.map(SimpleGrantedAuthority::new).toList();
};
}
// spring-boot looks for a Converter<Jwt, ? extends AbstractAuthenticationToken> bean
// that is a converter from Jwt to something extending AbstractAuthenticationToken (and not AbstractAuthenticationToken itself)
// In this conf, we use JwtAuthenticationToken as AbstractAuthenticationToken implementation
public interface Jwt2AuthenticationConverter extends Converter<Jwt, JwtAuthenticationToken> {
}
#Bean
public Jwt2AuthenticationConverter authenticationConverter(Jwt2AuthoritiesConverter authoritiesConverter) {
return jwt -> new JwtAuthenticationToken(jwt, authoritiesConverter.convert(jwt));
}
// Give lower precedence to security filter-chains without "securityMatcher" so that the filter-chains with a "securityMatcher" get a chance to be matched
#Order(Ordered.LOWEST_PRECEDENCE)
#Bean
public SecurityFilterChain apiFilterChain(
HttpSecurity http,
ServerProperties serverProperties,
Converter<Jwt, ? extends AbstractAuthenticationToken> authenticationConverter) throws Exception {
// Enable OAuth2 with custom authorities mapping
http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(authenticationConverter);
// As the authentication bean is the one expected by spring-boot,
// an alternative would be to use just
// http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
// Enable anonymous
http.anonymous();
// Enable and configure CORS
http.cors().configurationSource(corsConfigurationSource());
// State-less session (state in access-token only)
// with Disable CSRF because of disabled sessions
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.csrf().disable();
// Return 401 (unauthorized) instead of 302 (redirect to login) when authorization is missing or invalid
http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"Restricted Content\"");
response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
});
// If SSL enabled, disable http (https only)
if (serverProperties.getSsl() != null && serverProperties.getSsl().isEnabled()) {
http.requiresChannel().anyRequest().requiresSecure();
}
// Route security: authenticated to all routes but actuator and Swagger-UI
http.authorizeRequests()
.antMatchers("/actuator/health/readiness", "/actuator/health/liveness", "/v3/api-docs/**").permitAll()
.anyRequest().authenticated();
return http.build();
}
private CorsConfigurationSource corsConfigurationSource() {
// Very permissive CORS config...
final var configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setExposedHeaders(Arrays.asList("*"));
// Limited to API routes (neither actuator nor Swagger-UI)
final var source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/greet/**", configuration);
return source;
}
}
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://localhost:8443/realms/master
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://localhost:8443/realms/master/protocol/openid-connect/certs
As mentioned in preamble, this is quite more verbose than spring-addons starters, it's not ready for multi-tenancy and each time CORS policy changes (new API routes for instance) or when the claims source for authorities change (new OAuth2 client with client-roles mapping or other OIDC provider than Keycloak), you'll have to edit source-code and re-publish your app...
Use the standard Spring Security OAuth2 client instead of a specific Keycloak adapter and SecurityFilterChain instead of WebSecurityAdapter.
Something like this:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(jsr250Enabled = true, prePostEnabled = true)
class OAuth2SecurityConfig {
#Bean
fun customOauth2FilterChain(http: HttpSecurity): SecurityFilterChain {
log.info("Configure HttpSecurity with OAuth2")
http {
oauth2ResourceServer {
jwt { jwtAuthenticationConverter = CustomBearerJwtAuthenticationConverter() }
}
oauth2Login {}
csrf { disable() }
authorizeRequests {
// Kubernetes
authorize("/readiness", permitAll)
authorize("/liveness", permitAll)
authorize("/actuator/health/**", permitAll)
// ...
// everything else needs at least a valid login, roles are checked at method level
authorize(anyRequest, authenticated)
}
}
return http.build()
}
And then in application.yml:
spring:
security:
oauth2:
client:
provider:
abc:
issuer-uri: https://keycloak.../auth/realms/foo
registration:
abc:
client-secret: ...
provider: abc
client-id: foo
scope: [ openid, profile, email ]
resourceserver:
jwt:
issuer-uri: https://keycloak.../auth/realms/foo
Using Keycloak adapters is not possible because the KeycloakWebSecurityConfigurerAdapter inherited from the WebSecurityConfigurerAdapter class, which was deprecated in Spring Security and subsequently removed in the newer release.
I have published a detailed article on integrating Keycloak with Spring Boot 3.0 on Medium, which provides a step-by-step guide on how to integrate Keycloak with Spring Boot 3.0.
This guide is particularly helpful for those who are new to integrating Keycloak with Spring Boot 3.0 or migrating to Spring Boot 3.0 from an older version.
You can check out the article (https://medium.com/geekculture/using-keycloak-with-spring-boot-3-0-376fa9f60e0b) for a comprehensive explanation of the integration process.
Hope this helps! If you have any questions, further clarifications or suggestions, Please feel free to leave a comment.

How to secure a Spring Boot REST API

How do I secure my Spring REST API? I would like clients (On other domains/apps) to login/register to my API via google OAuth2 or simple username & password in a POST request and for my API to then return something they can use to authenticate following requests to secured endpoints.
I'm having a hard time finding solutions, I have kinda improvised a registration/login
with the following class.
My problems are 2:
I'm returning a cookie JSESSIONID which is going to a client on another domain, or a mobile app, what else could I return and how do I configure that?
Spring auto-generates login and logout pages, I don't need them since the app is just a REST API web service
#Order(2)
#Configuration
static class ResourceSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Autowired
UserService userService
#Autowired
PasswordEncoder passwordEncoder
DaoAuthenticationProvider authProvider
/**
* Initialize the daoAuthenticationProvider
*/
#PostConstruct
void init() {
authProvider = new DaoAuthenticationProvider()
authProvider.setUserDetailsService(userService)
authProvider.setPasswordEncoder(passwordEncoder)
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// Disable auto config
http.httpBasic().disable()
// Authentication Provider
http.authenticationProvider(authProvider)
http.csrf().disable()
http.authorizeRequests().antMatchers('/api/users/auth/**').permitAll()
http.authorizeRequests().antMatchers("/login").denyAll()
http.authorizeRequests().anyRequest().authenticated()
http.formLogin().loginProcessingUrl('/api/users/auth/login')
http.logout().logoutUrl('/api/users/logout').invalidateHttpSession(true)
// Session Management
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
// Cors Configuration
CorsConfiguration corsConfiguration = new CorsConfiguration()
corsConfiguration.setAllowedHeaders(List.of("Authorization", "Cache-Control", "Content-Type"))
corsConfiguration.setAllowedOrigins(List.of("*"))
corsConfiguration.setAllowedMethods(List.of("GET", "POST", 'OPTIONS'))
corsConfiguration.setExposedHeaders(List.of("Authorization"))
http.cors().configurationSource(request -> corsConfiguration)
}
}
I'm okay with users sending POST requests to /login and /register, but I need to configure something better than a JSESSIONID cookie in my response.
Is there a way to do everything with Google OAuth2? (That'd be optimal, a frontend can just login to Google with the google login react-component and then send me some kind of 'token' I can send to back to Google to verify their identity)

Spring Cloud Gateway Oauth2Login Return JWT Token Instead of SESSION Cookie Upon Successful Login

sorry in advance if the question is previously asked, but I have not been able to find an answer.
I am trying to setup Spring Cloud Gateway to act as a OAuth2 client to authenticate/login users via a Keycloak Authentication server. I have been able to achieve this using the following code snipet:
Security Config:
#Configuration
#EnableWebFluxSecurity
#EnableReactiveMethodSecurity
public class SecurityConfig {
private final GatewayAuthenticationSuccessHandler gatewayAuthenticationSuccessHandler;
public SecurityConfig(GatewayAuthenticationSuccessHandler gatewayAuthenticationSuccessHandler) {
this.gatewayAuthenticationSuccessHandler = gatewayAuthenticationSuccessHandler;
}
#Bean
public SecurityWebFilterChain securityWebFilterChain(
ServerHttpSecurity http,
ReactiveClientRegistrationRepository clientRegistrationRepository) {
http
.authorizeExchange()
.pathMatchers("/ui/**").permitAll()
.anyExchange().authenticated()
.and()
.oauth2Login().authenticationSuccessHandler(gatewayAuthenticationSuccessHandler)
.and()
.oauth2ResourceServer().jwt();
http.logout(
logout ->
logout.logoutSuccessHandler(
new OidcClientInitiatedServerLogoutSuccessHandler(clientRegistrationRepository)));
http.logout().logoutUrl("/logout");
http.csrf().disable();
http.httpBasic().disable();
http.formLogin().disable();
return http.build();
}
}
Auth Success Handler:
#Component
public class GatewayAuthenticationSuccessHandler implements ServerAuthenticationSuccessHandler {
private ServerRedirectStrategy redirectStrategy = new DefaultServerRedirectStrategy();
#Value("${my.frontend_url}")
private String DEFAULT_LOGIN_SUCCESS_URL;
#Override
public Mono<Void> onAuthenticationSuccess(WebFilterExchange webFilterExchange, Authentication authentication) {
URI url = URI.create(DEFAULT_LOGIN_SUCCESS_URL);
return this.redirectStrategy.sendRedirect(webFilterExchange.getExchange(), url);
}
}
With this setup, the gateway app can authenticate the users and obtain a JWT token from the authentication server on behalf of the caller (UI app). Based on my understanding, Spring security then uses spring session to create and feed back a SESSION cookie to the caller. This session cookie can be used for subsequent calls to authenticate the user. The gateway would use the SESSION cookie value to retrieve the associated JWT token from the cache and relays it to the downstream resource servers when proxying requests. I have also setup a token refresh filter to refresh the JWT token on the caller's behalf and a Redis ache to share this session cookie between multiple instances of the gateway.
What I would like to do now is to return the actual JWT token that was retrieved by the gateway back to the caller (instead of a SESSION cookie). In other words I am hoping to make my gateway a little more stateless by using JWT end-to-end (instead of using SESSION cookie for caller --> gateway and then JWT for gateway --> resource servers). Is this even possible with the current state of spring cloud gateway?
PS. I am using spring boot version 2.2.8 and spring cloud version HOXTON.SR6
Not sure this can help , but try to add a SessionPolicy as STATELESS to your webfilter chain as shown below , and it should work.
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
Also you could try to override the sessionAuthenticationStrategy with a NullAuthenticatedSessionStrategy if you are extending your config class to WebSecurityConfigurerAdapter.
override fun sessionAuthenticationStrategy(): SessionAuthenticationStrategy {
return NullAuthenticatedSessionStrategy()
}

spring OAuth2 service to service client credentials

I have a set of services behind a zuul gateway, one of which is the auth server. I have it configured to parse the jwt with a jwk set from the auth server at /.well-known/jwks.json for users on each service with a password grant to access simple endpoints, but i'm wondering if it's possible to decide on a case by case basis which controllers and endpoints are using the user's access token vs using the service's client credentials when those services have to call other services. for example:
I have a contact service that manages customers, and another service that manages inventory.
When a user wants to see which customers are interacting with which inventory, i'm able to use an OAuth2RestTemplate to call the other service like so
#RequestMapping("/sales/{id}")
public Map<Object, Customer> getSales(#PathVariable Long customerId) {
Object inventory = restTemplate.getForObject("http://inventory-service/inventory", Object.class);
Customer customer = repository.findById(customerId);
Map<Object, Customer> sales = new HashMap;
sales.put(customer, inventory);
return sales;
}
I'm getting a 500 response A redirect is required to get the users approval even though i've tried configuring the Customer service to use client credentials flow instead of authorization code flow.
the security settings for the customer service:
security:
oauth2:
resource:
jwk:
key-set-uri: ${GATEWAY:http://localhost:8762}/.well-known/jwks.json
client:
client-id: first-client
client-secret: noonewilleverguess
access-token-uri: ${GATEWAY:http://localhost:8762}/oauth/token
with the main class annotated with #SpringBootApplication, #EnableGlobalMethodSecurity(prePostEnabled = true), and #EnableResourceServer.
here's some more configuration for context
#EnableWebSecurity
public class SecurityConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().anyRequest().permitAll();
}
#LoadBalanced
#Bean
public OAuth2RestTemplate restTemplate(OAuth2ClientContext clientContext,
OAuth2ProtectedResourceDetails resourceDetails) {
return new OAuth2RestTemplate(resourceDetails, clientContext);
}
the documentation suggests that #EnableOAuth2Client isn't necessary when exposing the OAuth2RestTemplate so i have omitted that annotation.
Ideally i'd like to be able to pick and choose which requests use the user's access token and which requests use the service's client credentials, but i haven't found any resources that do so. Is it even possible?

Spring Boot 2.0 OAuth2 Client - Getting bearer token across sessions

I have a basic OAuth2 App set up:
#Configuration
#EnableOAuth2Sso
#Order(0)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/actuator/health", "/", "/noauth", "/login").permitAll()
.anyRequest().authenticated().and()
.oauth2Login().defaultSuccessUrl("/auth");
}
}
It works well for a single instance and I can request OAuth2AuthorizedClient details:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authentication;
OAuth2AuthorizedClient client = clientService.loadAuthorizedClient(
oauthToken.getAuthorizedClientRegistrationId(), oauthToken.getName());
// Gets an OAuth2 token
client.getAccessToken().getTokenValue()
However, if I run this in a microservices environment (>1 instance) then client will always be null. Authentication also doesn't work correctly in this case.
I am using the org.springframework.security:spring-security-oauth2-jose library and authenticating with Google.
Any hints on how to persist the bearer token between sessions (or refresh it if it's not there)?

Resources