Spring Security Customizing Authorization Endpoint URL - spring

I am implementing a Spring MVC REST web service and attempting to security it with Spring Security Oauth2.
My authorization URL is at the following address (note that there is no registration id):
http://localhost:8080/myoauthserver/oauthservlet
So, in my Spring Security Config, I have this:
#Bean
#Profile("dev")
public ClientRegistration devClientRegistration() {
// set up variables to pass to ClientRegistration
ClientRegistration result = ClientRegistration
.withRegistrationId("") // this obviously doesn't work
.clientId(clientId)
.clientSecret(clientSecret)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationUri(authorizationUri)
.tokenUri(clientSecret)
.userInfoUri(userInfoUri)
.userNameAttributeName(userNameAttribute)
.redirectUri(redirectUrl)
.providerConfigurationMetadata(providerDetails)
.build();
return result;
}
#Bean
public ClientRegistrationRepository clientRegistrationRepository() {
ClientRegistrationRepository repository = new InMemoryClientRegistrationRepository(devClientRegistration());
return repository;
}
#Bean
#Profile("dev")
public SecurityFilterChain securityWebFilterChain(HttpSecurity http) throws Exception {
// this prevents throwing an exception in the oauth2 lambda function
Map<String, String> loginProps = ...
http
.authorizeRequests()
// what we want is for a few urls to be accessible to all, but most to require
// oauth authentication/authorization
.antMatchers("/login/**","/error/**", "/oauth2/**").anonymous()
.anyRequest().authenticated()
.and()
.oauth2Login(oauth2 -> {
oauth2
.clientRegistrationRepository(clientRegistrationRepository(loginPropsCopy))
.authorizationEndpoint()
.baseUri("http://localhost:8080/myoauthserver/oauthservlet");
}
);
return http.build();
}
How do I customize the entire authorization endpoint to not try to tack the registration id onto it?

Related

Securing Spring Cloud Gateway with Spring Security

I am struggling with configuring security for my Spring Cloud Gateway service.
For now i have configured in my api-gateway just one route to user service /api/v1/users. Requests are correctly routed to user service untill I add Spring Security to the dependescies.
Even with that simple config, that should allow all traffic, I am still getting 401 Unathorized response:
#Configuration
#EnableWebFluxSecurity
class SecurityConfiguration {
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity serverHttpSecurity) {
return serverHttpSecurity
.authorizeExchange()
.anyExchange().permitAll().and()
.csrf().disable()
.build();
}
}
What am I doing wrong?
You need to create user to do that. See the sample attached in below. I am using in-memory user to authenticate. Note in-memory user is just for testing purpose only.
#Configuration
public class InMemoryUserSecurityAdapter {
#Bean
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
return http
.authorizeExchange()
.pathMatchers("/school-library-service/**").authenticated()
.and().authenticationManager(reactiveAuthenticationManager())
.authorizeExchange().anyExchange().permitAll().and()
.httpBasic().and()
.build();
}
#Bean
ReactiveAuthenticationManager reactiveAuthenticationManager(){
return new UserDetailsRepositoryReactiveAuthenticationManager(getInMemoryUserDetails());
}
#Bean
public MapReactiveUserDetailsService getInMemoryUserDetails() {
UserDetails admin = User.withDefaultPasswordEncoder().username("admin1").password("password")
.roles("ADMIN")
.build();
return new MapReactiveUserDetailsService(admin);
}
}
https://github.com/DeepuGeorgeJacob/school-management/blob/main/security/in-memory-user-security/src/main/java/com/school/management/config/InMemoryUserSecurityAdapter.java
Happy coding :)

Spring Cloud Gateway - Intercept under hood request/response to Keycloak IDP

We are implementing a Spring Cloud Gateway application (with Webflux) that is mediating the OAuth2 authentication with Keycloak.
SCG checks if the Spring Session is active: if not, redirects to Keycloak login page and handles the response from the IDP. This process is executed out-of-the-box by the framework itself.
Our needs is to intercept the IDP Keycloak response in order to retrieve a field from the response payload.
Do you have any advices that will help us to accomplish this behavior?
Thanks!
You can implement ServerAuthenticationSuccessHandler:
#Component
public class AuthenticationSuccessHandler implements ServerAuthenticationSuccessHandler {
private ServerRedirectStrategy redirectStrategy;
public AuthenticationSuccessHandler(AuthenticationService authenticationService) {
redirectStrategy = new DefaultServerRedirectStrategy();
}
#Override
public Mono<Void> onAuthenticationSuccess(WebFilterExchange webFilterExchange, Authentication authentication) {
if(authentication instanceof OAuth2AuthenticationToken) {
//Your logic here to retrieve oauth2 user info
}
ServerWebExchange exchange = webFilterExchange.getExchange();
URI location = URI.create(httpRequest.getURI().getHost());
return redirectStrategy.sendRedirect(exchange, location);
}
}
And update your security configuration to include success handler:
#Configuration
public class SecurityConfiguration {
private AuthenticationSuccessHandler authSuccessHandler;
public SecurityConfiguration(AuthenticationSuccessHandler authSuccessHandler) {
this.authSuccessHandler = authSuccessHandler;
}
#Bean
SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange(exchange -> exchange
//other security configs
.anyExchange().authenticated()
.and()
.oauth2Login(oauth2 -> oauth2
.authenticationSuccessHandler(authSuccessHandler)
);
return http.build();
}
}

