Spring Authentication failure after database restore - java

EDIT: This issue turns out to have been user error, the passwords had been changed, so the hash was never going to match
I'm using a simple authentication in Spring 4.2.2's toolkit, using a DAO which reads a (Postgres) database table for user's name, password, and authorities
#EnableWebSecurity
#Configuration
class X extends WebSecurityConfigurerAdapter{
...
#Autowired
private SessionRegistry sessionRegistry;
#Autowired
private SessionAuthenticationStrategy sessionAuthenticationStrategy;
#Override
protected void configure(HttpSecurity http){
http.sessionManagement().sessionAuthenticationStrategy(sessionAuthenticationStrategy).maximumSessions(1).sessionRegistry(sessionRegistry).expiredUrl("/login.jsp");
//presumably unrelated additional code related to matchers, roles, https
}
#Bean
public SessionRegistry sessionRegistry(){
return new SessionRegistryImpl();
}
#Bean
public SessionAuthenticationStrategy sessionAuthenticationStrategy(){
return new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry);
}
#Bean
public PasswordEncoder passwordEncoder(){
return new StandardPasswordEncoder();
}
...
}
Recently I restored an old copy of the database, the old database comes from a Redhat 6 server, the new one is CentOS 7, though realistically since this is all database backed, it shouldn't matter. The authentication portion of our code hasn't changed at all, but since I restored the database despite entering the correct credentials I get
BadCredentialsException: Bad credentials at
org.springframework.security.authentication.dao.DaoAuthenticationProvider.additionalAuthenticationChecks(DaoAuthenticationProvider.java:98) at
org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:165) at org.springframework.security.authentication.ProvideManager.authenticate(ProviderManager.java:167) at
....
The rest of the stack trace is all more standard spring/catalina/java bits of the stack trace, nothing custom.
It's not expired, I've deleted cookies, it's not disabled....
This code hasn't changed in literal years neither have the backing database tables or Spring libraries. Debugging I can confirm that the correct user is retrieved by username as it goes towards authentication that the user object is correctly constructed with the password hash and authorities. Since so much of this is done by the default behavior of Spring classes, I can't step through very much of the code as it happens so it's very difficult to identify where the actual bad credentials have occurred and what the heck might have changed.
Googling around, I find many users have problems, but almost of them deal with initial bad configuration. This isn't such a problem, since this code used to work.
Are there any known gotchas related to Spring security that I might test?
Failing that, how can I triage this further?

Some more things to try:
Verify that the database collation and character-set matches across both old and new environments
Do you have a way within your application (or could you write some simple code to) reset a password then try and login with the newly-reset password (or just write some quick code to generate a new one that you know is correct and then set it directly against the database)? If that works, have a look at the format of the new password against the others in the database.
Wire in your own custom DaoAuthenticationProvider that enables you to set a breakpoint (or just logs to the console/file) the generated vs. database password hash
Implement your own custom StandardPasswordEncoder (from here), but again add more logging/breakpoints

Related

How to get current user authentication inside UserDetailsService

