How to get Authentication inside UserDetailsService subclass - spring

I was using custom auth like this
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException{
LoggedInUser user = new LoggedInUser();
...
...
return new UsernamePasswordAuthenticationToken(user, authentication.getCredentials());
}
here i was setting some value to authentication object
Now i am using password-encoder so my custom authentication is
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserService userService;
static final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
#Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
}
Here inside loadUserByUsername how can i set anything to Authentication object ?

UserDetailsService implementations should be responsible for retrieving user details based on provided ID and not to manipulate Authentication. To process a specific Authentication object, implement custom AuthenticationProvider or extend an existing one (e.g. DaoAuthenticationProvider) that will delegate to your UserDetailsService to retrieve a user. Such a user can be then assigned to the authentication object and returned from AuthenticationProvider.authenticate(...) method.
Look at Spring Security's DaoAuthenticationProvider that is implemented this way applying the single responsibility and separation of concerns principles.

Related

Required a bean of type 'org.springframework.security.authentication.AuthenticationManager' that could not be found. message from spring security

I am trying to implement one sample demo for Spring Security with Spring Boot for checking the authentication. I am trying to implement a basic workout for Spring Security and getting the following message,
Description:
Parameter 0 of constructor in com.spacestudy.service.CustomAuthenticationProvider required a bean of type 'org.springframework.security.authentication.AuthenticationManager' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.security.web.AuthenticationEntryPoint' in your configuration.
My security config class SecurityConfig.java,
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationEntryPoint authEntryPoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilter(new ApplicationContextHeaderFilter((ApplicationContext) authenticationManager()));
}
}
And my BasicAuthenticationFilter implementation like the following,
#Component
public class CustomAuthenticationProvider extends BasicAuthenticationFilter {
public CustomAuthenticationProvider(AuthenticationManager authenticationManager) {
super(authenticationManager);
// TODO Auto-generated constructor stub
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String bearerToken = request.getHeader("accessToken");
String username = "test";
String password = "test";
if (username != null && !username.isEmpty()) {
return new UsernamePasswordAuthenticationToken(username, null, null);
}
return null;
}
}
How can I resolve this issue?
Lot of problems are there in your code.
(ApplicationContext) authenticationManager()
you can not cast AuthenticationManager to ApplicationContext
.addFilter(new ApplicationContextHeaderFilter(...))
I don't know Why you are using ApplicationContextHeaderFilter for simple demo application.
You should have preferred BasicAuthenticationFilter or even simple default configuration provided for HttpSecurity with .httpBasic()
You should have preferred UsernamePasswordAuthenticationFilter or even simple default configuration provided in HttpSecurity with .formLogin()
CustomAuthenticationProvider extends BasicAuthenticationFilter
An authentication provider is one which implements AuthenticationProvider interface. In your case naming should be xxxAuthFilter.
You have done nothing in below code.(got existing authentication object and set it back without creating an valid authentication object.)
UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
SecurityContextHolder.getContext().setAuthentication(authentication);
Coming to the AuthenticationManager and AuthFilters implementation point of view,
For add filter you can add any implementation of Spring Security provided filters as given below
.addFilter(AnyFilterImplementationFromThisLink)
(But not all filters are auth filters. Where auth filters will attempt to authenticate with the authenticationManager configured)
For example If you consider UsernamePasswordAuthenticationFilter or BasicAuthenticationFilter
you should take care of setting AuthenticationManager where your auth manager should override authenticate() method and it should return Authentication object(where Authentication object will have auth principal, credentials and granted authorities list)
Or
If you don't want to implement authentication manager...
In simple way in your filters(implementation of OncePerRequestFilter) doFilterInternal() method set the `Authentication` object in `SecurityContext`
List<GrantedAuthority> authorityList = new ArrayList<>();
GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_USER");
authorityList.add(authority);
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(username, password, authorityList);
//Note UsernamePasswordAuthenticationToken implements Authentication
SecurityContextHolder.getContext().setAuthentication(authToken);
How any auth filter works is if there is a valid Authentication object then filter chain will continue without attempting authentication otherwise it will attemptAuthentication by overrided attemptAuthentication() method.
But your ApplicationContextHeaderFilter is a implementation of OncePerRequestFilter where it has no attemptAuthentication() and i don't know the order of ApplicationContextHeaderFilter if it's order is after creating security context then you can set the authentication object to security context.
Your error seems to be that the AuthenticationManager is not present as a Spring Bean.
Option 1
Register an AuthenticationManager in Spring Bean. All is provided by Spring for do this directly in your SecurityConfig class by overriding the WebSecurityConfigurerAdapter#authenticationManagerBean method like explain in the documentation of it
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
Option 2
Avoid to register an AuthenticationManager in Spring, but directly your CustomAuthenticationProvider classs.
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public CustomAuthenticationProvider authenticationProvider() throws Exception {
return new CustomAuthenticationProvider(authenticationManager());
}
}
Don't forget to remove the #Component annotation on the CustomAuthenticationProvider class with this method.
I am not sure but shoudn't the CustomAuthenticationProvider implement AuthenticationProvider and AuthenticationManager is just a container for authentication providers and it seems that you dont have any.
Check this site for more info
https://www.baeldung.com/spring-security-authentication-provider
You can try on this, put it in config security file
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
Create a new java class. And Configure like below:
#Configuration
#RequiredArgsConstructor
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception{
return super.authenticationManagerBean();
}
}

