Spring Boot Spring Security 5, OAuth2Login Not working as expected - spring

We have developed our own Authorization Server which we are using for Single Sign on
we developed our client app with the annotation #EnableOAuth2Sso with the following configuration
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
.and()
.antMatcher("/**")
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class).authorizeRequests()
.and()
.authorizeRequests()
.antMatchers("/","/login/**","/callback/url/**","/error**","/webjars/**","/favicon.ico**").permitAll()
.anyRequest().authenticated()
.and().formLogin().failureHandler(customAuthenticationFailureHandler)
.and().httpBasic()
.and()
.logout().invalidateHttpSession(true).deleteCookies("JSESSIONID");
it was working fine
but just now I want to use the .oauth2Login() features comes with Spring Security 5 so I changed the annotation to #EnableOAuth2Client from #EnableOAuth2Sso and used the following configuration
#Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.oauth2Login();
}
now while compiling I am getting the following error messages
Description:
Method springSecurityFilterChain in org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration required a bean of type 'org.springframework.security.oauth2.client.registration.ClientRegistrationRepository' that could not be found.
The following candidates were found but could not be injected:
- Bean method 'clientRegistrationRepository' in 'OAuth2ClientRegistrationRepositoryConfiguration' not loaded because OAuth2
Clients Configured Condition registered clients is not available
Action:
Consider revisiting the entries above or defining a bean of type 'org.springframework.security.oauth2.client.registration.ClientRegistrationRepository' in your configuration.
So I changed my application.properties from
security.oauth2.client.auto-approve-scopes=read,write
security.oauth2.client.access-token-uri=http://localhost:8080/xxxxx/oauth/token
security.oauth2.client.user-authorization-uri=http://localhost:8080/xxxxx/oauth/authorize
security.oauth2.client.client-id=xxx
security.oauth2.client.client-secret=secret
security.oauth2.client.pre-established-redirect-uri=http://localhost:11001/xxx/login/oauth2/code
security.oauth2.client.registered-redirect-uri=http://localhost:11001/xxx/login/oauth2/code
security.oauth2.client.useCurrentUri=false
security.oauth2.client.use-current-uri=false
security.oauth2.client.grant-type=authorization_code, refresh_token
to the following
spring.security.oauth2.client.registration.client-id=xxx_client
spring.security.oauth2.client.registration.client-secret=secret
spring.security.oauth2.client.registration.authorization-grant-type=authorization_code,refresh_token
spring.security.oauth2.client.registration.redirect-uri=http://localhost:11001/xxx/login/oauth2/code
spring.security.oauth2.client.registration.scope=read,write
spring.security.oauth2.client.registration.client-name=xxxxx
spring.security.oauth2.client.registration.xxxxx.client-authentication-method=POST
spring.security.oauth2.client.provider.token-uri=http://localhost:8080/xxxxx/oauth/token
spring.security.oauth2.client.provider.authorization-uri=http://localhost:8080/xxxxx/oauth/authorize
spring.security.oauth2.client.provider.user-info-uri=http://localhost:8080/xxxxx/validateLogin
But it displays a welcome page where our authorization server name is shown i.e xxxxx and if I click that link it does not connect to our authorization server.
I have confusion here what is the difference between #EnableOAuth2Sso and #EnableOAuth2Client Why when we use OAuth2Login() we need to change our properties
Is there any good example of using OAuth2Login with Custom Oauth2 provider (instead of google,facebook,okta etc)I searched google but no where any example provided

Related

How to secure only swagger UI page " swagger-ui/index.html#/ " and all other API end points should not be authenticated in Spring boot

I have a requirement where I just need to secure the Swagger UI page. All other endpoints I have written in the application should not be authenticated.
For this, I am using the Spring security starter. I have the Security Config for Spring boot in place. I am trying to authenticate ("/v2/api-docs") because this is where we see all the endpoints in Swagger UI. And also I am trying to permit ("/calculator-controller/callCalculatorServiceUsingPOST") which I see in browser URL when I click on my end point Try it now button and also permitting ("/calculate") which is in my controller. To be safer, I have tried to permit all possible combinations, but no luck.
What Am I missing ???
#Configuration #EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.authorizeRequests()
.antMatchers("/v2/api-docs").authenticated()
.antMatchers("/calculator-
controller/callCalculatorServiceUsingPOST",
"calculator-controller/**", "/calculate")
.permitAll()
.and()
.httpBasic();
}
}

