How to tune authenticationEntryPoint behaviour Spring Security - spring

I have Spring Boot 2 based Security Gateway performing OAuth2 authentication sitting before GUI app and back-end.
It is configured like
#Configuration
#EnableOAuth2Client
#EnableWebSecurity
public class SecurityGatewayConfig extends WebSecurityConfigurerAdapter{
#Bean
public SecurityGatewayAuthenticationFilter filter() {
return new SecurityGatewayAuthenticationFilter("/login");
}
#Override
public void configure(HttpSecurity http) {
http
.addFilterAfter(new OAuth2ClientContextFilter(), AbstractPreAuthenticatedProcessingFilter.class)
.addFilterAfter(filter(), OAuth2ClientContextFilter.class)
.httpBasic().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
.and()
...
It redirect requests to /login and SecurityGatewayAuthenticationFilter performs authentication against external OAuth2 provider.
It is good for GIU app. However, when accessing back-end services(they have /api/ in the path) I need different behaviour: If request is not authenticated, do not redirect, but immediately return 401 error.
Any idea, how to configure Spring Security for that?

If i got you questions right, what you can do is work with different ConfigurationAdapters. The basic idea looks like:
#Order(1)
#Configuration
#EnableOAuth2Sso
public static class SecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("defaultMatcher")
private RequestMatcher defaultMatcher;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatcher(defaultMatcher)...
}
}
#Order(2)
#Configuration
public static class OtherConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatcher(yourRequestMatcher())...
}
}
Spring will evalutate each request in the order you add with Order() if you use #EnableResourceServer, this will always have Order(3)
You can then build your request matchers as example like this (in this example it matches all, but excludes explicitly some other):
#Bean
public RequestMatcher defaultMatcher(#Qualifier("apiMatcher") RequestMatcher api, #Qualifier("anyother") RequestMatcher anyother) {
final RequestMatcher all = new AntPathRequestMatcher("/**");
final RequestMatcher nonApi = new NegatedRequestMatcher(new OrRequestMatcher(api, anyother));
return new AndRequestMatcher(all, nonApi);
}
Hope that helps.
best regards,
WiPu

you need add entry point filter
#Component
public final class CustomAuthenticationEntryPoint implements
AuthenticationEntryPoint {
#Override
public void commence(final HttpServletRequest request, final
HttpServletResponse response, final AuthenticationException
authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
When a client accesses resources without authentication...

Related

WebSecurity.ignoring() in ResourceServerConfigurerAdapter

I'm currently implementing oauth2 through Cognito for my Spring Boot API. As a part of configuring Spring Security, I set up my ResourceServerConfigurerAdapter with an override of configure(HttpSecurity http). However I also needed to use the configure(WebSecurity webSecurity) override in order to use WebSecurity.ignoring. Several of my endpoints need to be publicly accessible.
So this led to me having a WebSecurityConfigurerAdapter as well as my ResourceServerConfigurerAdapter. The problem arose with csrf though. The ResourceServerConfig is disabling csrf, but the WebSecurityConfig was apparently taking precedence and now all my endpoints require csrf. Overriding the configure(HttpSecurity) in the WebSecurityConfig to disable csrf fixes the issue but seems wrong to me. I'd like to not have to override and mess with HttpSecurity twice, and ResourceServerConfig doesn't have a webSecurity.ignoring option to my knowledge.
Here's my code for the two below
WebSecurityConfiguration
#Configuration
#EnableWebSecurity(debug = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
//Disabling this fixes the csrf issue.
http.csrf().disable();
}
#Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity.ignoring().antMatchers("/api/auctions/**");
webSecurity.ignoring().antMatchers("/api/lots/**");
}
}
ResourceServerConfiguration
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends ResourceServerConfigurerAdapter {
private final ResourceServerProperties resource;
public SecurityConfiguration(ResourceServerProperties resource) {
this.resource = resource;
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.cors();
http.csrf().disable();
http.authorizeRequests().antMatchers("/**").permitAll().anyRequest().permitAll();
}
// Enabling Cognito Converter
#Bean
public TokenStore jwkTokenStore() {
return new JwkTokenStore(
Collections.singletonList(resource.getJwk().getKeySetUri()),
new CognitoAccessTokenConverter(),
null);
}
}
I mostly just want some guidance on what the best path on this is. I've done some digging online and there's conflicting and outdated answers, and everyone seems to be doing very different things with these configurations.

Unable to use permitAll() with Spring Boot 2.3.4 to allow access to Swagger UI after integrating with API-Key Authentication