In my application I am trying to unite ActiveDirectory authentication with OAuth2 refresh tokens.
I was able to successfully authenticate via ActiveDirectoryLdapAuthenticationProvider. I have also provided my custom implementation of LdapUserDetailsMapper that populates the UserDetails with some custom attributes taken from ActiveDirectory. Key thing here is that these attributes have a confidentialty flag set on them and are only available to the user itself (i.e. authenticated user could read the values of these attributes for himself but not for the others). These attributes are stored in Authentication object and are used by an application in a context of an authenticated user.
Things get tricky when I try to add refresh tokens to the picture. Refresh tokens require me to implement a UserDetailsService where I have to provide new UserDetails having just a user name. This is not feasible due to confidentialty flag. Even if I have some master account in my application with the ability to browse ActiveDirectory I will not be able to retrieve the confidential attributes.
So I would rather prefer to provide more atomic implementations like the function that checks if the user is still active or the function that provides a renewed set of user authorities. Unfortunately I did not find this level of atomicity in Spring Security. So it looks like for refresh tokens I have to provide an implementation of UserDetailsService.
If I have to provide new user details I would like to have an access to previous user Authentication object. In this case I will check the user and if it is still active I will copy all the confidential information from previous Authentication. The problem is that it does not seem to be available. At the moment when UserDetailsService::loadUserByUsername() is called SecurityContextHolder.getContext() does not contain the user authentication. Authentication is also not available from UserDetailsService API - I only get the user name. At the same time user's Authentication object is present just one stack frame up in UserDetailsByNameServiceWrapper class:
public UserDetails loadUserDetails(T authentication) throws UsernameNotFoundException {
return this.userDetailsService.loadUserByUsername(authentication.getName());
}
The least thing I want to do here is to implement some in-memory storage for all user confidential information to be used whenever I need to provide new UserDetails. I already have all the required information in user authentication managed by Spring and doing this on my end seems to be just surplus.
And here comes question list:
If you feel that I am doing something terribly wrong from the perspective of application security architecture, please tell me
Is there a way to tell Spring during refresh token procedure to use previous UserDetails object so that application could just answer the question if the user is still active and should be issued a new access token (and not provide the UserDetailsService at all)?
Is there a way to get previous user Authentication object during the call to UserDetailsService::loadUserByUsername() so that I could use it as a source of confidential info?
Is there some other approach that I do not see at the moment to add refresh tokens to my application?
Update:
Here I saw a comment that you could implement your own AuthenticationUserDetailsService to work around the problem. This I do not see how to do. It is hardcoded in AuthorizationServerEndpointsConfigurer that it always creates an instance of UserDetailsByNameServiceWrapper so to provide your own implementation you would have to interfere into AuthorizationServerEndpointsConfigurer initialization process.
OK, looks like the answer with Spring Security 4.0 is you can't.
So I had to apply the following hack which works, but I do not like it very much. Since it works I am posting it here. Since it does not solve the original problem, but works around it I will not mark it as accepted by the author.
Switch to JWT tokens
Use custom TokenEnhancer to inject all information that is required to recreate the user (user secret in my case) to the token directly. Of course, the value must be encrypted by the server with symmetrical crypto algorithm before adding it to the token.
Instruct authorization server to use custom AccessTokenConverter. This implementation of AccessTokenConverter would extract the secret value from the token, decrypt it and put it to ThreadLocal field.
Instruct custom UserDetailsService to retrieve the user secret from the ThreadLocal field set in step 3. This is the best way I found so far to deliver the current authorization context to UserDetailsService. And this is the part that I do not like most in my solution.
Use custom security filter to erase the value set in step 3 from ThreadLocal field.
P.S. I still do not see the possibility to implement custom AuthenticationUserDetailsService that was mentioned earlier. If such possibility exists it could have been another way to solve the problem.
Some useful links:
Extending Spring Security OAuth for Multi-Tenant
Detailed explanation of the problem in spring-security-oauth GitHub
I've got the response from Joe Grandja on spring-security-oauth github page.
Posting it here since it actually provides an answer to the original question.
Hi #masm22. To help with question 1 and 2, below is a custom configuration that will allow you to hook into the refresh_token grant and provide your own behaviour or delegate to super to proceed with current behaviour. It will also allow you to access the user Authentication so you can read your custom (confidential) attributes.
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
..... // other config
#Autowired
private ClientDetailsService clientDetailsService;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenServices(this.customTokenServices());
}
private DefaultTokenServices customTokenServices() {
DefaultTokenServices tokenServices = new CustomTokenServices();
tokenServices.setTokenStore(new InMemoryTokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setReuseRefreshToken(true);
tokenServices.setClientDetailsService(this.clientDetailsService);
return tokenServices;
}
private static class CustomTokenServices extends DefaultTokenServices {
private TokenStore tokenStore;
#Override
public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest) throws AuthenticationException {
OAuth2RefreshToken refreshToken = this.tokenStore.readRefreshToken(refreshTokenValue);
OAuth2Authentication authentication = this.tokenStore.readAuthenticationForRefreshToken(refreshToken);
// Check attributes in the authentication and
// decide whether to grant the refresh token
boolean allowRefresh = true;
if (!allowRefresh) {
// throw UnauthorizedClientException or something similar
}
return super.refreshAccessToken(refreshTokenValue, tokenRequest);
}
#Override
public void setTokenStore(TokenStore tokenStore) {
super.setTokenStore(tokenStore);
this.tokenStore = tokenStore;
}
}
}
The other thing I want to point out for your information is in DefaultTokenServices.refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest)
has the following code:
OAuth2Authentication authentication = tokenStore.readAuthenticationForRefreshToken(refreshToken);
if (this.authenticationManager != null && !authentication.isClientOnly()) {
// The client has already been authenticated, but the user authentication might be old now, so give it a
// chance to re-authenticate.
Authentication user = new PreAuthenticatedAuthenticationToken(authentication.getUserAuthentication(), "", authentication.getAuthorities());
user = authenticationManager.authenticate(user);
Object details = authentication.getDetails();
authentication = new OAuth2Authentication(authentication.getOAuth2Request(), user);
authentication.setDetails(details);
}
The user is being re-authenticated. Possibly something you may want to do in your custom implementation if need be.

