Custom Authentication Entrypoint not being called on failed Authentication - spring-boot

I have setup an OAUTH Authorization server that's supposed to allow clients request for tokens. It's also supposed to allow admin users carry out other operations.
In my Web Security Configuration:
#Configuration
#EnableWebSecurity
public class ApiSecurityConfig extends WebSecurityConfigurerAdapter {
private #Autowired CustomAuthenticationProvider authenticationProvider;
private #Autowired CustomAuthenticationEntryPoint entryPoint;
#Override
#Bean
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().httpBasic().and().cors().and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll()
.anyRequest()
.authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(entryPoint)
.defaultAuthenticationEntryPointFor(entryPoint, new AntPathRequestMatcher("/api/v1/**"));
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
}
Ideally, when an admin user tries to call any endpoint under "/api/v1/**", they should be authenticated - and in fact, they are.
The issue now is, when authentication fails, the authentication entry endpoint is ignored. I don't understand why this is.
I even included the "default authentication entry point for" just to see if that would help, but it didn't.
Please, how do I resolve this?

After playing around with the http security configuration, I took inspiration from this article (https://www.baeldung.com/spring-security-basic-authentication) and changed it to:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().cors().and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic().authenticationEntryPoint(entryPoint);
}
Honestly, I don't know why what I had before wasn't working. Plenty of people have posted that as the solution to problems about entry end points. But I guess maybe something has changed in Spring that I'm not aware of.

Related

Springboot configuration 401 Unauthorized

I have this configure method and i want to make user be able to register but i get 401 Unathorized. It is caused by the .apply(**) and i am not able to do it.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/auth/register").permitAll()
.antMatchers("/auth/signin").permitAll()
.anyRequest().authenticated()
.and()
.apply(new JwtConfigurer(jwtTokenProvider, securityUtils));
}
JwtConfigurer.class
public class JwtConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private final JwtTokenProvider jwtTokenProvider;
private final SecurityUtils securityUtils;
public JwtConfigurer(JwtTokenProvider jwtTokenProvider, SecurityUtils securityUtils) {
this.jwtTokenProvider = jwtTokenProvider;
this.securityUtils = securityUtils;
}
#Override
public void configure(HttpSecurity http) {
JwtTokenFilter customFilter = new JwtTokenFilter(jwtTokenProvider, securityUtils);
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
}
}
So when request is sent to /auth/register i dont want to add .apply(**). Do u have any suggestion please?
In your class that extends WebSecurityConfigurerAdapter where your http configure() method with .apply() is written, you can use the following to tell Spring Boot to bypass or ignore the filter if encountered with the uri for user registration.
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(
"/auth/register");
}
Edit: Since you are facing the exception:
Cross-origin Request Blocked (Reason: CORS header ‘Access-Control-Allow-Origin’ missing and Reason: CORS request did not succeed)
it means that your OPTIONS request (preflight request) is failing. Are you using a different project as Front End for your application? If yes, you will need to specify in your spring boot configuration to allow origin and allow the specified methods from that particular origin.
Please refer to this official documentation to learn how to do that. You can enable Cors at Controller level or at global level. This StackOverflow thread should also be helpful in doing the implementation in case you are unable to proceed.

Configure public access, JWT authentication and HTTP basic authentication depending on paths in Spring Security

