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 previousUserDetails
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 theUserDetailsService
at all)? - Is there a way to get previous user
Authentication
object during the call toUserDetailsService::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.