log out from Oauth2 server AND from all clients

We have a java web app, which contains a lot of wars. We have an Oauth2 server(written by us) and we will have a lot clients( around 8). All of this will be under the same domain. Except of this we have another app( running on completely different tomcat. There a Liferay is used). The idea is that that the user will use them as they are using one app and they should not see big difference.
This is way now what I need is that when I log out from one place in some way to say the oauth2 server and all other clients to log out, too.
Because for client should be : I already logged out why in some parts I'm still logged in?
Currently I'm not sure how to do it.
And to a lot of places I read that normally this is not the practice.
Can you give me hints and explain me from where I can start? Maybe to use Oauth2 in my case in not the best choice?
For your requirement, you can implement OAuth2 using JDBC Token Store from Spring Security. For this to work once user logs out, all client should invoke your Delete token API where you can remove the Access Token
#FrameworkEndpoint
public class RevokeTokenEndpoint {
#Resource(name = "tokenServices")
ConsumerTokenServices tokenServices;
#RequestMapping(method = RequestMethod.DELETE, value = "/oauth/token")
#ResponseBody
public void revokeToken(HttpServletRequest request) {
//Delete the Token
}
}
Also, you should delete the refresh token.This way, the token would be invalidated once user logs-out and subsequent client can no longer use the same token

spring security manual login best practice

