Search code examples
authenticationspring-securityspring-boot

How to update Spring Security UserDetails impls after successful login?


I have a Spring Boot app that has a special requirement whereby users (UserDetails impls) need to have roles/permissions that change on the fly, after they have authenticated.

Does Spring Security allow for this, or will I have to use my own role/permission system independent of Spring?

My understanding with Spring Security is that the UserDetails impl is loaded once at (successful) login and is never updated after that. But it must be possible to update the user/principal on the fly, how else would users edit their username or password when they are logged in? So I assume that if username/password can be modified on the fly, so can roles and granted authorities (permissions).

But for the life of me I cannot find an example of this anywhere online, nor any Javadocs that explain how to do this. Also, would user caching being involved here in any way (that is, does Spring Security cache users, and would this cache need updating as well)?

My best attempt would be something like this:

// My UserDetailsService impl:
public class MyUserDetailsService implements UserDetailsService {
    @Resource
    private UserCache userCache;

    // Lots of stuff up here

    public void updateUserOnTheFly(UserUpdates userUpdates) {
        MyUser user = (MyUser)SecurityContextHolder.getContext()
            .getAuthentication().getPrincipal();
        MyUserUpdater updater = new MyUserUpdater();

        // Set new roles/permissions here, as well as (perhaps) other
        // things.
        updater.update(user, userUpdates); // I believe this updates the
                        // SecurityContext's Authentication instance

        userCache.removeUserFromCache(user);    // Necessary to first remove?
        userCache.putUserInCache(user);
    }
}

Am I on track or way off base here?


Solution

  • Usually you don't update the roles after login. You assign specific role(s) at registration. For e.g.:

    • Not signed in (UNAUTHORIZED)
    • Signed in (ROLE_USER)
    • Some specific signed in user which u assign manually or based on some logic (ROLE_ADMIN) ...

    and once the user successfully logs in you can read the credentials from UserDetails interface.

    But it can be done. Your user should implement UserDetails interface

    public class MyUser implements UserDetails { ... }
    

    And then when you get the user from database based on credentials you can update the roles/authorities and return back the object.

    public class MyUserDetailsService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        MyUser mu = customUserRepository.findByUsername(username);
    
        List<Authority> authorityList = (List<Authority>) mu.getAuthorities();
        authorityList.add(new Authority("ROLE_ON_THE_FLY"));
    
        mu.setAuthorities(authorityList);
        return mu;
    }
     class Authority implements GrantedAuthority {
    
        private final String authority;
    
        public Authority(String authority) {
            this.authority = authority;
        }
    
        @Override
        public String getAuthority() {
            return authority;
        }
    }
    }
    

    Hope it helps.