Skip to content

OAuth 2.0 Client supports application clustering #7889

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jgrandja opened this issue Feb 3, 2020 · 4 comments
Closed

OAuth 2.0 Client supports application clustering #7889

jgrandja opened this issue Feb 3, 2020 · 4 comments
Assignees
Labels
in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) type: enhancement A general enhancement
Milestone

Comments

@jgrandja
Copy link
Contributor

jgrandja commented Feb 3, 2020

OAuth 2.0 Client should support a deployment topology where application instances are replicated and distributed across a cluster. This should be supported when using HttpSecurity.oauth2Login() and/or HttpSecurity.oauth2Client(), as well for the reactive counterpart, ServerHttpSecurity.oauth2Login() and/or ServerHttpSecurity.oauth2Client().

This can be supported today by using Spring Session and providing a minor customization for OAuth 2.0 Client. Spring Session allows replacing the HttpSession in the application container in a neutral way. For example, instead of using the application container's HttpSession implementation, Spring Session can store session data to either Redis, JDBC or Hazelcast. This is key to enable application clustering since application instances do not depend on the underlying container's HttpSession and instead retrieve session data from a remote/central backing store.

In order to enable application clustering for OAuth 2.0 Client, you need to configure Spring Session and ensure the HttpSession implementations of AuthorizationRequestRepository<OAuth2AuthorizationRequest> and OAuth2AuthorizedClientRepository are configured.

The default AuthorizationRequestRepository<OAuth2AuthorizationRequest> is HttpSessionOAuth2AuthorizationRequestRepository, so there is no need to configure this further.

The default OAuth2AuthorizedClientRepository is AuthenticatedPrincipalOAuth2AuthorizedClientRepository, which is backed by an InMemoryOAuth2AuthorizedClientService that stores OAuth2AuthorizedClient in-memory. This default configuration will not work in a clustered environment unless you have Session affinity (sticky sessions) configured. To override the default configuration and ensure OAuth2AuthorizedClient are stored in the backing HttpSession, provide the following configuration:

@Bean
public OAuth2AuthorizedClientRepository authorizedClientRepository() {
	return new HttpSessionOAuth2AuthorizedClientRepository();
}

The reactive counterpart:

@Bean
public ServerOAuth2AuthorizedClientRepository authorizedClientRepository() {
	return new WebSessionServerOAuth2AuthorizedClientRepository();
}

The default serialization used by Spring Session is standard Java Serialization. However, if your requirements are to store session data in JSON format, this will be possible in the upcoming 5.3 release via #4886. To enable session data to be stored in JSON format when using Redis, add the following configuration (in addition to above):

@Bean
public RedisSerializer<Object> springSessionRedisSerializer() {
	return new GenericJackson2JsonRedisSerializer(objectMapper());
}

private ObjectMapper objectMapper() {
	ObjectMapper mapper = new ObjectMapper();
	mapper.registerModules(SecurityJackson2Modules.getModules(getClass().getClassLoader()));
	return mapper;
}

See the Spring Session Redis-JSON sample for further details on how to configure.

@jgrandja jgrandja self-assigned this Feb 3, 2020
@jgrandja jgrandja added this to the 5.3.0 milestone Feb 3, 2020
@jgrandja jgrandja added in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) type: enhancement A general enhancement labels Feb 3, 2020
@jgrandja
Copy link
Contributor Author

An alternative to storing OAuth2AuthorizedClient's in the HttpSession via HttpSessionOAuth2AuthorizedClientRepository, you may configure a JdbcOAuth2AuthorizedClientService that persists in a database using a JdbcOperations.
See #7655

@Gomzee18
Copy link

Gomzee18 commented Jul 25, 2020

@jgrandja

Below is the screenshot of my project dependencies:

image

And this is how my auth configure looks like in WebSecurity. I am using Spring JDBC session and annotated my main class with @EnableJdbcHttpSession as well.