I'm using spring security to implement a programmatic, manual user login. I have a scenario where I have positively established the user's identity, and wish to log them in. I don't know their password, and so can't use the regular login code path where you submit a form to a url, which spring intercepts via a servlet Filter, doing all of it's auth+session magic.
I've searched, and it seems most people create their own Authentication object, and then tell spring about via:
PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(user, "", user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
Indeed, this works. Spring even puts it into the session for me, making subsequent http requests maintain their auth status.
However, I feel like this is a dirty hack. I'll present some details that I hope will give concrete examples of the problems associated with using setAuthentication() inside a controller to achieve a manual login:
To give an idea, my config is:
httpSecurity
.authorizeRequests()
.antMatchers("/test/**").permitAll()
.antMatchers("/admin/**", "/api/admin/**").hasRole("USER_SUPER_ADMIN")
.and()
.formLogin()
.loginPage("/sign-in?sp")
.loginProcessingUrl("/api/auth/sign-in")
.successHandler(createLoginSuccessHandler())
.failureHandler(createLoginFailureHandler())
.permitAll()
.and()
.logout()
.logoutUrl("/api/auth/sign-out")
.logoutSuccessHandler(createLogoutSuccessHandler())
.and()
.sessionManagement()
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
.sessionRegistry(sessionRegistry)
;
Key points in the above config:
I use custom success and failure handlers for the form login
I want to config behavior for max concurrent sessions per user
I want to maintain spring's default session fixation protection (changing session id upon login).
I want to use a session registry
... more of these session/login functionalities, had I chosen to config it.
I stepped through the code to see how spring processes a form login. As expected, Spring does all the session/login functionalities that my HttpSecurity config told it to do when I use the form login. But, when I do my own custom/manual login via SecurityContextHolder.getContext().setAuthentication(), it does NONE of those functionalities. This is because spring does all of it's session/login functionalities stuff inside of a servlet Filter, and my programmatic code can't really call a Filter. Now, I can attempt to add the missing functionalities myself, duplicating their code: I see that the spring Filter uses: ConcurrentSessionControlAuthenticationStrategy, ChangeSessionIdAuthenticationStrategy, and RegisterSessionAuthenticationStrategy. I can create these objects myself, configure them, and call them after my custom login. But, that's really lame to duplicate all that spring code. Furthermore, there's still other behaviors I'm missing - I noticed that when using the form login code path, that spring triggers some login events which don't get triggered when I do my custom login. And there's probably other stuff that I'm missing or don't understand. The whole process is pretty complicated, and I feel like there's a high chance of introducing bugs if this isn't done right, not to mention that library updates would be a pain if I started duplicating spring code.
So, I feel like I'm approaching this from the wrong way. Should I be using a different strategy, so that I'm not bypassing so much of the stuff that spring does for me? Maybe I should try to make my own AuthenticationProvider to accomplish this custom login?
*To clarify, my code more or less works. But, I feel like I accomplished it using a poor strategy because I had to write code duplicating a lot of stuff that spring does for me. Further, my code doesn't perfectly replicate what spring does, making me wonder what negative implications might result. There must be a better way to programatically achieve login.
I wanted to elaborate on how I implemented the advice of dur. In my scenario, I only used a custom AuthenticationProvider.
Instead of creating a custom servlet Filter, such as extending AbstractAuthenticationProcessingFilter, which seemed like a lot of work, I choose to instead use the following strategy:
At the point in my code where I was confident that I had identified the user, and wanted them to be "logged in", I stuck a flag in the user's session, marking that they should be logged in on the next request, along with any other identity/bookkeeping info I needed, such as their username.
Then, I told the browser client to make an http post to the loginProcessingUrl (the same one I configured spring security to use for form-based login), telling them to send the standard username and password form params, although they don't need to send real values - dummy values like foo are fine.
When the user makes that post request (eg to /login), spring will invoke my custom AuthenticationProvider, which will look in the user's session to check for the flag, and to gather the username. Then it will create and return an Authentication object, such as PreAuthenticatedAuthenticationToken, which identifies the user.
Spring will handle the rest. The user is now logged in.
By doing it this way, you stay within the "normal" way of doing logins, and so spring will still automatically:
Call any custom success and failure handlers you configured for the form login, which is nice if you use that place to do certain things on login, like query or update a db.
It will respect any max concurrent sessions per user settings that you may be using.
You get to keep spring's default session fixation attack protection (changing session id upon login).
If you set a custom session timeout, eg via server.session.timeout in a properties file, spring will use it. There's probably other session config attributes that are done at this time too.
If you enabled spring's "remember me" functionality, it will work.
It will fire a login event, which is used for other spring components, such as storing the user's session in a SessionRegistry. I think the events are also used by other parts of spring, such as the actuator, and for auditing.
When I first tried just doing the typically recommended SecurityContextHolder.getContext().setAuthentication(authentication) to login my user, instead of the custom AuthenticationProvider, none of the above bullets were done for me, which can utterly break your app... or cause subtle security bugs - neither are good.
Here's some code to help solidify what I said:
Custom AuthenticationProvider
#Component
public class AccountVerificationAuthenticationProvider implements AuthenticationProvider {
#Autowired
private AppAuthenticatedUserService appAuthenticatedUserService;
#Autowired
private AuthService authService;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// This will look in the user's session to get their username, and to make sure the flag is set to allow login without password on this request.
UserAccount userAccount = authService.getUserAccountFromRecentAccountVerificationProcess();
if (userAccount == null) {
// Tell spring we can't process this AuthenticationProvider obj.
// Spring will continue, and try another AuthenticationProvider, if it can.
return null;
}
// A service to create a custom UserDetails object for this user.
UserDetails appAuthenticatedUser = appAuthenticatedUserService.create(userAccount.getEmail(), "", true);
PreAuthenticatedAuthenticationToken authenticationToken = new PreAuthenticatedAuthenticationToken(appAuthenticatedUser, "", appAuthenticatedUser.getAuthorities());
authenticationToken.setAuthenticated(true);
return authenticationToken;
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
Config spring security to use the provider
// In your WebSecurityConfigurerAdapter
#Configuration
#EnableWebSecurity
public class AppLoginConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AccountVerificationAuthenticationProvider accountVerificationAuthenticationProvider;
#Autowired
private ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider;
#Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
// Spring will try these auth providers in the order we register them.
// We do the accountVerificationAuthenticationProvider provider first, since it doesn't need to do any slow IO to check,
// so it's very fast. Only if this AuthenticationProvider rejects (which means this http request is not for programmatic login), will spring then try the next AuthenticationProvider in the list.
authenticationManagerBuilder
.authenticationProvider(accountVerificationAuthenticationProvider)
// I'm using ActiveDirectory / LDAP for when a user logs in via entering a user + password via the html form, but whatever you want to use here should work.
.authenticationProvider(activeDirectoryLdapAuthenticationProvider);
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
...
}
}
For custom web authentication you should implement combination of a custom authentication filter (for example AbstractAuthenticationProcessingFilter or just GenericFilterBean), a custom authentication provider (AuthenticationProvider) or/and custom authentication token (AbstractAuthenticationToken).
For example, see source of Spring Security Kerberos.
See also:
The AuthenticationManager, ProviderManager and AuthenticationProvider

Best way to instrument Spring Boot/Java Config Application