I tried integrating API-Key authentication mechanism to Spring Boot Application in the following way:
Created a CustomAPIKeyAuthFilter that extends AbstractPreAuthenticatedProcessingFilter where it gets the preauthenticated principal from the headers of the request.
public class CustomAPIKeyAuthFilter extends AbstractPreAuthenticatedProcessingFilter {
private String principalRequestHeader;
private String principalAuthKey;
public CustomAPIKeyAuthFilter(String principalRequestHeader, String principalAuthKey) {
this.principalRequestHeader = principalRequestHeader;
this.principalAuthKey = principalAuthKey;
}
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
return request.getHeader(principalRequestHeader);
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
// anything to be returned here??
return "TBD";
}
}
Created WebSecurityConfig that extends WebSecurityConfigurerAdapter. In this one, the custom filter is injected inside the overridden method protected void configure(HttpSecurity httpSecurity) {}
#EnableWebSecurity
#Order(1)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${superuser}")
private String principalRequestHeader;
#Value("${superuserauthkey}")
private String principalRequestValue;
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
CustomAPIKeyAuthFilter filter = new CustomAPIKeyAuthFilter(principalRequestHeader, principalRequestValue);
filter.setAuthenticationManager(new AuthenticationManager() {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String principal = (String) authentication.getPrincipal();
if (principalRequestValue.equals(principal)){
authentication.setAuthenticated(true);
} else {
throw new BadCredentialsException("Missing API Key");
}
return authentication;
}
});
httpSecurity.
cors().and().
csrf().disable().authorizeRequests()
.antMatchers("**swagger**").permitAll() // this is the part that is not working for me
.anyRequest().authenticated()
.and()
.addFilter(filter)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
As you can see from the comment above, even though I used permitAll, I get the error 401 No pre-authenticated principal found in request at runtime if I try to access Swagger UI which was working before introducing spring-boot-starter-security related dependencies in my pom.xml. Is there a better way to exclude swagger UI alone from the list of URL end points that need API-key based authentication ?
Note: I am using springfox-swagger2 implementation of Swagger and the version used is 2.8.0.
Swagger have api endpoint which should be allowed in security level, add the below snippet in WebSecurityConfig.class
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v2/api-docs",
"/configuration/ui",
"/swagger-resources/**",
"/configuration/security",
"/swagger-ui.html",
"/webjars/**");
}
You could also try permitAll() to the patterns included.This will exclude the swagger from being authenticated.

Authentication provider per url pattern - Spring Boot