Endpoint not accessible after Boot 2.6 upgrade

After upgrading the Boot version in my project to 2.6.0, my endpoint is no longer accessible, I'm automatically redirected to the login page even though I configured it with a permitAll() directive:
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.mvcMatchers("presentations")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin();
}
// ...
}
It seems this is actually related to how Spring Boot processes the mvcMatchers values after the upgrade:
The default strategy for matching request paths against registered Spring MVC handler mappings has changed from AntPathMatcher to PathPatternParser.
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.6-Release-Notes#deferred-openid-connect-discovery
And this new setup requires my presentations pattern to start with /:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.mvcMatchers("/presentations")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin();
}
Alternatively, if I had several routes and I need a quick fix I could set up the following application property to revert this to its previous behavior:
spring.mvc.pathmatch.matching-strategy=ant-path-matcher
BTW, path-pattern-parser is the default value, as it seems to be more efficient... here is some additional information on this and on the differences between PathPatternParser and AntPathMatcher:
https://spring.io/blog/2020/06/30/url-matching-with-pathpattern-in-spring-mvc
https://docs.spring.io/spring-framework/docs/5.3.13/reference/html/web.html#mvc-ann-requestmapping-uri-templates
EDIT: I also realized that using antMatchers() made some of my MockMvc tests fail, this was a bug that got fixed in Boot 2.6.1.

Spring Security with OAuth2 losing session

We have a Spring Boot-based Gateway using Spring Security, OAuth2 login, and Zuul routing. It is also using Spring Session to store sessions in Redis. This Gateway stores an OAuth2 token in the session and forwards the OAuth2 Bearer token to backend services.
We have an issue where users are being signed out quite often. It appears this happens roughly hourly. We are not even quite sure what is causing this with all the different tools in place.
Our session cookie in the browser expires in a longer period of time. So I suspect it is either Spring invalidating the session, or the OAuth2 token expiring.
From a quick inspection of the code, it appears that OAuth2TokenRelayFilter supports refreshing the token. Is this correct?
How can track down the cause of this and fix it?
For reference, we are using these versions:
Spring Boot 2.1.12
Spring Cloud Greenwich.SR4
Here are some relevant snippets.
Our web security config for the web pages.
#Configuration
#EnableWebSecurity
#EnableOAuth2Sso
#Order(SecurityProperties.BASIC_AUTH_ORDER - 2)
#Profile("!security-disabled")
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.authorizeRequests()
.antMatchers("/login", "/login/**", "/favicon.ico").permitAll()
.antMatchers("/signout").authenticated()
.anyRequest().hasAnyRole("ADMIN", "MEMBER")
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.httpBasic()
.disable()
.formLogin()
.disable()
.logout()
.logoutUrl("/signout")
.deleteCookies("SESSION")
.and()
// #formatter:on
}
Security configuration for API paths.
#Configuration
#Order(SecurityProperties.BASIC_AUTH_ORDER - 2 - 10)
#Profile("!security-disabled")
public class ApiSecurityConfig extends WebSecurityConfigurerAdapter
{
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.requestMatchers()
.antMatchers("/api/**")
.and()
.authorizeRequests()
.antMatchers("/**").hasAnyRole("ADMIN", "MEMBER")
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.headers()
.frameOptions().sameOrigin()
.and()
.httpBasic()
.disable()
.formLogin()
.disable()
.logout()
.disable()
.exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint());
// #formatter:on
}
}
Update
We have done some debugging of the Spring internals. First, we found that we were missing an OAuth2RestTemplate. Per the OAuth2 Boot documentation we found how to add it with:
#Bean
public OAuth2RestTemplate oauth2RestTemplate(
OAuth2ClientContext oauth2ClientContext,
OAuth2ProtectedResourceDetails details)
{
return new OAuth2RestTemplate(details, oauth2ClientContext);
}
This is now throwing an exception when OAuth2TokenRelayFilter calls restTemplate.getAccessToken().getValue();.
A redirect is required to get the users approval
This exception is thrown from AuthorizationCodeAccessTokenProvider.
OAuth2TokenRelayFilter
OAuth2TokenRelayFilter is a pre type filter which set the contexts with ACCESS_TOKEN and TOKEN_TYPE which will be used for the further authentication. It validates the tokens using getAccessToken() method and responds with "Cannot obtain valid access token" with 401 status.
You may check the validity of tokens and refresh token is correctly configured with grant_type as refresh_token as The Refresh Token grant type is used by clients to exchange a refresh token for an access token when the access token has expired which allows clients to continue to have a valid access token without further interaction with the user.
In case if you want to disable OAuth2TokenRelayFilter, you may use the following
zuul.OAuth2TokenRelayFilter.pre.disable=true