I'd like to keep track of certain things in a Spring Boot/OAuth2/Java Config setting and report those somewhere (think AWS CloudWatch, or Google Analytics, or any other similar services).
To be more precise, I have an Authorization+Resource Server with Spring OAuth2.
Those things I'd like to track would be (though not limited to):
Login failed attempts (for clients and users)
Login successful attempts (for clients and users)
Usage of REST controllers (like "/say_hello_world")
Exceptions (aside from the authentication ones)
I was planning on adding javax.servlet.Filters, but then it gets hairy when logging failed attempts with my Authorization Server (using #EnableAuthorizationServer and extending AuthorizationServerConfigurerAdapter). I think I'd need to either resort to using my custom Exception Translator or figure out how to set/wrap the AuthenticationManager in ClientCredentialsTokenEndpointFilter.
Is there a better way than wrapping a tons of things to collect the info I mentioned above?
Update:
As I mentioned in the comments, I'm not looking for a "log dump". I need to be able to get for example the user id that tried to login but failed, or the invalid access token that was used, etc.
I looked into coding my own ApplicationListener<ApplicationEvent>, detect AuthenticationFailureBadCredentialsEvent and the like and go that route.
For example, I can detect a BadCredentialsException, but then need to figure out if it's an InvalidTokenException or other (which is in the cause of that BadCredentialsException).
Next problem is that I can't extract the access token that was used and failed. Feels awkward and more hacking than it should.
I don't mind going through hoops like those, just wondered if there was a better way.
Update2:
There is one thing that helps "listening" to what's going on in a Spring application and it's the ApplicationListener.
Any events published can be caught by providing an implementation of that interface.
InteractiveAuthenticationSuccessEvent is published when a client successfully authenticates (i.e. clientId exists and secret key is valid)
AuthenticationSuccessEvent is published when a user successfully authenticates (i.e. username exists and password matches)
AuthenticationFailureBadCredentialsEvent is published when a user failed authenticating.
There was a problem with #2 and #3 in that in a ProviderManager a NullEventPublisher is setup by default so I had to change my (Java) config like so to get those authentication events:
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
...
#Autowired
private AuthenticationEventPublisher authenticationEventPublisher;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationEventPublisher(authenticationEventPublisher)
...
}
...
}
Now there remains the problem of receiving the event when the client failed authentication.
The InteractiveAuthenticationSuccessEvent is triggered by AbstractAuthenticationProcessingFilter (which ClientCredentialsTokenEndpointFilter extends) upon successful authentication. But when authentication fails, it does not publish any event (since at least version 3.0.0 to 4.0.1).
Another way would be to configure the AuthenticationManager the same way I did above by setting up a non-NullEventPublisher but as far as I can tell there's no way to setup ClientCredentialsTokenEndpointFilter in Spring Boot without manually setting up the universe???
You can use AOP in spring. Create advice #Before, #After, #Arround execution of method, or even AfterThrowing to detect any exception occured.
Using this approach, you can separate your auditing process from your business logic.

Spring Security userCache invalidation

Using Spring Security I have a DaoAuthenticationProvider described like here:
http://static.springsource.org/spring-security/site/docs/2.0.x/reference/dao-provider.html
I also have caching (also like it's described in that article).
The problem is that when a request comes in with a good username (that is already in the cache), but a bad password - it returns the user from the cache as if it is a good username/password. Because it uses the username as the key, the password is not involved at all.
The exact code that returns the user from the cache:
UserDetails user = this.userCache.getUserFromCache(username);
Did anybody ever dealt with this problem before? I can also check if the password is the same, but it would be a custom thing.
Thank you.
If you configured your application with the standard components, the scenario should be as follows:
At user request arrival the Authentication object is created and populated with username and password supplied by user.
User details are retrieved: if it's possible, UserCache is used to retrieve previously cached user details (i.e. getUserFromCache is called either by implementations of UserDetailsService or AuthenticationProvider before the call to AuthenticationManager is performed). And it is 100% OK that the user details from cache will come with the good password.
After basic pre-authentication checks (credentials expiration etc.) the actual authentication occurs. At this point the password from cached user details is compared to the password stored in Authentication object supplied (which currently contains the wrong password). At this point authentication attempt fails.
However, if you implement your own AuthenticationProvider or AuthenticationManager, you are responsible for password checking.
What's the code that originally gets the user from the DB and caches it? Does it check the password? Sounds like you have an abstraction issue - Spring Security should not know where the user is coming from (DB or Cache) and should use the same logic either way.

Categories