x509 validation fails before it can be captured

I have a Spring Boot application, using x509 authentication which further validates users against a database. When a user accesses the site, internal Spring code calls the loadUserByUsername method which in turn makes the database call. This all happens before the controller is aware anything is happening. If the user is not found it throws an EntityNotFoundException and displays the stack trace on the user's browser.
I'm using Spring Boot Starter. The controller has code to capture the exception and return a 'Not Authorized' message, but this happens long before. Has anyone else seen this and do you have a workaround?
#Service
public class UserService implements UserDetailsService {
public UserDetails loadUserByUsername(String dn) {
ApprovedUser details = auService.getOne(dn);
if (details == null){
String message = "User not authorized: " + dn;
throw new UsernameNotFoundException(message);
}
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
if (details.isAdminUser()){
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN_USER"));
}
return new AppUser(dn, "", authorities);
}
Usually, you would use a AuthenticationFailureHandler to encapsulate logic that's triggered by an AuthenticationException. The X509AuthenticationFilter would usually call PreAuthenticatedAuthenticationProvider to authenticate, which would in turn invoke the loadUserByUsername(...) method from UserDetailsService. Any AuthenticationException thrown by the UserDetailsService is caught by the filter and control is passed to the registered AuthenticationFailureHandler. This includes UsernameNotFoundException.
However, if you're using the X509Configurer, (http.x509()) there is no way of setting a handler directly on the filter. So once the exception is thrown, X509AuthenticationFilter catches it, sees that there's no default handler, and then simply passes the request to the next filter in the filter chain.
One way to get around this could be to provide a custom X509AuthenticationFilter.
In WebSecurityConfigurerAdapter:
#Autowired
private AuthenticationFailureHandler customFailureHandler;
#Autowired
private UserService customUserService;
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
protected void configure(HttpSecurity http) throws Exception {
...
http.x509().x509AuthenticationFilter(myX509Filter())
.userDetailsService(customUserService)
...
}
private X509AuthenticationFilter myX509Filter() {
X509AuthenticationFilter myCustomFilter = new X509AuthenticationFilter();
myCustomFilter.setAuthenticationManager(authenticationManagerBean());
...
myCustomFilter.setContinueFilterChainOnUnsuccessfulAuthentication(false);
myCustomFilter.setAuthenticationFailureHandler(customFailureHandler);
return myCustomFilter;
}
Then you can write your own AuthenticationFailureHandler implementation and expose it as a bean.

Where is better to put my custom authentication logic?

I want to add a bit of logic to my authentication in Spring Boot, check if an account have a specific logic, for example if a date in its account is before the current date.
Where is best placed in a custom filter or in UserDetailsService?
If it's in a filter, is better to extends from any spring class?
Explanation
As you can see bellow I use a custom userDetailsService() to get the users details (CuentaUser) in which there are the fields needed for the logic (for example the expiration date). So now I need to add the logic and comes to me two places where I can put it: in UserDetailsServices (throwing an exception if the logic fails) or as a custom filter.
Where is better to put my custom the authentication logic?
This is my actual security configuration:
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CuentaRepository accountRepository;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
}
#Bean
public UserDetailsService userDetailsService() {
return (username) -> accountRepository.findByUsuario(username)
.map(a -> new CuentaUser(a, AuthorityUtils.createAuthorityList("USER", "write")))
.orElseThrow(() -> new UsernameNotFoundException("could not find the user '" + username + "'"));
}
#Override
protected void configure(HttpSecurity http) throws Exception {
CsrfTokenResponseHeaderBindingFilter csrfTokenFilter = new CsrfTokenResponseHeaderBindingFilter();
http.addFilterAfter(csrfTokenFilter, CsrfFilter.class);
http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
}
Edit: I found that for the example of expiration date, UserDetails have an attribute for it, so is better to use it. Anyway you need to check it with a custom AuthenticationProvider if you don't use the default.
You can use an AuthenticationProvider and put the login inside it.
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
You can see more here:
http://www.baeldung.com/spring-security-authentication-provider