I faced problem when configuring different auth providers per url pattern using Spring Boot security. I am trying to configure security in Spring Boot app and want to have swagger behind basic auth and all API is secured only by token. I have it almost working, but noticed that API except the fact that it is secured by token which is verified by IDAuthProvider class it also is secured by basic auth. I do not want that and also noticed that if I removed line:
sessionCreationPolicy(SessionCreationPolicy.STATELESS).
it seems to be working correctly, but still header Basic {token} is being added in request which is something I do not want...
Do you know how can I configure it to make all swagger stuff secured by basic auth and API stuff secured by token?
My configuration looks like below:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Configuration
#Order(1)
public class SwaggerSecurityConfig extends WebSecurityConfigurerAdapter {
private final AuthenticationProvider userPassAuthProvider;
#Autowired
SwaggerSecurityConfig(UserPassAuthProvider userPassAuthProvider) {
this.userPassAuthProvider = userPassAuthProvider;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/swagger**").
authorizeRequests().
antMatchers("/swagger**").authenticated().
and().httpBasic().and().csrf().disable();
}
#Override
public void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(userPassAuthProvider);
}
}
#Configuration
#Order(2)
public class APISecurityConfig extends WebSecurityConfigurerAdapter {
private final AuthenticationProvider idAuthProvider;
#Autowired
APISecurityConfig(IDAuthProvider idAuthProvider) {
this.idAuthProvider = idAuthProvider;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/v1/**").
authorizeRequests().anyRequest().authenticated().
and().
addFilterBefore(idpAuthenticationFilter(authenticationManager()), BasicAuthenticationFilter.class).sessionManagement().
and().
csrf().disable();
}
#Override
public void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(idAuthProvider);
}
IDPAuthenticationFilter idpAuthenticationFilter(AuthenticationManager auth) {
return new IDPAuthenticationFilter(auth, new OrRequestMatcher(new AntPathRequestMatcher(ApiRouter.API_PATH + "/**", HttpMethod.GET.toString()), new AntPathRequestMatcher(ApiRouter.API_PATH + "/**", HttpMethod.POST.toString()), new AntPathRequestMatcher(ApiRouter.API_PATH + "/**", HttpMethod.DELETE.toString()), new AntPathRequestMatcher("/swagger**", HttpMethod.GET.toString())));
}
}
}

EnableResourceServer breaks oAuth2 authorization server

I implemented oAuth2 authorization server using Spring Boot version 1.5.2.RELEASE. The authorization server supports implicit flow. With the WebSecurityConfig below the login form (http://localhost:8200/login) works well.
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JpaUserDetailsService userDetailsService;
#Bean
#Override
public UserDetailsService userDetailsServiceBean() throws Exception {
return userDetailsService;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public AuthenticationProvider authenticationProvider() throws Exception {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsServiceBean());
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return new ProviderManager(singletonList(authenticationProvider()));
}
#Override
public void configure(WebSecurity web) {
web.ignoring()
.antMatchers("/")
.antMatchers("/docs/**")
.antMatchers("/swagger/**")
.antMatchers("/token/**")
.antMatchers("/v2/*")
.antMatchers(HttpMethod.OPTIONS, "/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/login**").permitAll().anyRequest().authenticated().and()
.formLogin().loginPage("/login").permitAll().and()
.logout().permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
}
I want resource server be a part of the same application. The purpose is I need a /me endpoint that will provide me details of logged in user and endpoints for managing users. But as soon as I add ResourceServerConfig annotated with EnableResourceServer below I start getting an error "Full authentication is required to access this resource" when I request http://localhost:8200/login.
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
public static final String RESOURCE_ID = "proclaim-auth";
#Autowired
private ResourceServerTokenServices tokenServices;
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources
.resourceId(RESOURCE_ID)
.tokenServices(tokenServices);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/api/ **").authenticated();
}
}
I suspect that resource server security chain precedes authorization server security chain. I tried to annotate WebSecurityConfig with annotation Order but it did not fix my problem:
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
}
What am I doing wrong? Please advise.
Thanks in advance!
EDIT 1
I added method configure(HttpSecurity http) into ResourceServerConfig and changed value of Order annotation to -1 on WebSecurityConfig. Now the security filted defined in WebSecurityConfig is applied and the one defined in ResourceServerConfig is ignored. So when I call /me endpoint with valid token I'm redirected to login page.
The cause of the problem was wrong configuration of http security in the ResourceServerConfig class. The correct configuration is as follows:
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/api/**").and()
.authorizeRequests().anyRequest().authenticated();
}
The requestMatchers will ensure that only requests on paths starting with "/api/" will be processed by this security chain. All other requests will be passed to the security chain defined in the WebSecurityConfig class. I was missing this in my config so all requests were processed by the ResourceServerConfig security chain and none request reached the WebSecurityConfig security chain.

Spring Security OAuth2, which decides security?

I've been trying to implement a OAuth2 authentication server using the guides by Dave Syer with some inspiration from JHipster. But I can't figure out how it all works together.
It looks like the security setup using the WebSecurityConfigurerAdapter is overwritten when I use ResourceServerConfigurerAdapter.
#Configuration
#EnableResourceServer
public class OAuth2ResourceConfig extends ResourceServerConfigurerAdapter {
private TokenExtractor tokenExtractor = new BearerTokenExtractor();
#Override
public void configure(HttpSecurity http) throws Exception {
http
.addFilterAfter(contextClearer(), AbstractPreAuthenticatedProcessingFilter.class)
.authorizeRequests()
.anyRequest().authenticated().and().httpBasic();
}
private OncePerRequestFilter contextClearer() {
return new OncePerRequestFilter() {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (tokenExtractor.extract(request) == null) {
SecurityContextHolder.clearContext();
}
filterChain.doFilter(request, response);
}
};
}
#Component
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
private final AuthenticationManager authenticationManager;
#Autowired
public CustomWebSecurityConfigurerAdapter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.parentAuthenticationManager(authenticationManager);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/login").permitAll()
.and()
.authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.and()
.requestMatchers().antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access")
.and()
.authorizeRequests().anyRequest().authenticated();
}
}
This is code taken from a few different examples, so they might not mix that well. But I can't find a good documentation/example list for OAuth2 (unlike Spring Boot which has a awesome documentation), so I'm having problems understanding how thye all fit together.
If I don't add the loginForm to the ResourceServerConfigurerAdapter, it will just give me unauthorized. But I defined it in the WebSecurityConfigurererAdapter as permitAll().
This is the AuthorizationServerConfigurerAdapter:
#Configuration
#EnableAuthorizationServer
public class OAuth2AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("acme")
.secret("acmesecret")
.authorizedGrantTypes("authorization_code", "refresh_token",
"password").scopes("openid");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager).accessTokenConverter(jwtAccessTokenConverter);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
}
Anything I'm doing wrong? Do I have to setup all the security within the ResourceServerConfigurerAdapter? Do I even need the WebSecurityConfigurerAdapter anymore?
If anyone know any guides, tutorials, blogs or anything alike that might help me wrap my head around how this works, that would be greatly appreciated.
Kind regards, Kenneth.
You need a WebSecurityConfigurerAdapter to secure the /authorize endpoint and to provide a way for users to authenticate. A Spring Boot application would do that for you (by adding its own WebSecurityConfigurerAdapter with HTTP basic auth). It creates a filter chain with order=0 by default, and protects all resources unless you provide a request matcher. The #EnableResourceServer does something similar, but the filter chain it adds is at order=3 by default. WebSecurityConfigurerAdapter has an #Order(100) annotation. So first the ResourceServer will be checked (authentication) and then your checks in your enxtension of WebSecurityConfigureAdapter will be checked.
Your configuration looks sane (the login chain takes precedence, but only matches a small set of requests).

Resources