Spring Boot Security - Multiple configurations

I'm working (and struggling a little bit) on an example using spring-boot with spring security.
My system is using a web app and also provide an REST-API, so i would like to have form based security (web) and basic auth (resp api).
As the spring documentation recommend (https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#multiple-httpsecurity), I need to create a multi http web security configuration.
The main code works, but if I use Postman for the test of my RestApi following use-case does not work.
All GET-requests to /restapi/ working without authentication (statuscode 200)
All POST-requests to /restapi/ without the BASIC Auth Header are working (statuscode 401)
All POST-requests to /restapi/ with a correct BASIC Auth Header are work (statuscode 200)
BUT all requests with a wrong BASIC Auth header (f.e. user1/1234567) are returning the HTML-Loginpage defined in the first WebSecurityConfigurerAdapter (FormWebSecurityConfigurerAdapter)
Does anyone has an idea - what is wrong with my configuration?
#EnableWebSecurity
public class MultiHttpSecurityConfig {
#Autowired
private static RestAuthenticationAccessDeniedHandler restAccessDeniedHandler;
#Autowired
public void configureAuth(AuthenticationManagerBuilder auth) throws Exception{
auth.inMemoryAuthentication()
.withUser("admin").password("{noop}12345678").roles("ADMIN").and()
.withUser("user").password("{noop}12345678").roles("USER");
}
#Configuration
#Order(1)
public static class RestWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/restapi/**")
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/restapi/**").permitAll()
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(UNAUTHORIZED))
.and()
.exceptionHandling().accessDeniedHandler(restAccessDeniedHandler) ;
}
}
/*
Ensures that any request to our application requires the user to be authenticated (execpt home page)
Requests matched against "/css/**", "/img/**", "/js/**", "/index.html", "/" are fully accessible
Allows users to authenticate with HTTP Form Based authentication
Configure logout with redirect to homepage
*/
#Configuration
public static class FormWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/css/**", "/img/**", "/js/**", "/index.html", "/").permitAll()
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/index.html")
.permitAll();
}
}
}
I know it is a question from some time ago but I still want to share the answer for people who are struggling with this issue.
After a lot of searching I found out that the /error endpoint in spring boot 2.x is now secured by default. What I mean to say is in the past the /error was a endpoint what had no security at all (or didn't exist). The solution to this issue is quite straight forward.
antMatchers('/error').permitAll()
within your web security adapter configuration(s).
What happens if you don't do this, the security will check the endpoint against your configuration and if it cannot find this endpoint (/error) it will redirect to the standard login form, hence the 302.

Mix HttpBasic and FormLogin in Spring Security with Spring-boot-starter

I use spring-boot-starter 0.5.0.M6 with spring security to build my application which contains:
"/admin/"**: should be accessible to anyone have role ADMIN, form-based login
"/api/"**: should be accessible to anyone have role API, http basic login
My first attempt was:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.and()
.formLogin()
.defaultSuccessUrl("/admin/home")
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET"))
.permitAll();
http
.authorizeRequests()
.antMatchers("/api/**").hasRole("API")
.and()
.httpBasic();
}
With this approach:
all the "/admin/" and "/api/" can authentication use both basic & form-based login. This is not a critical issue.
when any security issue occurred, eg: authentication failed, or authorization failed, the login form is shown. This is a critical issue, I want if /api/** get authentication failed or authorization failed, it show the basic authentication popup with 401/403 status code.
Then I try with the solution from https://github.com/spring-projects/spring-security-javaconfig/blob/master/samples-web.md#sample-multi-http-web-configuration, but I only able to secure either /api/** or /admin/** but not both, depends on which one I annotated with #Order.
Please give me a hand.
Thanks much
For your api part, use the following. Note the first ant matcher that limits the scope of what is filtered by this security configuration. That was the part I did not understand at first from your reference.
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
// the ant matcher is what limits the scope of this configuration.
http.antMatcher("/api/**").authorizeRequests()
.antMatchers("/api/**").authenticated()
.and().httpBasic().realmName("Sourcing API");
}
}

Resources