Add custom UserDetailsService to Spring Security OAuth2 app

How do I add the custom UserDetailsService below to this Spring OAuth2 sample?
The default user with default password is defined in the application.properties file of the authserver app.
However, I would like to add the following custom UserDetailsService to the demo package of the authserver app for testing purposes:
package demo;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.stereotype.Service;
#Service
class Users implements UserDetailsManager {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
String password;
List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER");
if (username.equals("Samwise")) {
auth = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_HOBBIT");
password = "TheShire";
}
else if (username.equals("Frodo")){
auth = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_HOBBIT");
password = "MyRing";
}
else{throw new UsernameNotFoundException("Username was not found. ");}
return new org.springframework.security.core.userdetails.User(username, password, auth);
}
#Override
public void createUser(UserDetails user) {// TODO Auto-generated method stub
}
#Override
public void updateUser(UserDetails user) {// TODO Auto-generated method stub
}
#Override
public void deleteUser(String username) {// TODO Auto-generated method stub
}
#Override
public void changePassword(String oldPassword, String newPassword) {
// TODO Auto-generated method stub
}
#Override
public boolean userExists(String username) {
// TODO Auto-generated method stub
return false;
}
}
As you can see, this UserDetailsService is not autowired yet, and it purposely uses insecure passwords because it is only designed for testing purposes.
What specific changes need to be made to the GitHub sample app so that a user can login as username=Samwise with password=TheShire, or as username=Frodo with password=MyRing? Do changes need to be made to AuthserverApplication.java or elsewhere?
SUGGESTIONS:
The Spring OAuth2 Developer Guide says to use a GlobalAuthenticationManagerConfigurer to configure a UserDetailsService globally. However, googling those names produces less than helpful results.
Also, a different app that uses internal spring security INSTEAD OF OAuth uses the following syntax to hook up the UserDetailsService, but I am not sure how to adjust its syntax to the current OP:
#Order(Ordered.HIGHEST_PRECEDENCE)
#Configuration
protected static class AuthenticationSecurity extends GlobalAuthenticationConfigurerAdapter {
#Autowired
private Users users;
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(users);
}
}
I tried using #Autowire inside the OAuth2AuthorizationConfig to connect Users to the AuthorizationServerEndpointsConfigurer as follows:
#Autowired//THIS IS A TEST
private Users users;//THIS IS A TEST
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.accessTokenConverter(jwtAccessTokenConverter())
.userDetailsService(users)//DetailsService)//THIS LINE IS A TEST
;
}
But the Spring Boot logs indicate that the user Samwise was not found, which means that the UserDetailsService was not successfully hooked up. Here is the relevant excerpt from the Spring Boot logs:
2016-04-20 15:34:39.998 DEBUG 5535 --- [nio-9999-exec-9] o.s.s.a.dao.DaoAuthenticationProvider :
User 'Samwise' not found
2016-04-20 15:34:39.998 DEBUG 5535 --- [nio-9999-exec-9]
w.a.UsernamePasswordAuthenticationFilter :
Authentication request failed:
org.springframework.security.authentication.BadCredentialsException:
Bad credentials
What else can I try?
I ran into a similar issue when developing my oauth server with Spring Security. My situation was slightly different, as I wanted to add a UserDetailsService to authenticate refresh tokens, but I think my solution will help you as well.
Like you, I first tried specifying the UserDetailsService using the AuthorizationServerEndpointsConfigurer, but this does not work. I'm not sure if this is a bug or by design, but the UserDetailsService needs to be set in the AuthenticationManager in order for the various oauth2 classes to find it. This worked for me:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
Users userDetailsService;
#Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// other stuff to configure your security
}
}
I think if you changed the following starting at line 73, it may work for you:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
You would also of course need to add #Autowired Users userDetailsService; somewhere in WebSecurityConfigurerAdapter
Other things I wanted to mention:
This may be version specific, I'm on spring-security-oauth2 2.0.12
I can't cite any sources for why this is the way it is, I'm not even sure if my solution is a real solution or a hack.
The GlobalAuthenticationManagerConfigurer referred to in the guide is almost certainly a typo, I can't find that string anywhere in the source code for anything in Spring.
My requirement was to get a database object off the back of the oauth2 email attribute. I found this question as I assumed that I need to create a custom user details service. Actually I need to implment the OidcUser interface and hook into that process.
Initially I thought it was the OAuth2UserService, but I've set up my AWS Cognito authentication provider so that it's open id connect..
//inside WebSecurityConfigurerAdapter
http
.oauth2Login()
.userInfoEndpoint()
.oidcUserService(new CustomOidcUserServiceImpl());
...
public class CustomOidcUserServiceImpl implements OAuth2UserService<OidcUserRequest, OidcUser> {
private OidcUserService oidcUserService = new OidcUserService();
#Override
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
OidcUser oidcUser = oidcUserService.loadUser(userRequest);
return new CustomUserPrincipal(oidcUser);
}
}
...
public class CustomUserPrincipal implements OidcUser {
private OidcUser oidcUser;
//forward all calls onto the included oidcUser
}
The custom service is where any bespoke logic can go.
I plan on implementing UserDetails interface on my CustomUserPrincipal so that I can have different authentication mechanisms for live and test to facilitate automated ui testing.
I ran into the same issue and originally had the same solution as Manan Mehta posted. Just recently, some version combination of spring security and spring oauth2 resulted in any attempt to refresh tokens resulting in an HTTP 500 error stating that UserDetailsService is required in my logs.
The relevant stack trace looks like:
java.lang.IllegalStateException: UserDetailsService is required.
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$UserDetailsServiceDelegator.loadUserByUsername(WebSecurityConfigurerAdapter.java:463)
at org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper.loadUserDetails(UserDetailsByNameServiceWrapper.java:68)
at org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider.authenticate(PreAuthenticatedAuthenticationProvider.java:103)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174)
at org.springframework.security.oauth2.provider.token.DefaultTokenServices.refreshAccessToken(DefaultTokenServices.java:150)
You can see at the bottom that the DefaultTokenServices is attempting to refresh the token. It then calls into an AuthenticationManager to re-authenticate (in case the user revoked permission or the user was deleted, etc.) but this is where it all unravels. You see at the top of the stack trace that UserDetailsServiceDelegator is what gets the call to loadUserByUsername instead of my beautiful UserDetailsService. Even though inside my WebSecurityConfigurerAdapter I set the UserDetailsService, there are two other WebSecurityConfigurerAdapters. One for the ResourceServerConfiguration and one for the AuthorizationServerSecurityConfiguration and those configurations never get the UserDetailsService that I set.
In tracing all the way through Spring Security to piece together what is going on, I found that there is a "local" AuthenticationManagerBuilder and a "global" AuthenticationManagerBuilder and we need to set it on the global version in order to have this information passed to these other builder contexts.
So, the solution I came up with was to get the "global" version in the same way the other contexts were getting the global version. Inside my WebSecurityConfigurerAdapter I had the following:
#Autowired
public void setApplicationContext(ApplicationContext context) {
super.setApplicationContext(context);
AuthenticationManagerBuilder globalAuthBuilder = context
.getBean(AuthenticationManagerBuilder.class);
try {
globalAuthBuilder.userDetailsService(userDetailsService);
} catch (Exception e) {
e.printStackTrace();
}
}
And this worked. Other contexts now had my UserDetailsService. I leave this here for any brave soldiers who stumble upon this minefield in the future.
For anyone got UserDetailsService is required error when doing refresh token, and you confirm you already have UserDetailsService bean.
try add this:
#Configuration
public class GlobalSecurityConfig extends GlobalAuthenticationConfigurerAdapter {
private UserDetailsService userDetailsService;
public GlobalSecurityConfig(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
}
This is my try and error, maybe not work for you.
By the way, if you give up "guess" which bean will pick by spring, you can extends AuthorizationServerConfigurerAdapter and WebSecurityConfigurerAdapter and config all stuff by yourself, but I think it's lose power of spring autoconfig.
Why I need config everything if I just need customize some config?

Spring Security: Authenticate against multiple LDAP servers & DAO-based authentication

I'm working on a Springboot application which has a requirement to support authentication locally (through a DAO-based provider) and through multiple LDAP servers (administratively configured, stored in the database).
With a single LDAP provider my configure method looks like:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
auth.ldapAuthentication()
.userSearchBase(userSearchBase)
.groupSearchBase(groupSearchBase)
.userSearchFilter(userSearchFilter)
.userDetailsContextMapper(new DaoUserDetailsContextMapper())
.contextSource().url(url+"/"+base)
.managerPassword(managerPassword)
.managerDn(managerDn);
}
Through other similar posts it appears this could be accomplished through creating multiple LDAP providers, and Spring security will cycle through each one until a successful login is found. I have the associated LDAP configuration record associated as a foreign key on the User table.
Is there a more efficient way to try the specific LDAP endpoint associated with the user, or is it best to let Spring iterate through the available providers?
Thanks for any input!
after long search in my way, I found something interesting about how Spring Security authentification works (there is a video : https://youtu.be/caCJAJC41Rk?t=781)
After that, you can use the system that Spring has implemented which is to override the supports(class<?> authenticationClass) method. This method works as "Can you, AuthenticationProvider, manage this kind of AuthenticationClass ?" if true, the provider will try to authenticate the user, if not, never execute any tasks.
In that way, you can implement your own CustomAuthentificationProvider which implements the AuthenticationProvider interface.
public class LocalUserAuthenticationProvider implements AuthenticationProvider {
private final UserRepository;
public LocalUserAuthenticationProvider(UserRepository userRepository){
this.userRepository = userRepository;
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
/*my jobs*/
throws new AuthenticationException("Cannot authenticate");
}
#Override
public boolean supports(Class<?> aClass) {
return MyUsernameAuthenticationToken.class.isAssignableFrom(aClass);
}
}
with your own AuthenticationToken
public class StringUsernameAuthenticationToken extends UsernamePasswordAuthenticationToken {
public StringUsernameAuthenticationToken(Object principal, Object credentials) {
super(principal, credentials);
}
public StringUsernameAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(principal, credentials, authorities);
}
}
Actually I don't find any solutions with the LDAP authentication in the AuthenticationManagerBuilder implemented by Spring Security (ref : https://spring.io/guides/gs/authenticating-ldap/)
Hope this could help people

Resources