protected void configure(HttpSecurity http) throws Exception {
            http.antMatcher("/**")
                    .csrf()
                        .disable()

                    .authorizeRequests()
                        .antMatchers("/dashboard").hasRole("ADMIN")
                        .anyRequest().permitAll()
                    .and().exceptionHandling().accessDeniedPage("/accessdenied")
                    .and()
                        .oauth2Login()
                        .authorizedClientRepository(authorizedClientRepository())
                        .userInfoEndpoint()
                        .userAuthoritiesMapper(this.userAuthoritiesMapper());

    }`

Based on your above recommendation I have also added:

@Bean public OAuth2AuthorizedClientRepository authorizedClientRepository() { return new HttpSessionOAuth2AuthorizedClientRepository(); }

But still some of login request gets failed intermittently in my clustered environment. With error:


2020-07-24T19:46:46.621+08:00 [APP/PROC/WEB/0] [OUT] 2020-07-24 11:46:46.620 DEBUG 14 --- [nio-8080-exec-9] o.s.security.web.FilterChainProxy : /login/oauth2/code/?code=7ImkA4&state=akHJEo-dDMevWFv3MSznq4dU7brEzU_eHGJjuObnO98%3D at position 6 of 14 in additional filter chain; firing Filter: 'OAuth2LoginAuthenticationFilter'
2020-07-24T19:46:46.621+08:00 [APP/PROC/WEB/0] [OUT] 2020-07-24 11:46:46.621 DEBUG 14 --- [nio-8080-exec-9] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/login/oauth2/code/'; against '/login/oauth2/code/*'
2020-07-24T19:46:46.621+08:00 [APP/PROC/WEB/0] [OUT] 2020-07-24 11:46:46.621 DEBUG 14 --- [nio-8080-exec-9] .s.o.c.w.OAuth2LoginAuthenticationFilter : Request is to process authentication
2020-07-24T19:46:46.623+08:00 [APP/PROC/WEB/0] [OUT] 2020-07-24 11:46:46.622 DEBUG 14 --- [nio-8080-exec-9] o.s.s.authentication.ProviderManager : Authentication attempt using org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider
2020-07-24T19:46:46.872+08:00 [APP/PROC/WEB/0] [OUT] 2020-07-24 11:46:46.872 DEBUG 14 --- [nio-8080-exec-9] o.s.s.authentication.ProviderManager : Authentication attempt using org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider
2020-07-24T19:46:46.872+08:00 [APP/PROC/WEB/0] [OUT] 2020-07-24 11:46:46.872 DEBUG 14 --- [nio-8080-exec-9] .s.a.DefaultAuthenticationEventPublisher : No event was found for the exception org.springframework.security.oauth2.core.OAuth2AuthenticationException
2020-07-24T19:46:46.874+08:00 [APP/PROC/WEB/0] [OUT] 2020-07-24 11:46:46.873 DEBUG 14 --- [nio-8080-exec-9] .s.o.c.w.OAuth2LoginAuthenticationFilter : Authentication request failed: org.springframework.security.oauth2.core.OAuth2AuthenticationException: [invalid_grant] Invalid authorization code: 7ImkA4
2020-07-24T19:46:46.874+08:00 [APP/PROC/WEB/0] [OUT] org.springframework.security.oauth2.core.OAuth2AuthenticationException: [invalid_grant] Invalid authorization code: 7ImkA4'

In DEBUG logs I can see this:

2020-07-24T19:46:46.990+08:00 [APP/PROC/WEB/0] [OUT] 2020-07-24 11:46:46.989 DEBUG 14 --- [nio-8080-exec-2] w.c.HttpSessionSecurityContextRepository : HttpSession returned null object for SPRING_SECURITY_CONTEXT
2020-07-24T19:46:46.990+08:00 [APP/PROC/WEB/0] [OUT] 2020-07-24 11:46:46.989 DEBUG 14 --- [nio-8080-exec-2] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper$HttpSessionWrapper@1ab423b1. A new one will be created.

For authorization I am using:
authorization-grant-type=authorization_code client-authentication-method=basic

@jgrandja Can you please help in understanding what can be going wrong here?

@jgrandja
Copy link
Contributor Author

@Gomzee18 Looking at your config and logs, it's not clear why you are having this issue. It may be a misconfiguration on the Spring Session side since the log reveals:

No SecurityContext was available from the HttpSession

@vijaynr
Copy link

vijaynr commented Jul 15, 2023

Can I use a static HashMap to store the authorized requests instead? I believe that should work for statelessness on a single node. But definitely does not work for clustered environments.
What other problems do you foresee with the static MAP approach?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

3 participants