A Spring Boot application provides some REST endpoints with different authentication mechanisms. I'm trying to setup the security configuration according to the following requirements:
By default, all endpoints shall be "restricted", that is, if any endpoint is hit for which no specific rule exists, then it must be forbidden.
All endpoints starting with /services/** shall be secured with a JWT token.
All endpoints starting with /api/** shall be secured with HTTP basic authentication.
Any endpoint defined in a RESOURCE_WHITELIST shall be public, that is, they are accessible without any authentication. Even if rules #2 or #3 would apply.
This is what I came up with so far but it does not match the above requirements. Could you help me with this?
#Configuration
#RequiredArgsConstructor
public static class ApiSecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final String[] RESOURCE_WHITELIST = {
"/services/login",
"/services/reset-password",
"/metrics",
"/api/notification"
};
private final JwtRequestFilter jwtRequestFilter;
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("some-username")
.password(passwordEncoder().encode("some-api-password"))
.roles("api-role");
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors()
.and()
.csrf().disable()
.formLogin().disable()
// apply JWT authentication
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.mvcMatchers(RESOURCE_WHITELIST).permitAll()
.mvcMatchers("/services/**").authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// apply HTTP Basic Authentication
.and()
.authorizeRequests()
.mvcMatchers("/api/**")
.hasRole(API_USER_ROLE)
.and()
.httpBasic()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}

Spring security - Simple Multi Security now working [duplicate]

This question already has answers here:
Spring Security : Multiple HTTP Config not working
(2 answers)
Closed 1 year ago.
I am dealing with this, since 7 hours ago ,and I cant find an explanation, for simplicity, I just did the example a little smaller.
I need some URLs with security access (JWT), and other path (dashboard) with a form login.
This is my code:
#EnableWebSecurity
public class MultiHttpSecurityConfig {
#Autowired
private UserDetailsService jwtUserDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(jwtUserDetailsService)
.passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Autowired
private JwtRequestFilter jwtRequestFilter;
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
// Get Request and /Authenticate do not need authentication
.authorizeRequests()
.antMatchers("/authenticate", "/authenticate/**").permitAll()
.antMatchers(HttpMethod.GET, "/api/**").permitAll()
// all others do need authentication
.anyRequest().authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
#Configuration
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/dashboard/index.html").authenticated()
.and()
.formLogin();
}
}
This example is working, the JWT mechanism works great.
The only thing it does not work, is the form login. When I hit the browser localhost:8080/dashboard/index.html, the file appears.
This is what I need:
/authorize --> Anyone can hit that URL to get the JWT token
/api --> Get methods do not need authorization
/api --> All others verbs, do need a token.
/dashboard/index.html --> A form login should appear.
I know that anyRequest().authenticated(), it is in the first configuration but if I even comment that line, the second Order is totally ignored.
What should I add or remove to accomplish my idea?
In your FormLoginWebSecurityConfigurerAdapter, the antMatchers() should be called before authorizeRequests() - this indicate that this filter chain only apply request to /dashboard/index.html.
http.antMatcher("/dashboard/index.html")
.authorizeRequests()
.anyRequest().authenticated() // since this filter chain only apply to /dashboard/index.html, don't need use antMatchers() to check again
.and()
.formLogin();
For more info: https://docs.spring.io/spring-security/site/docs/current/reference/html5/#multiple-httpsecurity
The second issue is that the Order of yourFormLoginWebSecurityConfigurerAdapter must be before (less than) ApiWebSecurityConfigurationAdapter. WebSecurityConfigurerAdapter has a default #Order of 100, so you should annotate #Order(0) on your FormLoginWebSecurityConfigurerAdapter.

How to implement Spring Security Ldap authentication using the configurer class correctly?

Hi I'm trying to implement spring's ldap authentication using the WebSecurityConfigurerAdapter class.
So far I can authenticate through the in memory method and even my corp's ldap server, however the latter method I'm only able to authenticate if I pass a hardcoded userDN and password when I create the new context, if I don't create a new context or I don't put the userDN and password, jvm throws me:
Caused by: javax.naming.NamingException: [LDAP: error code 1 - 000004DC: LdapErr: DSID-0C0906E8, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, v1db1\u0000]; Remaining name: '/'
My question is, how can I get the user password and userDN from the login form so I can put it in the context? If that is not possible how can I get the context that the password and userDn are?
This is the code that I have:
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication().userSearchFilter("(&(objectClass=user)(sAMAccountName={0}))")
.groupSearchFilter("(&(memberOf:1.2.840.113556.1.4.1941:=CN=DL - DC859 - MIDDLEWARE,OU=Dyn,OU=Dist,OU=Security Groups,OU=POP,DC=pop,DC=corp,DC=local))")
.contextSource(getLdapContextSource());
}
private LdapContextSource getLdapContextSource() throws Exception {
LdapContextSource cs = new LdapContextSource();
cs.setUrl("ldap://tcp-prd.pop.corp.local:389");
cs.setBase("DC=pop,DC=corp,DC=local");
cs.setUserDn("t8951435#pop.corp.local");
cs.setPassword("mypassword");
cs.afterPropertiesSet();
return cs;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll();
}
}
Thank you.
I've finally figured it out from this post. I still don't know how to set the group filters, but at least now I can bind to the server.
#Bean
public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider("pop.corp.local",
"ldap://tcp-prd.pop.corp.local:389");
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
#Bean
public LoggerListener loggerListener() {
return new LoggerListener();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll();
}
EDIT: I finally found out how to filter by groups. It turns out that they added a setSearchFilter() method in ActiveDirectoryLdapAuthenticationProvider class v3.2.6. As I am using an older version I never knew about this. So I made a copy of the class with the method and just created a buildFilter method to create the filter string that is passed to the setSearchFilter.

Basic security not kicking in with Spring Boot

I'm trying to setup a vanilla Spring Boot environment with Basic authentication.
Basically the only thing I want to customize are the users, the protected paths and a custom password encoder.
The Spring Boot documentation states:
To override the access rules without changing any other autoconfigured
features add a #Bean of type WebConfigurerAdapter with
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER).
Note: I think WebConfigurerAdapter should be WebSecurityConfigurerAdapter.
So I tried the following:
protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter {
#Autowired
private PasswordEncoder passwordEncoder;
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.authorizeRequests()
.antMatchers("/assets/**").permitAll()
.antMatchers("/api/**").hasRole("USER")
.antMatchers("/management/**").hasRole("ADMIN");
// #formatter:on
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// #formatter:off
auth
.inMemoryAuthentication()
.passwordEncoder(passwordEncoder)
.withUser("admin")
.password(passwordEncoder.encode("pwd"))
.roles("USER", "ADMIN")
.and()
.withUser("user")
.password(passwordEncoder.encode("pwd"))
.roles("USER");
// #formatter:on
}
}
The default Boot security seem exactly what I want:
security.enable-csrf=false
security.basic.enabled=true
security.sessions=stateless
However when I run the app the Basic Authentication does not work.
When I configure it explicitly in my WebSecurityConfigurerAdapter using http.httpBasic() like:
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/assets/**").permitAll()
.antMatchers("/api/**").hasRole("USER")
.antMatchers("/management/**").hasRole("ADMIN");
// #formatter:on
}
Then the Basic Authentication is working.
So the initial setup above does not seem to take the default configuration.
Am I missing something?
Every WebSecurityConfigurer has its own filter chain with its own request matchers and security rules. Adding a WebSecurityConfigurer (sorry for the typo in the docs) doesn't change the default boot autoconfig filter chain but it doesn't do anything magic for its own filter chain either. You need to tell Spring Security how to secure those resources - you gave it access rules but no authentication strategy. Makes sense?

Resources