Spring security very simple basic authentication

I've tried to implement a very simple BASIC authentication with Spring Boot, without the deprecated WebSecurityConfigurerAdapter.
#Configuration
public class SecurityConfig {
#Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().antMatchers("/a", "/b", "/c", "/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html");
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated()
)
.httpBasic();
return http.build();
}
#Bean
public InMemoryUserDetailsManager userDetailsService() {
UserDetails user = User.builder()
.username("user")
.password("{bcrypt}$2y$10$rUzpfbTx9lcIs6N4Elcg2e2DGM4wMwkx0ixom7qLW5kYnztRgT.a2")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
The ignored endpoints work (with a warning: You are asking Spring Security to ignore Ant [pattern='/swagger-ui.html']. This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.). For the other, I get an HTTP 403.
What have I done wrong?
If you are doing POST request, it can be the CSRF protection. Add logging.level.org.springframework.security=TRACE in your application.properties file and see the console output after the request is made to see what is happening.
If it is CSRF protection, I recommend you leave it enabled unless you have a requirement that tells you to disable it. You can have more details about Cross Site Request Forgery here.
Also, if you want to use the {bcrypt} prefix in your password, use the PasswordEncoderFactories.createDelegatingPasswordEncoder. If you want to use only the BCryptPasswordEncoder then you have to remove the {bcrypt} prefix

customize state parameter with Oauth2client using spring security

I want to customize state parameter with Oauth2client using spring security OIDC. I am using spring reactive. The issue is I am not able to add customize state in client registration. Below is my security configuration.
#Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
return http
.authorizeExchange()
.pathMatchers("/","/jwt").permitAll()
.anyExchange().authenticated().and()
.oauth2Client()
.build();
}
#Bean
public ReactiveClientRegistrationRepository reactiveClientRegistrationRepository(RegisteredClients clients) {
List<ClientRegistration> clientRegistrations = clients.getClients()
.entrySet().stream()
.map(clientRegistration -> {
String registrationId = clientRegistration.getKey();
RegisteredClients.OAuthClient client = clientRegistration.getValue();
return ClientRegistration.withRegistrationId(registrationId)
.clientId(client.getClientId())
.clientSecret(client.getClientSecret())
.redirectUriTemplate(client.getRedirectUri())
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.tokenUri(client.getTokenUri())
.authorizationUri(client.getAuthorizationUri())
.scope("openid")
.build();
})
.collect(toList());
return new InMemoryReactiveClientRegistrationRepository(clientRegistrations);
}
To customize the authorization request, you should wire an ServerAuthorizationRequestResolver:
#Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
return http
// ...
.oauth2Client((oauth2) -> oauth2
.authorizationEndpoint((authorization) -> authorization
.authorizationRequestResolver(authorizationRequestResolver)
)
)
.build();
}
#Bean
OAuth2ServerAuthorizationRequestResolver authorizationRequestResolver
(ReactiveClientRegistrationRepository registrations) {
DefaultServerOAuth2AuthorizationRequestResolver resolver =
new DefaultServerOAuth2AuthorizationRequestResolver(registrations);
resolver.setAuthorizationRequestCustomizer((request) -> request.state(...));
return resolver;
}
Spring Security has some related servlet documentation that may be useful as well.

ReactiveSpringSecurity role mapping

Based on this example https://blog.jdriven.com/2019/11/spring-cloud-gateway-with-openid-connect-and-token-relay I'm trying to use Spring Gateway, the latest version of Spring Security and Keycloak. Behind the Gateway there is a static application that I want to limit access to. I managed to configure everything so that authentication works. However, I am unable to validate the role correctly. Spring Security does not read it from the token and always assigns ROLE_USER. Token contains proper role and other parameters like username or scopes are read correctly. How to map a roles using ReactiveSecurity. Below is my configuration.
#Configuration
public class SecurityConfig {
#Bean
public OidcClientInitiatedServerLogoutSuccessHandler logoutSuccessHandler(#Value("${postLogoutRedirectUrl}") URI postLogoutRedirectUrl, ReactiveClientRegistrationRepository clientRegistrationRepository) {
OidcClientInitiatedServerLogoutSuccessHandler logoutSuccessHandler = new OidcClientInitiatedServerLogoutSuccessHandler(clientRegistrationRepository);
logoutSuccessHandler.setPostLogoutRedirectUri(postLogoutRedirectUrl);
return logoutSuccessHandler;
}
#Bean
public SecurityWebFilterChain springSecurityFilterChain(#Value("${securedPaths}") String[] securedPaths, ServerHttpSecurity http,
ReactiveClientRegistrationRepository clientRegistrationRepository,
OidcClientInitiatedServerLogoutSuccessHandler logoutSuccessHandler) {
http.oauth2Login();
http.logout(logout -> logout.logoutSuccessHandler(new OidcClientInitiatedServerLogoutSuccessHandler(clientRegistrationRepository)));
http.authorizeExchange()
.pathMatchers(securedPaths).hasRole("admin")
.anyExchange().permitAll();
http.logout().logoutSuccessHandler(logoutSuccessHandler);
http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler());
http.csrf().disable();
return http.build();
}
}

Resources